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.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +20 -5
- claude_mpm/agents/agent_loader.py +19 -2
- claude_mpm/agents/base_agent_loader.py +5 -5
- claude_mpm/agents/frontmatter_validator.py +4 -4
- claude_mpm/agents/templates/agent-manager.json +3 -3
- claude_mpm/agents/templates/agentic-coder-optimizer.json +3 -3
- claude_mpm/agents/templates/api_qa.json +1 -1
- claude_mpm/agents/templates/clerk-ops.json +3 -3
- claude_mpm/agents/templates/code_analyzer.json +3 -3
- claude_mpm/agents/templates/dart_engineer.json +294 -0
- claude_mpm/agents/templates/data_engineer.json +3 -3
- claude_mpm/agents/templates/documentation.json +2 -2
- claude_mpm/agents/templates/engineer.json +2 -2
- claude_mpm/agents/templates/gcp_ops_agent.json +2 -2
- claude_mpm/agents/templates/imagemagick.json +1 -1
- claude_mpm/agents/templates/local_ops_agent.json +319 -41
- claude_mpm/agents/templates/memory_manager.json +2 -2
- claude_mpm/agents/templates/nextjs_engineer.json +2 -2
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/php-engineer.json +1 -1
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/prompt-engineer.json +6 -4
- claude_mpm/agents/templates/python_engineer.json +2 -2
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/react_engineer.json +3 -3
- claude_mpm/agents/templates/refactoring_engineer.json +3 -3
- claude_mpm/agents/templates/research.json +2 -2
- claude_mpm/agents/templates/security.json +2 -2
- claude_mpm/agents/templates/ticketing.json +2 -2
- claude_mpm/agents/templates/typescript_engineer.json +2 -2
- claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
- claude_mpm/agents/templates/version_control.json +2 -2
- claude_mpm/agents/templates/web_qa.json +6 -6
- claude_mpm/agents/templates/web_ui.json +3 -3
- claude_mpm/cli/__init__.py +49 -19
- claude_mpm/cli/commands/agent_manager.py +3 -3
- claude_mpm/cli/commands/agents.py +6 -6
- claude_mpm/cli/commands/aggregate.py +4 -4
- claude_mpm/cli/commands/analyze.py +2 -2
- claude_mpm/cli/commands/analyze_code.py +1 -1
- claude_mpm/cli/commands/cleanup.py +3 -3
- claude_mpm/cli/commands/config.py +2 -2
- claude_mpm/cli/commands/configure.py +605 -21
- claude_mpm/cli/commands/dashboard.py +1 -1
- claude_mpm/cli/commands/debug.py +3 -3
- claude_mpm/cli/commands/doctor.py +1 -1
- claude_mpm/cli/commands/mcp.py +7 -7
- claude_mpm/cli/commands/mcp_command_router.py +1 -1
- claude_mpm/cli/commands/mcp_config.py +2 -2
- claude_mpm/cli/commands/mcp_external_commands.py +2 -2
- claude_mpm/cli/commands/mcp_install_commands.py +3 -3
- claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
- claude_mpm/cli/commands/mcp_setup_external.py +3 -3
- claude_mpm/cli/commands/monitor.py +1 -1
- claude_mpm/cli/commands/mpm_init_handler.py +1 -1
- claude_mpm/cli/interactive/agent_wizard.py +1 -1
- claude_mpm/cli/parsers/configure_parser.py +5 -0
- claude_mpm/cli/parsers/search_parser.py +1 -1
- claude_mpm/cli/shared/argument_patterns.py +2 -2
- claude_mpm/cli/shared/base_command.py +1 -1
- claude_mpm/cli/startup_logging.py +4 -4
- claude_mpm/config/experimental_features.py +4 -4
- claude_mpm/config/socketio_config.py +2 -2
- claude_mpm/core/__init__.py +53 -17
- claude_mpm/core/agent_session_manager.py +2 -2
- claude_mpm/core/api_validator.py +3 -3
- claude_mpm/core/base_service.py +10 -1
- claude_mpm/core/cache.py +2 -2
- claude_mpm/core/config.py +5 -5
- claude_mpm/core/config_aliases.py +4 -4
- claude_mpm/core/config_constants.py +1 -1
- claude_mpm/core/error_handler.py +1 -1
- claude_mpm/core/file_utils.py +5 -5
- claude_mpm/core/framework/formatters/capability_generator.py +5 -5
- claude_mpm/core/framework/loaders/agent_loader.py +1 -1
- claude_mpm/core/framework/processors/metadata_processor.py +1 -1
- claude_mpm/core/framework/processors/template_processor.py +3 -3
- claude_mpm/core/framework_loader.py +2 -2
- claude_mpm/core/log_manager.py +11 -4
- claude_mpm/core/logger.py +2 -2
- claude_mpm/core/optimized_startup.py +1 -1
- claude_mpm/core/output_style_manager.py +1 -1
- claude_mpm/core/service_registry.py +2 -2
- claude_mpm/core/session_manager.py +3 -3
- claude_mpm/core/shared/config_loader.py +1 -1
- claude_mpm/core/socketio_pool.py +2 -2
- claude_mpm/core/unified_agent_registry.py +2 -2
- claude_mpm/core/unified_config.py +6 -6
- claude_mpm/core/unified_paths.py +2 -2
- claude_mpm/dashboard/api/simple_directory.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +1 -1
- claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
- claude_mpm/hooks/claude_hooks/installer.py +9 -9
- claude_mpm/hooks/claude_hooks/response_tracking.py +16 -11
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +16 -13
- claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/hooks/validation_hooks.py +1 -1
- claude_mpm/init.py +4 -4
- claude_mpm/models/agent_session.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +5 -5
- claude_mpm/services/__init__.py +145 -161
- claude_mpm/services/agent_capabilities_service.py +1 -1
- claude_mpm/services/agents/agent_builder.py +4 -4
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
- claude_mpm/services/agents/deployment/deployment_config_loader.py +21 -0
- claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
- claude_mpm/services/agents/loading/base_agent_manager.py +12 -2
- claude_mpm/services/agents/local_template_manager.py +5 -5
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
- claude_mpm/services/agents/registry/modification_tracker.py +19 -11
- claude_mpm/services/async_session_logger.py +3 -3
- claude_mpm/services/claude_session_logger.py +4 -4
- claude_mpm/services/cli/agent_listing_service.py +3 -3
- claude_mpm/services/cli/agent_validation_service.py +1 -1
- claude_mpm/services/cli/session_manager.py +2 -2
- claude_mpm/services/core/path_resolver.py +1 -1
- claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
- claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
- claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
- claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
- claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
- claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
- claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
- claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
- claude_mpm/services/event_aggregator.py +1 -1
- claude_mpm/services/event_bus/event_bus.py +7 -2
- claude_mpm/services/events/consumers/dead_letter.py +2 -2
- claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
- claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
- claude_mpm/services/hook_installer_service.py +7 -7
- claude_mpm/services/infrastructure/context_preservation.py +7 -7
- claude_mpm/services/infrastructure/daemon_manager.py +5 -5
- claude_mpm/services/mcp_config_manager.py +169 -48
- claude_mpm/services/mcp_gateway/__init__.py +98 -94
- claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
- claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
- claude_mpm/services/mcp_gateway/config/configuration.py +3 -3
- claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
- claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
- claude_mpm/services/mcp_gateway/main.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
- claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
- claude_mpm/services/mcp_service_verifier.py +1 -1
- claude_mpm/services/memory/builder.py +1 -1
- claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
- claude_mpm/services/memory/indexed_memory.py +3 -3
- claude_mpm/services/monitor/daemon.py +1 -1
- claude_mpm/services/monitor/daemon_manager.py +9 -9
- claude_mpm/services/monitor/event_emitter.py +1 -1
- claude_mpm/services/monitor/handlers/file.py +1 -1
- claude_mpm/services/monitor/handlers/hooks.py +3 -3
- claude_mpm/services/monitor/management/lifecycle.py +7 -7
- claude_mpm/services/monitor/server.py +2 -2
- claude_mpm/services/orphan_detection.py +788 -0
- claude_mpm/services/port_manager.py +2 -2
- claude_mpm/services/project/analyzer.py +3 -3
- claude_mpm/services/project/archive_manager.py +13 -13
- claude_mpm/services/project/dependency_analyzer.py +4 -4
- claude_mpm/services/project/documentation_manager.py +4 -4
- claude_mpm/services/project/enhanced_analyzer.py +8 -8
- claude_mpm/services/project/registry.py +4 -4
- claude_mpm/services/project_port_allocator.py +597 -0
- claude_mpm/services/response_tracker.py +1 -1
- claude_mpm/services/session_management_service.py +1 -1
- claude_mpm/services/session_manager.py +6 -4
- claude_mpm/services/socketio/event_normalizer.py +1 -1
- claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
- claude_mpm/services/socketio/handlers/file.py +1 -1
- claude_mpm/services/socketio/migration_utils.py +1 -1
- claude_mpm/services/socketio/server/core.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
- claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
- claude_mpm/services/unified/config_strategies/context_strategy.py +6 -6
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
- claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
- claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
- claude_mpm/services/unified/deployment_strategies/base.py +4 -4
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
- claude_mpm/services/unified/deployment_strategies/local.py +9 -9
- claude_mpm/services/unified/deployment_strategies/utils.py +9 -9
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -7
- claude_mpm/services/unified/unified_config.py +5 -5
- claude_mpm/services/unified/unified_deployment.py +2 -2
- claude_mpm/services/utility_service.py +1 -1
- claude_mpm/services/version_control/conflict_resolution.py +2 -2
- claude_mpm/services/version_control/git_operations.py +3 -3
- claude_mpm/services/version_control/semantic_versioning.py +13 -13
- claude_mpm/services/version_control/version_parser.py +1 -1
- claude_mpm/storage/state_storage.py +12 -13
- claude_mpm/tools/code_tree_analyzer.py +5 -5
- claude_mpm/tools/code_tree_builder.py +4 -4
- claude_mpm/tools/socketio_debug.py +1 -1
- claude_mpm/utils/agent_dependency_loader.py +4 -4
- claude_mpm/utils/common.py +2 -2
- claude_mpm/utils/config_manager.py +3 -3
- claude_mpm/utils/dependency_cache.py +2 -2
- claude_mpm/utils/dependency_strategies.py +6 -6
- claude_mpm/utils/file_utils.py +11 -11
- claude_mpm/utils/log_cleanup.py +1 -1
- claude_mpm/utils/path_operations.py +1 -1
- claude_mpm/validation/agent_validator.py +2 -2
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/METADATA +1 -1
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/RECORD +226 -223
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/WHEEL +0 -0
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/top_level.txt +0 -0
@@ -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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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)
|
@@ -190,7 +190,7 @@ class AsyncSessionLogger:
|
|
190
190
|
self._initialized = True
|
191
191
|
|
192
192
|
# Log initialization status
|
193
|
-
logger.
|
193
|
+
logger.debug(
|
194
194
|
f"AsyncSessionLogger initialized with SessionManager: session_id={self.session_id}, async={self.enable_async}, format={self.log_format.value}"
|
195
195
|
)
|
196
196
|
|
@@ -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(
|
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}")
|
@@ -537,7 +537,7 @@ class AsyncSessionLogger:
|
|
537
537
|
# Also update SessionManager to keep consistency
|
538
538
|
session_manager = get_session_manager()
|
539
539
|
session_manager.set_session_id(session_id)
|
540
|
-
logger.
|
540
|
+
logger.debug(f"Session ID updated to: {session_id}")
|
541
541
|
|
542
542
|
def is_enabled(self) -> bool:
|
543
543
|
"""Check if logging is enabled."""
|
@@ -88,7 +88,7 @@ class ClaudeSessionLogger:
|
|
88
88
|
# Use centralized SessionManager for session ID
|
89
89
|
session_manager = get_session_manager()
|
90
90
|
self.session_id = session_manager.get_session_id()
|
91
|
-
logger.
|
91
|
+
logger.debug(
|
92
92
|
f"ClaudeSessionLogger using session ID from SessionManager: {self.session_id}"
|
93
93
|
)
|
94
94
|
|
@@ -125,11 +125,11 @@ class ClaudeSessionLogger:
|
|
125
125
|
# Synchronize session IDs - use the one we already generated
|
126
126
|
if self.session_id and hasattr(self._async_logger, "set_session_id"):
|
127
127
|
self._async_logger.set_session_id(self.session_id)
|
128
|
-
logger.
|
128
|
+
logger.debug(
|
129
129
|
f"Using async logger with session ID: {self.session_id}"
|
130
130
|
)
|
131
131
|
else:
|
132
|
-
logger.
|
132
|
+
logger.debug("Using async logger for improved performance")
|
133
133
|
except Exception as e:
|
134
134
|
logger.warning(
|
135
135
|
f"Failed to initialize async logger, falling back to sync: {e}"
|
@@ -224,7 +224,7 @@ class ClaudeSessionLogger:
|
|
224
224
|
|
225
225
|
# Save response
|
226
226
|
try:
|
227
|
-
with open(
|
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(
|
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(
|
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(
|
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]:
|
169
|
+
def detect_framework_path(self) -> Optional[Path]:
|
170
170
|
"""
|
171
171
|
Auto-detect claude-mpm framework using unified path management.
|
172
172
|
|
@@ -187,7 +187,7 @@ class ClaudeCodeCheck(BaseDiagnosticCheck):
|
|
187
187
|
|
188
188
|
# Check if it's up to date
|
189
189
|
try:
|
190
|
-
with open(
|
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(
|
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(
|
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(
|
286
|
+
with user_config.open() as f:
|
287
287
|
user_data = yaml.safe_load(f) or {}
|
288
288
|
|
289
|
-
with open(
|
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(
|
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(
|
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:
|
141
|
+
def _check_installation_method(self) -> DiagnosticResult:
|
142
142
|
"""Detect how claude-mpm was installed."""
|
143
143
|
methods_found = []
|
144
144
|
details = {}
|
@@ -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(
|
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(
|
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(
|
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(
|
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(
|
905
|
+
with backup_path.open() as bf:
|
906
906
|
backup_config = json.load(bf)
|
907
|
-
with open(
|
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(
|
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(
|
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")
|
@@ -65,6 +65,9 @@ class EventBus:
|
|
65
65
|
self._event_history: List[Dict[str, Any]] = []
|
66
66
|
self._max_history_size = 100
|
67
67
|
|
68
|
+
# Track async handler tasks to prevent garbage collection
|
69
|
+
self._handler_tasks: Set[asyncio.Task] = set()
|
70
|
+
|
68
71
|
logger.info("EventBus initialized")
|
69
72
|
|
70
73
|
@classmethod
|
@@ -190,13 +193,15 @@ class EventBus:
|
|
190
193
|
try:
|
191
194
|
# Call with event_type and data for wildcard handlers
|
192
195
|
if asyncio.iscoroutinefunction(handler):
|
193
|
-
# Schedule async handlers
|
196
|
+
# Schedule async handlers with tracking
|
194
197
|
try:
|
195
198
|
loop = asyncio.get_event_loop()
|
196
199
|
if loop.is_running():
|
197
|
-
asyncio.create_task(
|
200
|
+
task = asyncio.create_task(
|
198
201
|
handler(event_type, data)
|
199
202
|
)
|
203
|
+
self._handler_tasks.add(task)
|
204
|
+
task.add_done_callback(self._handler_tasks.discard)
|
200
205
|
else:
|
201
206
|
loop.run_until_complete(
|
202
207
|
handler(event_type, data)
|
@@ -109,7 +109,7 @@ class DeadLetterConsumer(IEventConsumer):
|
|
109
109
|
self._rotate_file()
|
110
110
|
|
111
111
|
# Write to file
|
112
|
-
with
|
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(
|
168
|
+
with file_path.open() as f:
|
169
169
|
for line in f:
|
170
170
|
event_data = json.loads(line)
|
171
171
|
|
@@ -95,7 +95,7 @@ class DeploymentManager:
|
|
95
95
|
|
96
96
|
# Check if file exists and compare versions
|
97
97
|
if target_file.exists() and not force:
|
98
|
-
with open(
|
98
|
+
with target_file.open() as f:
|
99
99
|
existing_content = f.read()
|
100
100
|
existing_fw_ver = self.version_manager.parse_current_version(
|
101
101
|
existing_content
|
@@ -110,7 +110,7 @@ class DeploymentManager:
|
|
110
110
|
parent_path.mkdir(parents=True, exist_ok=True)
|
111
111
|
|
112
112
|
# Write content
|
113
|
-
with open(
|
113
|
+
with target_file.open("w") as f:
|
114
114
|
f.write(content)
|
115
115
|
|
116
116
|
# Get version info for success message
|
@@ -137,7 +137,7 @@ class DeploymentManager:
|
|
137
137
|
return True, f"{self.target_filename} does not exist"
|
138
138
|
|
139
139
|
try:
|
140
|
-
with open(
|
140
|
+
with target_file.open() as f:
|
141
141
|
existing_content = f.read()
|
142
142
|
existing_fw_ver = self.version_manager.parse_current_version(
|
143
143
|
existing_content
|
@@ -38,7 +38,7 @@ class VersionManager:
|
|
38
38
|
version_path = package_path.parent / "framework" / "VERSION"
|
39
39
|
|
40
40
|
if version_path.exists():
|
41
|
-
with open(
|
41
|
+
with version_path.open() as f:
|
42
42
|
version_content = f.read().strip()
|
43
43
|
# Framework VERSION file contains just the framework version number
|
44
44
|
try:
|
@@ -24,7 +24,7 @@ class HookInstallerService:
|
|
24
24
|
self.claude_dir = Path.home() / ".claude"
|
25
25
|
self.settings_file = self.claude_dir / "settings.json"
|
26
26
|
|
27
|
-
def is_hooks_configured(self) -> bool:
|
27
|
+
def is_hooks_configured(self) -> bool:
|
28
28
|
"""Check if hooks are configured in Claude settings.
|
29
29
|
|
30
30
|
Returns:
|
@@ -35,7 +35,7 @@ class HookInstallerService:
|
|
35
35
|
self.logger.debug("Claude settings file does not exist")
|
36
36
|
return False
|
37
37
|
|
38
|
-
with
|
38
|
+
with self.settings_file.open() as f:
|
39
39
|
settings = json.load(f)
|
40
40
|
|
41
41
|
# Check if hooks section exists
|
@@ -292,7 +292,7 @@ class HookInstallerService:
|
|
292
292
|
|
293
293
|
# Load existing settings or create new
|
294
294
|
if self.settings_file.exists():
|
295
|
-
with
|
295
|
+
with self.settings_file.open() as f:
|
296
296
|
settings = json.load(f)
|
297
297
|
self.logger.debug("Found existing Claude settings")
|
298
298
|
else:
|
@@ -320,7 +320,7 @@ class HookInstallerService:
|
|
320
320
|
settings["hooks"][event_type] = [hook_config]
|
321
321
|
|
322
322
|
# Write settings
|
323
|
-
with
|
323
|
+
with self.settings_file.open("w") as f:
|
324
324
|
json.dump(settings, f, indent=2)
|
325
325
|
|
326
326
|
self.logger.info(f"Updated Claude settings at: {self.settings_file}")
|
@@ -380,7 +380,7 @@ class HookInstallerService:
|
|
380
380
|
|
381
381
|
self.logger.info("Removing Claude Code hooks...")
|
382
382
|
|
383
|
-
with
|
383
|
+
with self.settings_file.open() as f:
|
384
384
|
settings = json.load(f)
|
385
385
|
|
386
386
|
hooks_removed = 0
|
@@ -422,7 +422,7 @@ class HookInstallerService:
|
|
422
422
|
del settings["hooks"]
|
423
423
|
|
424
424
|
# Write updated settings
|
425
|
-
with
|
425
|
+
with self.settings_file.open("w") as f:
|
426
426
|
json.dump(settings, f, indent=2)
|
427
427
|
|
428
428
|
if hooks_removed > 0:
|
@@ -469,7 +469,7 @@ class HookInstallerService:
|
|
469
469
|
|
470
470
|
try:
|
471
471
|
if self.settings_file.exists():
|
472
|
-
with
|
472
|
+
with self.settings_file.open() as f:
|
473
473
|
settings = json.load(f)
|
474
474
|
|
475
475
|
if "hooks" in settings:
|
@@ -151,7 +151,7 @@ class ContextPreservationService(BaseService):
|
|
151
151
|
return None
|
152
152
|
|
153
153
|
# Use streaming to find active conversation
|
154
|
-
with
|
154
|
+
with self.claude_json_path.open("rb") as f:
|
155
155
|
parser = ijson.parse(f)
|
156
156
|
|
157
157
|
active_conv_id = None
|
@@ -207,7 +207,7 @@ class ContextPreservationService(BaseService):
|
|
207
207
|
keep_recent_days * 86400
|
208
208
|
)
|
209
209
|
|
210
|
-
with
|
210
|
+
with self.claude_json_path.open() as f:
|
211
211
|
data = json.load(f)
|
212
212
|
|
213
213
|
original_count = len(data.get("conversations", []))
|
@@ -239,7 +239,7 @@ class ContextPreservationService(BaseService):
|
|
239
239
|
|
240
240
|
# Write compressed version
|
241
241
|
temp_path = self.claude_json_path.with_suffix(".tmp")
|
242
|
-
with open(
|
242
|
+
with temp_path.open("w") as f:
|
243
243
|
json.dump(data, f, separators=(",", ":")) # Compact format
|
244
244
|
|
245
245
|
# Replace original
|
@@ -316,7 +316,7 @@ class ContextPreservationService(BaseService):
|
|
316
316
|
# Use streaming to extract preferences
|
317
317
|
preferences = {}
|
318
318
|
|
319
|
-
with
|
319
|
+
with self.claude_json_path.open("rb") as f:
|
320
320
|
parser = ijson.parse(f)
|
321
321
|
|
322
322
|
for prefix, event, value in parser:
|
@@ -339,7 +339,7 @@ class ContextPreservationService(BaseService):
|
|
339
339
|
) -> ConversationState:
|
340
340
|
"""Parse Claude JSON using standard JSON parser."""
|
341
341
|
try:
|
342
|
-
with
|
342
|
+
with self.claude_json_path.open() as f:
|
343
343
|
data = json.load(f)
|
344
344
|
|
345
345
|
return await self._extract_conversation_state(data, extract_full)
|
@@ -357,7 +357,7 @@ class ContextPreservationService(BaseService):
|
|
357
357
|
preferences = {}
|
358
358
|
open_files = []
|
359
359
|
|
360
|
-
with
|
360
|
+
with self.claude_json_path.open("rb") as f:
|
361
361
|
parser = ijson.parse(f)
|
362
362
|
|
363
363
|
conversation_count = 0
|
@@ -527,7 +527,7 @@ class ContextPreservationService(BaseService):
|
|
527
527
|
backup_name += ".gz"
|
528
528
|
backup_path = self.claude_backup_dir / backup_name
|
529
529
|
|
530
|
-
with
|
530
|
+
with self.claude_json_path.open("rb") as f_in:
|
531
531
|
with gzip.open(backup_path, "wb") as f_out:
|
532
532
|
shutil.copyfileobj(f_in, f_out)
|
533
533
|
else:
|