claude-mpm 4.1.0__py3-none-any.whl → 4.1.2__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/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/__main__.py +1 -1
- claude_mpm/agents/BASE_PM.md +74 -46
- claude_mpm/agents/INSTRUCTIONS.md +11 -153
- claude_mpm/agents/WORKFLOW.md +61 -321
- claude_mpm/agents/__init__.py +11 -11
- claude_mpm/agents/agent_loader.py +23 -20
- claude_mpm/agents/agent_loader_integration.py +1 -1
- claude_mpm/agents/agents_metadata.py +27 -0
- claude_mpm/agents/async_agent_loader.py +5 -8
- claude_mpm/agents/base_agent_loader.py +36 -25
- claude_mpm/agents/frontmatter_validator.py +6 -6
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +9 -9
- claude_mpm/agents/templates/api_qa.json +47 -2
- claude_mpm/agents/templates/imagemagick.json +256 -0
- claude_mpm/agents/templates/qa.json +41 -2
- claude_mpm/agents/templates/ticketing.json +5 -5
- claude_mpm/agents/templates/web_qa.json +133 -58
- claude_mpm/agents/templates/web_ui.json +3 -3
- claude_mpm/cli/__init__.py +51 -46
- claude_mpm/cli/__main__.py +1 -1
- claude_mpm/cli/commands/__init__.py +10 -12
- claude_mpm/cli/commands/agent_manager.py +186 -181
- claude_mpm/cli/commands/agents.py +271 -268
- claude_mpm/cli/commands/aggregate.py +30 -29
- claude_mpm/cli/commands/cleanup.py +50 -44
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
- claude_mpm/cli/commands/config.py +162 -127
- claude_mpm/cli/commands/doctor.py +52 -62
- claude_mpm/cli/commands/info.py +37 -25
- claude_mpm/cli/commands/mcp.py +3 -7
- claude_mpm/cli/commands/mcp_command_router.py +14 -18
- claude_mpm/cli/commands/mcp_install_commands.py +28 -23
- claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
- claude_mpm/cli/commands/mcp_server_commands.py +23 -17
- claude_mpm/cli/commands/memory.py +192 -141
- claude_mpm/cli/commands/monitor.py +117 -88
- claude_mpm/cli/commands/run.py +120 -84
- claude_mpm/cli/commands/run_config_checker.py +4 -5
- claude_mpm/cli/commands/socketio_monitor.py +17 -19
- claude_mpm/cli/commands/tickets.py +92 -92
- claude_mpm/cli/parser.py +1 -5
- claude_mpm/cli/parsers/__init__.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
- claude_mpm/cli/parsers/agents_parser.py +2 -3
- claude_mpm/cli/parsers/base_parser.py +7 -5
- claude_mpm/cli/parsers/mcp_parser.py +4 -2
- claude_mpm/cli/parsers/monitor_parser.py +26 -18
- claude_mpm/cli/shared/__init__.py +10 -10
- claude_mpm/cli/shared/argument_patterns.py +57 -71
- claude_mpm/cli/shared/base_command.py +61 -53
- claude_mpm/cli/shared/error_handling.py +62 -58
- claude_mpm/cli/shared/output_formatters.py +78 -77
- claude_mpm/cli/startup_logging.py +204 -172
- claude_mpm/cli/utils.py +10 -11
- claude_mpm/cli_module/__init__.py +1 -1
- claude_mpm/cli_module/args.py +1 -1
- claude_mpm/cli_module/migration_example.py +5 -5
- claude_mpm/config/__init__.py +9 -9
- claude_mpm/config/agent_config.py +15 -14
- claude_mpm/config/experimental_features.py +4 -4
- claude_mpm/config/paths.py +0 -1
- claude_mpm/config/socketio_config.py +5 -6
- claude_mpm/constants.py +1 -2
- claude_mpm/core/__init__.py +8 -8
- claude_mpm/core/agent_name_normalizer.py +1 -1
- claude_mpm/core/agent_registry.py +20 -23
- claude_mpm/core/agent_session_manager.py +3 -3
- claude_mpm/core/base_service.py +7 -15
- claude_mpm/core/cache.py +4 -6
- claude_mpm/core/claude_runner.py +85 -113
- claude_mpm/core/config.py +43 -28
- claude_mpm/core/config_aliases.py +0 -9
- claude_mpm/core/config_constants.py +52 -30
- claude_mpm/core/constants.py +0 -1
- claude_mpm/core/container.py +18 -27
- claude_mpm/core/exceptions.py +2 -2
- claude_mpm/core/factories.py +10 -12
- claude_mpm/core/framework_loader.py +581 -280
- claude_mpm/core/hook_manager.py +26 -22
- claude_mpm/core/hook_performance_config.py +58 -47
- claude_mpm/core/injectable_service.py +1 -1
- claude_mpm/core/interactive_session.py +61 -152
- claude_mpm/core/interfaces.py +1 -100
- claude_mpm/core/lazy.py +5 -5
- claude_mpm/core/log_manager.py +587 -0
- claude_mpm/core/logger.py +125 -8
- claude_mpm/core/logging_config.py +15 -15
- claude_mpm/core/minimal_framework_loader.py +5 -8
- claude_mpm/core/oneshot_session.py +15 -33
- claude_mpm/core/optimized_agent_loader.py +4 -6
- claude_mpm/core/optimized_startup.py +2 -1
- claude_mpm/core/output_style_manager.py +147 -106
- claude_mpm/core/pm_hook_interceptor.py +0 -1
- claude_mpm/core/service_registry.py +11 -8
- claude_mpm/core/session_manager.py +1 -2
- claude_mpm/core/shared/__init__.py +1 -1
- claude_mpm/core/shared/config_loader.py +101 -97
- claude_mpm/core/shared/path_resolver.py +72 -68
- claude_mpm/core/shared/singleton_manager.py +56 -50
- claude_mpm/core/socketio_pool.py +26 -6
- claude_mpm/core/tool_access_control.py +4 -5
- claude_mpm/core/typing_utils.py +50 -59
- claude_mpm/core/unified_agent_registry.py +14 -19
- claude_mpm/core/unified_config.py +4 -6
- claude_mpm/core/unified_paths.py +197 -109
- claude_mpm/dashboard/open_dashboard.py +2 -4
- claude_mpm/experimental/cli_enhancements.py +51 -36
- claude_mpm/generators/agent_profile_generator.py +2 -4
- claude_mpm/hooks/base_hook.py +1 -2
- claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
- claude_mpm/hooks/claude_hooks/event_handlers.py +93 -38
- claude_mpm/hooks/claude_hooks/hook_handler.py +130 -76
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
- claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
- claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
- claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
- claude_mpm/hooks/memory_integration_hook.py +5 -5
- claude_mpm/hooks/tool_call_interceptor.py +1 -1
- claude_mpm/hooks/validation_hooks.py +4 -4
- claude_mpm/init.py +4 -9
- claude_mpm/models/__init__.py +2 -2
- claude_mpm/models/agent_session.py +11 -14
- claude_mpm/scripts/mcp_server.py +20 -11
- claude_mpm/scripts/mcp_wrapper.py +5 -5
- claude_mpm/scripts/mpm_doctor.py +321 -0
- claude_mpm/scripts/socketio_daemon.py +28 -25
- claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
- claude_mpm/scripts/socketio_server_manager.py +116 -95
- claude_mpm/services/__init__.py +49 -49
- claude_mpm/services/agent_capabilities_service.py +12 -18
- claude_mpm/services/agents/__init__.py +22 -22
- claude_mpm/services/agents/agent_builder.py +140 -119
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
- claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
- claude_mpm/services/agents/deployment/agent_deployment.py +136 -106
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
- claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
- claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
- claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
- claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
- claude_mpm/services/agents/deployment/agent_validator.py +0 -1
- claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
- claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
- claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
- claude_mpm/services/agents/deployment/config/__init__.py +1 -1
- claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
- claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
- claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
- claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
- claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
- claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
- claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
- claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
- claude_mpm/services/agents/deployment/results/__init__.py +1 -1
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
- claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
- claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
- claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
- claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
- claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
- claude_mpm/services/agents/loading/__init__.py +1 -1
- claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
- claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
- claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
- claude_mpm/services/agents/management/__init__.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
- claude_mpm/services/agents/management/agent_management_service.py +5 -9
- claude_mpm/services/agents/memory/__init__.py +4 -4
- claude_mpm/services/agents/memory/agent_memory_manager.py +280 -160
- claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
- claude_mpm/services/agents/memory/content_manager.py +44 -38
- claude_mpm/services/agents/memory/template_generator.py +4 -6
- claude_mpm/services/agents/registry/__init__.py +10 -6
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
- claude_mpm/services/agents/registry/modification_tracker.py +3 -6
- claude_mpm/services/async_session_logger.py +1 -2
- claude_mpm/services/claude_session_logger.py +1 -2
- claude_mpm/services/command_deployment_service.py +173 -0
- claude_mpm/services/command_handler_service.py +20 -22
- claude_mpm/services/core/__init__.py +25 -25
- claude_mpm/services/core/base.py +0 -5
- claude_mpm/services/core/interfaces/__init__.py +32 -32
- claude_mpm/services/core/interfaces/agent.py +0 -21
- claude_mpm/services/core/interfaces/communication.py +0 -27
- claude_mpm/services/core/interfaces/infrastructure.py +0 -56
- claude_mpm/services/core/interfaces/service.py +0 -29
- claude_mpm/services/diagnostics/__init__.py +1 -1
- claude_mpm/services/diagnostics/checks/__init__.py +6 -6
- claude_mpm/services/diagnostics/checks/agent_check.py +89 -80
- claude_mpm/services/diagnostics/checks/base_check.py +12 -16
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
- claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
- claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
- claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
- claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
- claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
- claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
- claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
- claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
- claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
- claude_mpm/services/diagnostics/models.py +21 -19
- claude_mpm/services/event_aggregator.py +10 -17
- claude_mpm/services/event_bus/__init__.py +1 -1
- claude_mpm/services/event_bus/config.py +54 -35
- claude_mpm/services/event_bus/event_bus.py +76 -71
- claude_mpm/services/event_bus/relay.py +74 -64
- claude_mpm/services/events/__init__.py +11 -11
- claude_mpm/services/events/consumers/__init__.py +3 -3
- claude_mpm/services/events/consumers/dead_letter.py +71 -63
- claude_mpm/services/events/consumers/logging.py +39 -37
- claude_mpm/services/events/consumers/metrics.py +56 -57
- claude_mpm/services/events/consumers/socketio.py +82 -81
- claude_mpm/services/events/core.py +110 -99
- claude_mpm/services/events/interfaces.py +56 -72
- claude_mpm/services/events/producers/__init__.py +1 -1
- claude_mpm/services/events/producers/hook.py +38 -38
- claude_mpm/services/events/producers/system.py +46 -44
- claude_mpm/services/exceptions.py +81 -80
- claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
- claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
- claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
- claude_mpm/services/hook_service.py +6 -9
- claude_mpm/services/infrastructure/__init__.py +1 -1
- claude_mpm/services/infrastructure/context_preservation.py +8 -12
- claude_mpm/services/infrastructure/monitoring.py +21 -23
- claude_mpm/services/mcp_gateway/__init__.py +37 -37
- claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
- claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
- claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
- claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
- claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
- claude_mpm/services/mcp_gateway/core/base.py +0 -3
- claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
- claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
- claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
- claude_mpm/services/mcp_gateway/main.py +2 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
- claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
- claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
- claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
- claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
- claude_mpm/services/memory/__init__.py +3 -3
- claude_mpm/services/memory/builder.py +3 -6
- claude_mpm/services/memory/cache/__init__.py +1 -1
- claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/memory/indexed_memory.py +5 -7
- claude_mpm/services/memory/optimizer.py +7 -10
- claude_mpm/services/memory/router.py +8 -9
- claude_mpm/services/memory_hook_service.py +48 -34
- claude_mpm/services/monitor_build_service.py +77 -73
- claude_mpm/services/port_manager.py +130 -108
- claude_mpm/services/project/analyzer.py +12 -10
- claude_mpm/services/project/registry.py +11 -11
- claude_mpm/services/recovery_manager.py +10 -19
- claude_mpm/services/response_tracker.py +0 -1
- claude_mpm/services/runner_configuration_service.py +19 -20
- claude_mpm/services/session_management_service.py +7 -11
- claude_mpm/services/shared/__init__.py +1 -1
- claude_mpm/services/shared/async_service_base.py +58 -50
- claude_mpm/services/shared/config_service_base.py +73 -67
- claude_mpm/services/shared/lifecycle_service_base.py +82 -78
- claude_mpm/services/shared/manager_base.py +94 -82
- claude_mpm/services/shared/service_factory.py +96 -98
- claude_mpm/services/socketio/__init__.py +3 -3
- claude_mpm/services/socketio/client_proxy.py +5 -5
- claude_mpm/services/socketio/event_normalizer.py +199 -181
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +5 -4
- claude_mpm/services/socketio/handlers/connection.py +163 -136
- claude_mpm/services/socketio/handlers/file.py +13 -14
- claude_mpm/services/socketio/handlers/git.py +12 -7
- claude_mpm/services/socketio/handlers/hook.py +49 -44
- claude_mpm/services/socketio/handlers/memory.py +0 -1
- claude_mpm/services/socketio/handlers/project.py +0 -1
- claude_mpm/services/socketio/handlers/registry.py +37 -19
- claude_mpm/services/socketio/migration_utils.py +98 -84
- claude_mpm/services/socketio/server/__init__.py +1 -1
- claude_mpm/services/socketio/server/broadcaster.py +81 -87
- claude_mpm/services/socketio/server/core.py +65 -54
- claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
- claude_mpm/services/socketio/server/main.py +64 -38
- claude_mpm/services/socketio_client_manager.py +10 -12
- claude_mpm/services/subprocess_launcher_service.py +4 -7
- claude_mpm/services/system_instructions_service.py +13 -14
- claude_mpm/services/ticket_manager.py +2 -2
- claude_mpm/services/utility_service.py +5 -13
- claude_mpm/services/version_control/__init__.py +16 -16
- claude_mpm/services/version_control/branch_strategy.py +5 -8
- claude_mpm/services/version_control/conflict_resolution.py +9 -23
- claude_mpm/services/version_control/git_operations.py +5 -7
- claude_mpm/services/version_control/semantic_versioning.py +16 -17
- claude_mpm/services/version_control/version_parser.py +13 -18
- claude_mpm/services/version_service.py +10 -11
- claude_mpm/storage/__init__.py +1 -1
- claude_mpm/storage/state_storage.py +22 -28
- claude_mpm/utils/__init__.py +6 -6
- claude_mpm/utils/agent_dependency_loader.py +47 -33
- claude_mpm/utils/config_manager.py +11 -14
- claude_mpm/utils/dependency_cache.py +1 -1
- claude_mpm/utils/dependency_manager.py +13 -17
- claude_mpm/utils/dependency_strategies.py +8 -10
- claude_mpm/utils/environment_context.py +3 -9
- claude_mpm/utils/error_handler.py +3 -13
- claude_mpm/utils/file_utils.py +1 -1
- claude_mpm/utils/path_operations.py +8 -12
- claude_mpm/utils/robust_installer.py +110 -33
- claude_mpm/utils/subprocess_utils.py +5 -6
- claude_mpm/validation/agent_validator.py +3 -6
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/METADATA +1 -1
- claude_mpm-4.1.2.dist-info/RECORD +498 -0
- claude_mpm-4.1.0.dist-info/RECORD +0 -494
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/top_level.txt +0 -0
|
@@ -15,7 +15,7 @@ DESIGN DECISIONS:
|
|
|
15
15
|
import re
|
|
16
16
|
from datetime import datetime
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from typing import
|
|
18
|
+
from typing import Any, Dict, Optional
|
|
19
19
|
|
|
20
20
|
from ..models import DiagnosticResult, DiagnosticStatus
|
|
21
21
|
from .base_check import BaseDiagnosticCheck
|
|
@@ -23,51 +23,51 @@ from .base_check import BaseDiagnosticCheck
|
|
|
23
23
|
|
|
24
24
|
class StartupLogCheck(BaseDiagnosticCheck):
|
|
25
25
|
"""Analyze startup logs for errors and issues."""
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
# Common error patterns and their fixes
|
|
28
28
|
ERROR_PATTERNS = {
|
|
29
29
|
r"Agent deployment.*failed": (
|
|
30
30
|
"Agent deployment failure",
|
|
31
|
-
"Check agent configuration in .claude/agents/ and run 'claude-mpm deploy'"
|
|
31
|
+
"Check agent configuration in .claude/agents/ and run 'claude-mpm deploy'",
|
|
32
32
|
),
|
|
33
33
|
r"MCP.*not found": (
|
|
34
34
|
"MCP server not found",
|
|
35
|
-
"Install MCP server: npm install -g @modelcontextprotocol/server"
|
|
35
|
+
"Install MCP server: npm install -g @modelcontextprotocol/server",
|
|
36
36
|
),
|
|
37
37
|
r"Port \d+ .*in use": (
|
|
38
38
|
"Port binding conflict",
|
|
39
|
-
"Kill the process using the port or use --websocket-port to specify a different port"
|
|
39
|
+
"Kill the process using the port or use --websocket-port to specify a different port",
|
|
40
40
|
),
|
|
41
41
|
r"Memory loading.*error": (
|
|
42
42
|
"Memory loading failure",
|
|
43
|
-
"Check .claude-mpm/memory/ permissions and run 'claude-mpm memory validate'"
|
|
43
|
+
"Check .claude-mpm/memory/ permissions and run 'claude-mpm memory validate'",
|
|
44
44
|
),
|
|
45
45
|
r"Permission denied": (
|
|
46
46
|
"Permission error",
|
|
47
|
-
"Check file permissions in project directory and .claude-mpm/"
|
|
47
|
+
"Check file permissions in project directory and .claude-mpm/",
|
|
48
48
|
),
|
|
49
49
|
r"ModuleNotFoundError|ImportError": (
|
|
50
50
|
"Missing Python dependency",
|
|
51
|
-
"Install missing dependencies: pip install -e . or pip install claude-mpm[agents]"
|
|
51
|
+
"Install missing dependencies: pip install -e . or pip install claude-mpm[agents]",
|
|
52
52
|
),
|
|
53
53
|
r"Socket\.IO.*failed|socketio.*error": (
|
|
54
54
|
"Socket.IO initialization failure",
|
|
55
|
-
"Install monitor dependencies: pip install claude-mpm[monitor]"
|
|
55
|
+
"Install monitor dependencies: pip install claude-mpm[monitor]",
|
|
56
56
|
),
|
|
57
57
|
r"Configuration.*invalid|yaml.*error": (
|
|
58
58
|
"Configuration file error",
|
|
59
|
-
"Validate configuration: claude-mpm config validate"
|
|
59
|
+
"Validate configuration: claude-mpm config validate",
|
|
60
60
|
),
|
|
61
61
|
r"claude\.json.*large|memory.*issue": (
|
|
62
62
|
"Large .claude.json file",
|
|
63
|
-
"Run 'claude-mpm cleanup-memory' to archive old conversations"
|
|
63
|
+
"Run 'claude-mpm cleanup-memory' to archive old conversations",
|
|
64
64
|
),
|
|
65
65
|
r"Failed to start.*daemon": (
|
|
66
66
|
"Daemon startup failure",
|
|
67
|
-
"Check system resources and try: claude-mpm monitor stop && claude-mpm monitor start"
|
|
67
|
+
"Check system resources and try: claude-mpm monitor stop && claude-mpm monitor start",
|
|
68
68
|
),
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
# Warning patterns that should be noted
|
|
72
72
|
WARNING_PATTERNS = {
|
|
73
73
|
r"agent.*source.*tracking": "Agent source tracking warning",
|
|
@@ -76,21 +76,21 @@ class StartupLogCheck(BaseDiagnosticCheck):
|
|
|
76
76
|
r"Socket\.IO.*not available": "Monitor mode unavailable",
|
|
77
77
|
r"Response logging.*disabled": "Response logging is disabled",
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
@property
|
|
81
81
|
def name(self) -> str:
|
|
82
82
|
return "startup_log_check"
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
@property
|
|
85
85
|
def category(self) -> str:
|
|
86
86
|
return "Startup Log"
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
def run(self) -> DiagnosticResult:
|
|
89
89
|
"""Run startup log diagnostics."""
|
|
90
90
|
try:
|
|
91
91
|
# Find the latest startup log
|
|
92
92
|
log_file = self._find_latest_log()
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
if not log_file:
|
|
95
95
|
return DiagnosticResult(
|
|
96
96
|
category=self.category,
|
|
@@ -98,16 +98,16 @@ class StartupLogCheck(BaseDiagnosticCheck):
|
|
|
98
98
|
message="No startup logs found",
|
|
99
99
|
details={
|
|
100
100
|
"recommendation": "Startup logging will be created on next run"
|
|
101
|
-
}
|
|
101
|
+
},
|
|
102
102
|
)
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
# Parse the log file
|
|
105
105
|
analysis = self._analyze_log_file(log_file)
|
|
106
|
-
|
|
106
|
+
|
|
107
107
|
# Determine status based on findings
|
|
108
108
|
status = self._determine_status(analysis)
|
|
109
109
|
message = self._create_message(analysis)
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
# Build details
|
|
112
112
|
details = {
|
|
113
113
|
"log_file": str(log_file),
|
|
@@ -116,18 +116,18 @@ class StartupLogCheck(BaseDiagnosticCheck):
|
|
|
116
116
|
"warnings": analysis["warning_count"],
|
|
117
117
|
"info_messages": analysis["info_count"],
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
# Add specific issues found
|
|
121
121
|
if analysis["errors_found"]:
|
|
122
122
|
details["errors_found"] = analysis["errors_found"]
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
if analysis["warnings_found"]:
|
|
125
125
|
details["warnings_found"] = analysis["warnings_found"]
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
# Add recommendations
|
|
128
128
|
if analysis["recommendations"]:
|
|
129
129
|
details["recommendations"] = analysis["recommendations"]
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
# Create sub-results if verbose
|
|
132
132
|
sub_results = []
|
|
133
133
|
if self.verbose and analysis["errors_found"]:
|
|
@@ -137,42 +137,40 @@ class StartupLogCheck(BaseDiagnosticCheck):
|
|
|
137
137
|
category="Error",
|
|
138
138
|
status=DiagnosticStatus.ERROR,
|
|
139
139
|
message=error_type,
|
|
140
|
-
details={"fix": fix}
|
|
140
|
+
details={"fix": fix},
|
|
141
141
|
)
|
|
142
142
|
)
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
return DiagnosticResult(
|
|
145
145
|
category=self.category,
|
|
146
146
|
status=status,
|
|
147
147
|
message=message,
|
|
148
148
|
details=details,
|
|
149
149
|
sub_results=sub_results if self.verbose else [],
|
|
150
|
-
fix_command=self._get_fix_command(analysis)
|
|
150
|
+
fix_command=self._get_fix_command(analysis),
|
|
151
151
|
)
|
|
152
|
-
|
|
152
|
+
|
|
153
153
|
except Exception as e:
|
|
154
154
|
return DiagnosticResult(
|
|
155
155
|
category=self.category,
|
|
156
156
|
status=DiagnosticStatus.ERROR,
|
|
157
|
-
message=f"Startup log check failed: {
|
|
158
|
-
details={"error": str(e)}
|
|
157
|
+
message=f"Startup log check failed: {e!s}",
|
|
158
|
+
details={"error": str(e)},
|
|
159
159
|
)
|
|
160
|
-
|
|
160
|
+
|
|
161
161
|
def _find_latest_log(self) -> Optional[Path]:
|
|
162
162
|
"""Find the most recent startup log file."""
|
|
163
163
|
log_dir = Path.cwd() / ".claude-mpm" / "logs" / "startup"
|
|
164
|
-
|
|
164
|
+
|
|
165
165
|
if not log_dir.exists():
|
|
166
166
|
return None
|
|
167
|
-
|
|
167
|
+
|
|
168
168
|
log_files = sorted(
|
|
169
|
-
log_dir.glob("startup-*.log"),
|
|
170
|
-
key=lambda p: p.stat().st_mtime,
|
|
171
|
-
reverse=True
|
|
169
|
+
log_dir.glob("startup-*.log"), key=lambda p: p.stat().st_mtime, reverse=True
|
|
172
170
|
)
|
|
173
|
-
|
|
171
|
+
|
|
174
172
|
return log_files[0] if log_files else None
|
|
175
|
-
|
|
173
|
+
|
|
176
174
|
def _analyze_log_file(self, log_file: Path) -> Dict[str, Any]:
|
|
177
175
|
"""Analyze the contents of a startup log file."""
|
|
178
176
|
analysis = {
|
|
@@ -186,19 +184,19 @@ class StartupLogCheck(BaseDiagnosticCheck):
|
|
|
186
184
|
"recommendations": [],
|
|
187
185
|
"startup_successful": False,
|
|
188
186
|
}
|
|
189
|
-
|
|
187
|
+
|
|
190
188
|
try:
|
|
191
|
-
with open(log_file,
|
|
189
|
+
with open(log_file, encoding="utf-8") as f:
|
|
192
190
|
lines = f.readlines()
|
|
193
|
-
|
|
191
|
+
|
|
194
192
|
# Extract timestamp from filename or first line
|
|
195
|
-
match = re.search(r
|
|
193
|
+
match = re.search(r"(\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2})", log_file.name)
|
|
196
194
|
if match:
|
|
197
195
|
timestamp_str = match.group(1)
|
|
198
196
|
# Convert to readable format
|
|
199
197
|
dt = datetime.strptime(timestamp_str, "%Y-%m-%d-%H-%M-%S")
|
|
200
198
|
analysis["timestamp"] = dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
201
|
-
|
|
199
|
+
|
|
202
200
|
# Process each line
|
|
203
201
|
for line in lines:
|
|
204
202
|
# Count log levels
|
|
@@ -212,22 +210,22 @@ class StartupLogCheck(BaseDiagnosticCheck):
|
|
|
212
210
|
analysis["info_count"] += 1
|
|
213
211
|
elif " - DEBUG - " in line:
|
|
214
212
|
analysis["debug_count"] += 1
|
|
215
|
-
|
|
213
|
+
|
|
216
214
|
# Check for successful startup indicators
|
|
217
215
|
if "Claude session completed successfully" in line:
|
|
218
216
|
analysis["startup_successful"] = True
|
|
219
217
|
elif "Starting Claude MPM session" in line:
|
|
220
218
|
analysis["startup_successful"] = True # At least startup began
|
|
221
|
-
|
|
219
|
+
|
|
222
220
|
# Generate recommendations based on findings
|
|
223
221
|
self._generate_recommendations(analysis)
|
|
224
|
-
|
|
222
|
+
|
|
225
223
|
except Exception as e:
|
|
226
224
|
analysis["error_count"] += 1
|
|
227
225
|
analysis["errors_found"].append(("Failed to parse log", str(e)))
|
|
228
|
-
|
|
226
|
+
|
|
229
227
|
return analysis
|
|
230
|
-
|
|
228
|
+
|
|
231
229
|
def _check_error_patterns(self, line: str, analysis: Dict[str, Any]) -> None:
|
|
232
230
|
"""Check line for known error patterns."""
|
|
233
231
|
for pattern, (error_type, fix) in self.ERROR_PATTERNS.items():
|
|
@@ -236,7 +234,7 @@ class StartupLogCheck(BaseDiagnosticCheck):
|
|
|
236
234
|
if (error_type, fix) not in analysis["errors_found"]:
|
|
237
235
|
analysis["errors_found"].append((error_type, fix))
|
|
238
236
|
break
|
|
239
|
-
|
|
237
|
+
|
|
240
238
|
def _check_warning_patterns(self, line: str, analysis: Dict[str, Any]) -> None:
|
|
241
239
|
"""Check line for known warning patterns."""
|
|
242
240
|
for pattern, warning_type in self.WARNING_PATTERNS.items():
|
|
@@ -245,70 +243,66 @@ class StartupLogCheck(BaseDiagnosticCheck):
|
|
|
245
243
|
if warning_type not in analysis["warnings_found"]:
|
|
246
244
|
analysis["warnings_found"].append(warning_type)
|
|
247
245
|
break
|
|
248
|
-
|
|
246
|
+
|
|
249
247
|
def _generate_recommendations(self, analysis: Dict[str, Any]) -> None:
|
|
250
248
|
"""Generate recommendations based on analysis."""
|
|
251
249
|
recommendations = []
|
|
252
|
-
|
|
250
|
+
|
|
253
251
|
# High error count
|
|
254
252
|
if analysis["error_count"] > 5:
|
|
255
253
|
recommendations.append(
|
|
256
254
|
"High error count detected. Review full log for systematic issues."
|
|
257
255
|
)
|
|
258
|
-
|
|
256
|
+
|
|
259
257
|
# No successful startup
|
|
260
258
|
if not analysis["startup_successful"] and analysis["error_count"] > 0:
|
|
261
259
|
recommendations.append(
|
|
262
260
|
"Startup may have failed. Check error messages above."
|
|
263
261
|
)
|
|
264
|
-
|
|
262
|
+
|
|
265
263
|
# Specific error recommendations
|
|
266
264
|
if any("MCP" in error[0] for error in analysis["errors_found"]):
|
|
267
265
|
recommendations.append(
|
|
268
266
|
"MCP issues detected. Run 'claude-mpm doctor' for full diagnostics."
|
|
269
267
|
)
|
|
270
|
-
|
|
268
|
+
|
|
271
269
|
if any("Agent" in error[0] for error in analysis["errors_found"]):
|
|
272
270
|
recommendations.append(
|
|
273
271
|
"Agent issues detected. Run 'claude-mpm agents validate' to check agents."
|
|
274
272
|
)
|
|
275
|
-
|
|
273
|
+
|
|
276
274
|
if any("memory" in error[0].lower() for error in analysis["errors_found"]):
|
|
277
275
|
recommendations.append(
|
|
278
276
|
"Memory issues detected. Run 'claude-mpm cleanup-memory' to free space."
|
|
279
277
|
)
|
|
280
|
-
|
|
278
|
+
|
|
281
279
|
analysis["recommendations"] = recommendations
|
|
282
|
-
|
|
280
|
+
|
|
283
281
|
def _determine_status(self, analysis: Dict[str, Any]) -> DiagnosticStatus:
|
|
284
282
|
"""Determine overall status based on analysis."""
|
|
285
283
|
if analysis["error_count"] > 0:
|
|
286
284
|
return DiagnosticStatus.ERROR
|
|
287
|
-
|
|
285
|
+
if analysis["warning_count"] > 3 or analysis["warning_count"] > 0:
|
|
288
286
|
return DiagnosticStatus.WARNING
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
else:
|
|
292
|
-
return DiagnosticStatus.OK
|
|
293
|
-
|
|
287
|
+
return DiagnosticStatus.OK
|
|
288
|
+
|
|
294
289
|
def _create_message(self, analysis: Dict[str, Any]) -> str:
|
|
295
290
|
"""Create summary message based on analysis."""
|
|
296
291
|
if analysis["error_count"] > 0:
|
|
297
292
|
return f"Startup has {analysis['error_count']} error(s)"
|
|
298
|
-
|
|
293
|
+
if analysis["warning_count"] > 0:
|
|
299
294
|
return f"Startup successful with {analysis['warning_count']} warning(s)"
|
|
300
|
-
|
|
295
|
+
if analysis["startup_successful"]:
|
|
301
296
|
return "Last startup was successful"
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
297
|
+
return "Startup log is clean"
|
|
298
|
+
|
|
305
299
|
def _get_fix_command(self, analysis: Dict[str, Any]) -> Optional[str]:
|
|
306
300
|
"""Get the most relevant fix command based on errors found."""
|
|
307
301
|
if not analysis["errors_found"]:
|
|
308
302
|
return None
|
|
309
|
-
|
|
303
|
+
|
|
310
304
|
# Priority order for fix commands
|
|
311
|
-
for
|
|
305
|
+
for _error_type, fix in analysis["errors_found"]:
|
|
312
306
|
if "claude-mpm" in fix:
|
|
313
307
|
# Extract claude-mpm command from fix suggestion
|
|
314
308
|
match = re.search(r'(claude-mpm [^\'"\n]+)', fix)
|
|
@@ -318,5 +312,5 @@ class StartupLogCheck(BaseDiagnosticCheck):
|
|
|
318
312
|
match = re.search(r'(pip install [^\'"\n]+)', fix)
|
|
319
313
|
if match:
|
|
320
314
|
return match.group(1)
|
|
321
|
-
|
|
322
|
-
return None
|
|
315
|
+
|
|
316
|
+
return None
|
|
@@ -8,7 +8,7 @@ and aggregate results for reporting.
|
|
|
8
8
|
import asyncio
|
|
9
9
|
import logging
|
|
10
10
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
11
|
-
from typing import List,
|
|
11
|
+
from typing import List, Type
|
|
12
12
|
|
|
13
13
|
from .checks import (
|
|
14
14
|
AgentCheck,
|
|
@@ -27,14 +27,14 @@ from .models import DiagnosticResult, DiagnosticStatus, DiagnosticSummary
|
|
|
27
27
|
|
|
28
28
|
class DiagnosticRunner:
|
|
29
29
|
"""Orchestrate diagnostic checks and aggregate results.
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
WHY: Provides a single entry point for running all diagnostics with
|
|
32
32
|
proper error handling, parallel execution, and result aggregation.
|
|
33
33
|
"""
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
def __init__(self, verbose: bool = False, fix: bool = False):
|
|
36
36
|
"""Initialize diagnostic runner.
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
Args:
|
|
39
39
|
verbose: Include detailed information in results
|
|
40
40
|
fix: Attempt to fix issues automatically (future feature)
|
|
@@ -42,7 +42,7 @@ class DiagnosticRunner:
|
|
|
42
42
|
self.verbose = verbose
|
|
43
43
|
self.fix = fix
|
|
44
44
|
self.logger = logging.getLogger(__name__)
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
# Define check order (dependencies first)
|
|
47
47
|
self.check_classes: List[Type[BaseDiagnosticCheck]] = [
|
|
48
48
|
InstallationCheck,
|
|
@@ -55,56 +55,56 @@ class DiagnosticRunner:
|
|
|
55
55
|
StartupLogCheck, # Check startup logs for recent issues
|
|
56
56
|
CommonIssuesCheck,
|
|
57
57
|
]
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
def run_diagnostics(self) -> DiagnosticSummary:
|
|
60
60
|
"""Run all diagnostic checks synchronously.
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
Returns:
|
|
63
63
|
DiagnosticSummary with all results
|
|
64
64
|
"""
|
|
65
65
|
summary = DiagnosticSummary()
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
# Run checks in order
|
|
68
68
|
for check_class in self.check_classes:
|
|
69
69
|
try:
|
|
70
70
|
check = check_class(verbose=self.verbose)
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
# Skip if check shouldn't run
|
|
73
73
|
if not check.should_run():
|
|
74
74
|
self.logger.debug(f"Skipping {check.name}")
|
|
75
75
|
continue
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
self.logger.debug(f"Running {check.name}")
|
|
78
78
|
result = check.run()
|
|
79
79
|
summary.add_result(result)
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
# If fix mode is enabled and there's a fix available
|
|
82
82
|
if self.fix and result.has_issues and result.fix_command:
|
|
83
83
|
self._attempt_fix(result)
|
|
84
|
-
|
|
84
|
+
|
|
85
85
|
except Exception as e:
|
|
86
86
|
self.logger.error(f"Check {check_class.__name__} failed: {e}")
|
|
87
87
|
error_result = DiagnosticResult(
|
|
88
88
|
category=check_class.__name__.replace("Check", ""),
|
|
89
89
|
status=DiagnosticStatus.ERROR,
|
|
90
|
-
message=f"Check failed: {
|
|
91
|
-
details={"error": str(e)}
|
|
90
|
+
message=f"Check failed: {e!s}",
|
|
91
|
+
details={"error": str(e)},
|
|
92
92
|
)
|
|
93
93
|
summary.add_result(error_result)
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
return summary
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
def run_diagnostics_parallel(self) -> DiagnosticSummary:
|
|
98
98
|
"""Run diagnostic checks in parallel for faster execution.
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
WHY: Some checks may involve I/O or network operations, running them
|
|
101
101
|
in parallel can significantly speed up the overall diagnostic process.
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
Returns:
|
|
104
104
|
DiagnosticSummary with all results
|
|
105
105
|
"""
|
|
106
106
|
summary = DiagnosticSummary()
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
# Group checks by dependency level
|
|
109
109
|
# Level 1: No dependencies
|
|
110
110
|
level1 = [InstallationCheck, FilesystemCheck, ConfigurationCheck]
|
|
@@ -112,28 +112,30 @@ class DiagnosticRunner:
|
|
|
112
112
|
level2 = [ClaudeDesktopCheck, AgentCheck, MCPCheck, MonitorCheck]
|
|
113
113
|
# Level 3: Depends on others
|
|
114
114
|
level3 = [CommonIssuesCheck]
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
for level in [level1, level2, level3]:
|
|
117
117
|
level_results = self._run_level_parallel(level)
|
|
118
118
|
for result in level_results:
|
|
119
119
|
summary.add_result(result)
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
return summary
|
|
122
|
-
|
|
123
|
-
def _run_level_parallel(
|
|
122
|
+
|
|
123
|
+
def _run_level_parallel(
|
|
124
|
+
self, check_classes: List[Type[BaseDiagnosticCheck]]
|
|
125
|
+
) -> List[DiagnosticResult]:
|
|
124
126
|
"""Run a group of checks in parallel.
|
|
125
|
-
|
|
127
|
+
|
|
126
128
|
Args:
|
|
127
129
|
check_classes: List of check classes to run
|
|
128
|
-
|
|
130
|
+
|
|
129
131
|
Returns:
|
|
130
132
|
List of DiagnosticResults
|
|
131
133
|
"""
|
|
132
134
|
results = []
|
|
133
|
-
|
|
135
|
+
|
|
134
136
|
with ThreadPoolExecutor(max_workers=len(check_classes)) as executor:
|
|
135
137
|
future_to_check = {}
|
|
136
|
-
|
|
138
|
+
|
|
137
139
|
for check_class in check_classes:
|
|
138
140
|
try:
|
|
139
141
|
check = check_class(verbose=self.verbose)
|
|
@@ -141,14 +143,18 @@ class DiagnosticRunner:
|
|
|
141
143
|
future = executor.submit(check.run)
|
|
142
144
|
future_to_check[future] = check_class.__name__
|
|
143
145
|
except Exception as e:
|
|
144
|
-
self.logger.error(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
self.logger.error(
|
|
147
|
+
f"Failed to create check {check_class.__name__}: {e}"
|
|
148
|
+
)
|
|
149
|
+
results.append(
|
|
150
|
+
DiagnosticResult(
|
|
151
|
+
category=check_class.__name__.replace("Check", ""),
|
|
152
|
+
status=DiagnosticStatus.ERROR,
|
|
153
|
+
message=f"Check initialization failed: {e!s}",
|
|
154
|
+
details={"error": str(e)},
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
|
|
152
158
|
for future in as_completed(future_to_check):
|
|
153
159
|
check_name = future_to_check[future]
|
|
154
160
|
try:
|
|
@@ -156,26 +162,28 @@ class DiagnosticRunner:
|
|
|
156
162
|
results.append(result)
|
|
157
163
|
except Exception as e:
|
|
158
164
|
self.logger.error(f"Check {check_name} failed: {e}")
|
|
159
|
-
results.append(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
results.append(
|
|
166
|
+
DiagnosticResult(
|
|
167
|
+
category=check_name.replace("Check", ""),
|
|
168
|
+
status=DiagnosticStatus.ERROR,
|
|
169
|
+
message=f"Check execution failed: {e!s}",
|
|
170
|
+
details={"error": str(e)},
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
|
|
166
174
|
return results
|
|
167
|
-
|
|
175
|
+
|
|
168
176
|
def run_specific_checks(self, check_names: List[str]) -> DiagnosticSummary:
|
|
169
177
|
"""Run only specific diagnostic checks.
|
|
170
|
-
|
|
178
|
+
|
|
171
179
|
Args:
|
|
172
180
|
check_names: List of check names to run (e.g., ["installation", "agents"])
|
|
173
|
-
|
|
181
|
+
|
|
174
182
|
Returns:
|
|
175
183
|
DiagnosticSummary with results from specified checks
|
|
176
184
|
"""
|
|
177
185
|
summary = DiagnosticSummary()
|
|
178
|
-
|
|
186
|
+
|
|
179
187
|
# Map check names to classes
|
|
180
188
|
check_map = {
|
|
181
189
|
"installation": InstallationCheck,
|
|
@@ -193,13 +201,13 @@ class DiagnosticRunner:
|
|
|
193
201
|
"common": CommonIssuesCheck,
|
|
194
202
|
"issues": CommonIssuesCheck,
|
|
195
203
|
}
|
|
196
|
-
|
|
204
|
+
|
|
197
205
|
for name in check_names:
|
|
198
206
|
check_class = check_map.get(name.lower())
|
|
199
207
|
if not check_class:
|
|
200
208
|
self.logger.warning(f"Unknown check: {name}")
|
|
201
209
|
continue
|
|
202
|
-
|
|
210
|
+
|
|
203
211
|
try:
|
|
204
212
|
check = check_class(verbose=self.verbose)
|
|
205
213
|
if check.should_run():
|
|
@@ -210,38 +218,38 @@ class DiagnosticRunner:
|
|
|
210
218
|
error_result = DiagnosticResult(
|
|
211
219
|
category=check_class.__name__.replace("Check", ""),
|
|
212
220
|
status=DiagnosticStatus.ERROR,
|
|
213
|
-
message=f"Check failed: {
|
|
214
|
-
details={"error": str(e)}
|
|
221
|
+
message=f"Check failed: {e!s}",
|
|
222
|
+
details={"error": str(e)},
|
|
215
223
|
)
|
|
216
224
|
summary.add_result(error_result)
|
|
217
|
-
|
|
225
|
+
|
|
218
226
|
return summary
|
|
219
|
-
|
|
227
|
+
|
|
220
228
|
def _attempt_fix(self, result: DiagnosticResult):
|
|
221
229
|
"""Attempt to fix an issue automatically.
|
|
222
|
-
|
|
230
|
+
|
|
223
231
|
Args:
|
|
224
232
|
result: DiagnosticResult with fix_command
|
|
225
233
|
"""
|
|
226
234
|
if not result.fix_command:
|
|
227
235
|
return
|
|
228
|
-
|
|
236
|
+
|
|
229
237
|
self.logger.info(f"Attempting to fix: {result.message}")
|
|
230
238
|
self.logger.info(f"Running: {result.fix_command}")
|
|
231
|
-
|
|
239
|
+
|
|
232
240
|
# In a real implementation, this would execute the fix command
|
|
233
241
|
# For now, we just log it
|
|
234
242
|
# TODO: Implement actual fix execution with proper safeguards
|
|
235
|
-
|
|
243
|
+
|
|
236
244
|
async def run_diagnostics_async(self) -> DiagnosticSummary:
|
|
237
245
|
"""Run diagnostics asynchronously (future enhancement).
|
|
238
|
-
|
|
246
|
+
|
|
239
247
|
WHY: For integration with async frameworks and better performance
|
|
240
248
|
with I/O-bound checks.
|
|
241
|
-
|
|
249
|
+
|
|
242
250
|
Returns:
|
|
243
251
|
DiagnosticSummary with all results
|
|
244
252
|
"""
|
|
245
253
|
# Convert sync execution to async for now
|
|
246
254
|
loop = asyncio.get_event_loop()
|
|
247
|
-
return await loop.run_in_executor(None, self.run_diagnostics)
|
|
255
|
+
return await loop.run_in_executor(None, self.run_diagnostics)
|