claude-mpm 4.1.1__py3-none-any.whl → 4.1.3__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/engineer.json +33 -11
- 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 +50 -2
- 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 +648 -1098
- 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 +339 -967
- claude_mpm/cli/commands/monitor.py +117 -88
- claude_mpm/cli/commands/run.py +233 -542
- 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 +280 -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 +22 -29
- 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 +500 -680
- 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 -17
- 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 +99 -154
- claude_mpm/hooks/claude_hooks/hook_handler.py +110 -720
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
- 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/services/__init__.py +13 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- 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 +129 -511
- 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/base_agent_locator.py +132 -0
- 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_results_manager.py +185 -0
- 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/single_agent_deployer.py +315 -0
- 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 +157 -503
- 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/memory_categorization_service.py +165 -0
- claude_mpm/services/agents/memory/memory_file_service.py +103 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
- claude_mpm/services/agents/memory/template_generator.py +4 -6
- claude_mpm/services/agents/registry/__init__.py +11 -7
- 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/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +407 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +589 -0
- claude_mpm/services/cli/dashboard_launcher.py +424 -0
- claude_mpm/services/cli/memory_crud_service.py +617 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/session_manager.py +513 -0
- claude_mpm/services/cli/socketio_manager.py +498 -0
- claude_mpm/services/cli/startup_checker.py +370 -0
- 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/cache_manager.py +311 -0
- 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/core/memory_manager.py +637 -0
- claude_mpm/services/core/path_resolver.py +498 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- 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 +152 -97
- 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.1.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
- claude_mpm-4.1.3.dist-info/RECORD +528 -0
- claude_mpm/cli/commands/run_config_checker.py +0 -160
- claude_mpm-4.1.1.dist-info/RECORD +0 -494
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
|
@@ -15,28 +15,76 @@ DESIGN DECISIONS:
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import logging
|
|
18
|
-
import os
|
|
19
18
|
import shutil
|
|
20
19
|
import subprocess
|
|
21
20
|
import sys
|
|
22
21
|
from datetime import datetime
|
|
23
22
|
from pathlib import Path
|
|
24
|
-
from typing import Any, Dict, Optional
|
|
23
|
+
from typing import Any, Dict, Optional
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
import psutil
|
|
27
|
+
|
|
28
|
+
PSUTIL_AVAILABLE = True
|
|
29
|
+
except ImportError:
|
|
30
|
+
PSUTIL_AVAILABLE = False
|
|
25
31
|
|
|
26
32
|
from ..core.logger import get_logger
|
|
27
33
|
|
|
28
34
|
|
|
35
|
+
def log_memory_stats(logger=None, prefix="Memory Usage"):
|
|
36
|
+
"""
|
|
37
|
+
Log current memory statistics.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
logger: Logger to use (defaults to 'cli' logger)
|
|
41
|
+
prefix: Prefix for the log message
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Dict with memory stats or None if psutil unavailable
|
|
45
|
+
"""
|
|
46
|
+
if not PSUTIL_AVAILABLE:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
if logger is None:
|
|
50
|
+
logger = get_logger("cli")
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
process = psutil.Process()
|
|
54
|
+
memory_info = process.memory_info()
|
|
55
|
+
|
|
56
|
+
# Convert to MB for readability
|
|
57
|
+
rss_mb = memory_info.rss / (1024 * 1024)
|
|
58
|
+
vms_mb = memory_info.vms / (1024 * 1024)
|
|
59
|
+
|
|
60
|
+
# Get percentage of system memory if available
|
|
61
|
+
try:
|
|
62
|
+
memory_percent = process.memory_percent()
|
|
63
|
+
logger.info(
|
|
64
|
+
f"{prefix}: RSS={rss_mb:.1f}MB, VMS={vms_mb:.1f}MB, "
|
|
65
|
+
f"System={memory_percent:.1f}%"
|
|
66
|
+
)
|
|
67
|
+
return {"rss_mb": rss_mb, "vms_mb": vms_mb, "percent": memory_percent}
|
|
68
|
+
except:
|
|
69
|
+
logger.info(f"{prefix}: RSS={rss_mb:.1f}MB, VMS={vms_mb:.1f}MB")
|
|
70
|
+
return {"rss_mb": rss_mb, "vms_mb": vms_mb, "percent": None}
|
|
71
|
+
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logger.debug(f"Failed to get memory info: {e}")
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
29
77
|
class StartupStatusLogger:
|
|
30
78
|
"""Logs MCP server and monitor setup status during startup."""
|
|
31
|
-
|
|
79
|
+
|
|
32
80
|
def __init__(self, logger_name: str = "startup_status"):
|
|
33
81
|
"""Initialize the startup status logger."""
|
|
34
82
|
self.logger = get_logger(logger_name)
|
|
35
|
-
|
|
83
|
+
|
|
36
84
|
def log_mcp_server_status(self) -> None:
|
|
37
85
|
"""
|
|
38
86
|
Log MCP server installation and configuration status.
|
|
39
|
-
|
|
87
|
+
|
|
40
88
|
Checks:
|
|
41
89
|
- MCP server executable availability
|
|
42
90
|
- MCP server version if available
|
|
@@ -48,7 +96,7 @@ class StartupStatusLogger:
|
|
|
48
96
|
mcp_executable = self._find_mcp_executable()
|
|
49
97
|
if mcp_executable:
|
|
50
98
|
self.logger.info(f"MCP Server: Installed at {mcp_executable}")
|
|
51
|
-
|
|
99
|
+
|
|
52
100
|
# Try to get version
|
|
53
101
|
version = self._get_mcp_version(mcp_executable)
|
|
54
102
|
if version:
|
|
@@ -57,20 +105,22 @@ class StartupStatusLogger:
|
|
|
57
105
|
self.logger.info("MCP Server: Version unknown")
|
|
58
106
|
else:
|
|
59
107
|
self.logger.info("MCP Server: Not found in PATH")
|
|
60
|
-
|
|
108
|
+
|
|
61
109
|
# Check MCP configuration in ~/.claude.json
|
|
62
110
|
config_status = self._check_mcp_configuration()
|
|
63
111
|
if config_status["found"]:
|
|
64
112
|
self.logger.info("MCP Server: Configuration found in ~/.claude.json")
|
|
65
113
|
if config_status["servers_count"] > 0:
|
|
66
|
-
self.logger.info(
|
|
114
|
+
self.logger.info(
|
|
115
|
+
f"MCP Server: {config_status['servers_count']} server(s) configured"
|
|
116
|
+
)
|
|
67
117
|
else:
|
|
68
118
|
self.logger.info("MCP Server: No servers configured")
|
|
69
119
|
self._log_mcp_setup_hint()
|
|
70
120
|
else:
|
|
71
121
|
self.logger.info("MCP Server: No configuration found in ~/.claude.json")
|
|
72
122
|
self._log_mcp_setup_hint()
|
|
73
|
-
|
|
123
|
+
|
|
74
124
|
# Check for claude-mpm MCP gateway status
|
|
75
125
|
gateway_status = self._check_mcp_gateway_status()
|
|
76
126
|
if gateway_status["configured"]:
|
|
@@ -78,20 +128,40 @@ class StartupStatusLogger:
|
|
|
78
128
|
else:
|
|
79
129
|
self.logger.info("MCP Gateway: Claude MPM gateway not configured")
|
|
80
130
|
# Check if this is a pipx installation that could benefit from auto-config
|
|
81
|
-
if
|
|
82
|
-
self.
|
|
83
|
-
|
|
131
|
+
if (
|
|
132
|
+
self._is_pipx_installation()
|
|
133
|
+
and not self._has_auto_config_preference()
|
|
134
|
+
):
|
|
135
|
+
self.logger.info(
|
|
136
|
+
"MCP Gateway: Auto-configuration available for pipx users"
|
|
137
|
+
)
|
|
138
|
+
|
|
84
139
|
except Exception as e:
|
|
85
140
|
self.logger.warning(f"MCP Server: Status check failed - {e}")
|
|
86
|
-
|
|
87
|
-
def
|
|
141
|
+
|
|
142
|
+
def log_memory_status(self) -> None:
|
|
143
|
+
"""
|
|
144
|
+
Log current process memory usage.
|
|
145
|
+
|
|
146
|
+
Logs both RSS (Resident Set Size) and VMS (Virtual Memory Size)
|
|
147
|
+
to help track memory consumption and potential leaks.
|
|
148
|
+
"""
|
|
149
|
+
stats = log_memory_stats(self.logger, "Memory Usage")
|
|
150
|
+
|
|
151
|
+
# Log warning if memory usage is high
|
|
152
|
+
if stats and stats.get("rss_mb", 0) > 500: # Warn if using more than 500MB
|
|
153
|
+
self.logger.warning(f"High memory usage detected: {stats['rss_mb']:.1f}MB")
|
|
154
|
+
|
|
155
|
+
def log_monitor_setup_status(
|
|
156
|
+
self, monitor_mode: bool = False, websocket_port: int = 8765
|
|
157
|
+
) -> None:
|
|
88
158
|
"""
|
|
89
159
|
Log monitor service initialization status.
|
|
90
|
-
|
|
160
|
+
|
|
91
161
|
Args:
|
|
92
162
|
monitor_mode: Whether monitor mode is enabled
|
|
93
163
|
websocket_port: WebSocket port for monitoring
|
|
94
|
-
|
|
164
|
+
|
|
95
165
|
Checks:
|
|
96
166
|
- Monitor service initialization status
|
|
97
167
|
- Which monitors are enabled/disabled
|
|
@@ -101,235 +171,246 @@ class StartupStatusLogger:
|
|
|
101
171
|
try:
|
|
102
172
|
if monitor_mode:
|
|
103
173
|
self.logger.info("Monitor: Mode enabled")
|
|
104
|
-
|
|
174
|
+
|
|
105
175
|
# Check SocketIO dependencies
|
|
106
176
|
socketio_status = self._check_socketio_dependencies()
|
|
107
177
|
if socketio_status["available"]:
|
|
108
178
|
self.logger.info("Monitor: Socket.IO dependencies available")
|
|
109
179
|
else:
|
|
110
|
-
self.logger.info(
|
|
111
|
-
|
|
180
|
+
self.logger.info(
|
|
181
|
+
f"Monitor: Socket.IO dependencies missing - {socketio_status['error']}"
|
|
182
|
+
)
|
|
183
|
+
|
|
112
184
|
# Check if server is running
|
|
113
185
|
server_running = self._check_socketio_server_running(websocket_port)
|
|
114
186
|
if server_running:
|
|
115
|
-
self.logger.info(
|
|
187
|
+
self.logger.info(
|
|
188
|
+
f"Monitor: Socket.IO server running on port {websocket_port}"
|
|
189
|
+
)
|
|
116
190
|
else:
|
|
117
|
-
self.logger.info(
|
|
118
|
-
|
|
191
|
+
self.logger.info(
|
|
192
|
+
f"Monitor: Socket.IO server will start on port {websocket_port}"
|
|
193
|
+
)
|
|
194
|
+
|
|
119
195
|
# Check response logging configuration
|
|
120
196
|
logging_config = self._check_response_logging_config()
|
|
121
197
|
if logging_config["enabled"]:
|
|
122
|
-
self.logger.info(
|
|
198
|
+
self.logger.info(
|
|
199
|
+
f"Monitor: Response logging enabled to {logging_config['directory']}"
|
|
200
|
+
)
|
|
123
201
|
else:
|
|
124
202
|
self.logger.info("Monitor: Response logging disabled")
|
|
125
|
-
|
|
203
|
+
|
|
126
204
|
else:
|
|
127
205
|
self.logger.info("Monitor: Mode disabled")
|
|
128
|
-
|
|
206
|
+
|
|
129
207
|
# Still check if there's an existing server running
|
|
130
208
|
server_running = self._check_socketio_server_running(websocket_port)
|
|
131
209
|
if server_running:
|
|
132
|
-
self.logger.info(
|
|
133
|
-
|
|
210
|
+
self.logger.info(
|
|
211
|
+
f"Monitor: Background Socket.IO server detected on port {websocket_port}"
|
|
212
|
+
)
|
|
213
|
+
|
|
134
214
|
except Exception as e:
|
|
135
215
|
self.logger.warning(f"Monitor: Status check failed - {e}")
|
|
136
|
-
|
|
216
|
+
|
|
137
217
|
def _find_mcp_executable(self) -> Optional[str]:
|
|
138
218
|
"""Find MCP server executable in PATH."""
|
|
139
219
|
# Common MCP executable names
|
|
140
220
|
executables = ["claude-mpm-mcp", "mcp", "claude-mcp"]
|
|
141
|
-
|
|
221
|
+
|
|
142
222
|
for exe_name in executables:
|
|
143
223
|
exe_path = shutil.which(exe_name)
|
|
144
224
|
if exe_path:
|
|
145
225
|
return exe_path
|
|
146
|
-
|
|
226
|
+
|
|
147
227
|
# Check if it's installed as a Python package
|
|
148
228
|
try:
|
|
149
229
|
result = subprocess.run(
|
|
150
230
|
[sys.executable, "-m", "claude_mpm.scripts.mcp_server", "--version"],
|
|
151
231
|
capture_output=True,
|
|
152
232
|
text=True,
|
|
153
|
-
timeout=5
|
|
233
|
+
timeout=5,
|
|
234
|
+
check=False,
|
|
154
235
|
)
|
|
155
236
|
if result.returncode == 0:
|
|
156
237
|
return f"{sys.executable} -m claude_mpm.scripts.mcp_server"
|
|
157
238
|
except Exception:
|
|
158
239
|
pass
|
|
159
|
-
|
|
240
|
+
|
|
160
241
|
return None
|
|
161
|
-
|
|
242
|
+
|
|
162
243
|
def _get_mcp_version(self, executable: str) -> Optional[str]:
|
|
163
244
|
"""Get MCP server version."""
|
|
164
245
|
try:
|
|
165
246
|
# Try --version flag
|
|
166
247
|
result = subprocess.run(
|
|
167
|
-
executable.split()
|
|
248
|
+
[*executable.split(), "--version"],
|
|
168
249
|
capture_output=True,
|
|
169
250
|
text=True,
|
|
170
|
-
timeout=5
|
|
251
|
+
timeout=5,
|
|
252
|
+
check=False,
|
|
171
253
|
)
|
|
172
254
|
if result.returncode == 0:
|
|
173
255
|
# Extract version from output
|
|
174
256
|
output = result.stdout.strip()
|
|
175
257
|
if output:
|
|
176
258
|
return output
|
|
177
|
-
|
|
259
|
+
|
|
178
260
|
# Try version command
|
|
179
261
|
result = subprocess.run(
|
|
180
|
-
executable.split()
|
|
262
|
+
[*executable.split(), "version"],
|
|
181
263
|
capture_output=True,
|
|
182
264
|
text=True,
|
|
183
|
-
timeout=5
|
|
265
|
+
timeout=5,
|
|
266
|
+
check=False,
|
|
184
267
|
)
|
|
185
268
|
if result.returncode == 0:
|
|
186
269
|
output = result.stdout.strip()
|
|
187
270
|
if output:
|
|
188
271
|
return output
|
|
189
|
-
|
|
272
|
+
|
|
190
273
|
except Exception:
|
|
191
274
|
pass
|
|
192
|
-
|
|
275
|
+
|
|
193
276
|
return None
|
|
194
|
-
|
|
277
|
+
|
|
195
278
|
def _check_mcp_configuration(self) -> Dict[str, Any]:
|
|
196
279
|
"""Check MCP configuration in ~/.claude.json."""
|
|
197
280
|
claude_json_path = Path.home() / ".claude.json"
|
|
198
|
-
|
|
199
|
-
result = {
|
|
200
|
-
|
|
201
|
-
"servers_count": 0,
|
|
202
|
-
"error": None
|
|
203
|
-
}
|
|
204
|
-
|
|
281
|
+
|
|
282
|
+
result = {"found": False, "servers_count": 0, "error": None}
|
|
283
|
+
|
|
205
284
|
try:
|
|
206
285
|
if not claude_json_path.exists():
|
|
207
286
|
return result
|
|
208
|
-
|
|
287
|
+
|
|
209
288
|
import json
|
|
210
|
-
|
|
289
|
+
|
|
290
|
+
with open(claude_json_path) as f:
|
|
211
291
|
config = json.load(f)
|
|
212
|
-
|
|
292
|
+
|
|
213
293
|
result["found"] = True
|
|
214
|
-
|
|
294
|
+
|
|
215
295
|
# Check for MCP servers configuration
|
|
216
296
|
mcp_config = config.get("mcpServers", {})
|
|
217
297
|
result["servers_count"] = len(mcp_config)
|
|
218
|
-
|
|
298
|
+
|
|
219
299
|
except Exception as e:
|
|
220
300
|
result["error"] = str(e)
|
|
221
|
-
|
|
301
|
+
|
|
222
302
|
return result
|
|
223
|
-
|
|
303
|
+
|
|
224
304
|
def _check_mcp_gateway_status(self) -> Dict[str, Any]:
|
|
225
305
|
"""Check Claude MPM MCP gateway configuration status."""
|
|
226
|
-
result = {
|
|
227
|
-
|
|
228
|
-
"error": None
|
|
229
|
-
}
|
|
230
|
-
|
|
306
|
+
result = {"configured": False, "error": None}
|
|
307
|
+
|
|
231
308
|
try:
|
|
232
309
|
# Check if MCP gateway startup verification is available
|
|
233
|
-
from ..services.mcp_gateway.core.startup_verification import
|
|
310
|
+
from ..services.mcp_gateway.core.startup_verification import (
|
|
311
|
+
is_mcp_gateway_configured,
|
|
312
|
+
)
|
|
313
|
+
|
|
234
314
|
result["configured"] = is_mcp_gateway_configured()
|
|
235
315
|
except ImportError:
|
|
236
316
|
# MCP gateway not available
|
|
237
317
|
pass
|
|
238
318
|
except Exception as e:
|
|
239
319
|
result["error"] = str(e)
|
|
240
|
-
|
|
320
|
+
|
|
241
321
|
return result
|
|
242
|
-
|
|
322
|
+
|
|
243
323
|
def _check_socketio_dependencies(self) -> Dict[str, Any]:
|
|
244
324
|
"""Check if Socket.IO dependencies are available."""
|
|
245
|
-
result = {
|
|
246
|
-
|
|
247
|
-
"error": None
|
|
248
|
-
}
|
|
249
|
-
|
|
325
|
+
result = {"available": False, "error": None}
|
|
326
|
+
|
|
250
327
|
try:
|
|
251
|
-
import socketio
|
|
252
328
|
import aiohttp
|
|
253
329
|
import engineio
|
|
330
|
+
import socketio
|
|
331
|
+
|
|
254
332
|
result["available"] = True
|
|
255
333
|
except ImportError as e:
|
|
256
334
|
result["error"] = f"Missing dependencies: {e}"
|
|
257
335
|
except Exception as e:
|
|
258
336
|
result["error"] = str(e)
|
|
259
|
-
|
|
337
|
+
|
|
260
338
|
return result
|
|
261
|
-
|
|
339
|
+
|
|
262
340
|
def _check_socketio_server_running(self, port: int) -> bool:
|
|
263
341
|
"""Check if Socket.IO server is running on specified port."""
|
|
264
342
|
try:
|
|
265
343
|
import socket
|
|
344
|
+
|
|
266
345
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
267
346
|
s.settimeout(1)
|
|
268
|
-
result = s.connect_ex((
|
|
347
|
+
result = s.connect_ex(("localhost", port))
|
|
269
348
|
return result == 0
|
|
270
349
|
except Exception:
|
|
271
350
|
return False
|
|
272
|
-
|
|
351
|
+
|
|
273
352
|
def _check_response_logging_config(self) -> Dict[str, Any]:
|
|
274
353
|
"""Check response logging configuration."""
|
|
275
|
-
result = {
|
|
276
|
-
|
|
277
|
-
"directory": None,
|
|
278
|
-
"error": None
|
|
279
|
-
}
|
|
280
|
-
|
|
354
|
+
result = {"enabled": False, "directory": None, "error": None}
|
|
355
|
+
|
|
281
356
|
try:
|
|
282
357
|
from ..core.shared.config_loader import ConfigLoader
|
|
283
|
-
|
|
358
|
+
|
|
284
359
|
config_loader = ConfigLoader()
|
|
285
360
|
config = config_loader.load_main_config()
|
|
286
|
-
|
|
361
|
+
|
|
287
362
|
# Check response logging configuration
|
|
288
363
|
response_logging = config.get("response_logging", {})
|
|
289
364
|
result["enabled"] = response_logging.get("enabled", False)
|
|
290
|
-
|
|
365
|
+
|
|
291
366
|
if result["enabled"]:
|
|
292
|
-
log_dir = response_logging.get(
|
|
367
|
+
log_dir = response_logging.get(
|
|
368
|
+
"session_directory", ".claude-mpm/responses"
|
|
369
|
+
)
|
|
293
370
|
if not Path(log_dir).is_absolute():
|
|
294
371
|
log_dir = Path.cwd() / log_dir
|
|
295
372
|
result["directory"] = str(log_dir)
|
|
296
|
-
|
|
373
|
+
|
|
297
374
|
except Exception as e:
|
|
298
375
|
result["error"] = str(e)
|
|
299
|
-
|
|
376
|
+
|
|
300
377
|
return result
|
|
301
|
-
|
|
378
|
+
|
|
302
379
|
def _is_pipx_installation(self) -> bool:
|
|
303
380
|
"""Check if this is a pipx installation."""
|
|
304
381
|
try:
|
|
305
382
|
# Check if running from pipx
|
|
306
383
|
if "pipx" in sys.executable.lower():
|
|
307
384
|
return True
|
|
308
|
-
|
|
385
|
+
|
|
309
386
|
# Check module path
|
|
310
387
|
import claude_mpm
|
|
388
|
+
|
|
311
389
|
module_path = Path(claude_mpm.__file__).parent
|
|
312
390
|
if "pipx" in str(module_path):
|
|
313
391
|
return True
|
|
314
392
|
except Exception:
|
|
315
393
|
pass
|
|
316
|
-
|
|
394
|
+
|
|
317
395
|
return False
|
|
318
|
-
|
|
396
|
+
|
|
319
397
|
def _has_auto_config_preference(self) -> bool:
|
|
320
398
|
"""Check if user has already been asked about auto-configuration."""
|
|
321
399
|
try:
|
|
322
400
|
from ..config.paths import paths
|
|
323
|
-
|
|
401
|
+
|
|
402
|
+
preference_file = (
|
|
403
|
+
paths.claude_mpm_dir_hidden / "mcp_auto_config_preference.json"
|
|
404
|
+
)
|
|
324
405
|
return preference_file.exists()
|
|
325
406
|
except Exception:
|
|
326
407
|
return False
|
|
327
|
-
|
|
408
|
+
|
|
328
409
|
def _log_mcp_setup_hint(self) -> None:
|
|
329
410
|
"""Log helpful hints for MCP setup."""
|
|
330
411
|
# Check if installed via pipx
|
|
331
412
|
is_pipx = self._check_pipx_installation()
|
|
332
|
-
|
|
413
|
+
|
|
333
414
|
if is_pipx:
|
|
334
415
|
self.logger.info("💡 TIP: It looks like you installed claude-mpm via pipx")
|
|
335
416
|
self.logger.info(" To configure MCP for Claude Code with pipx:")
|
|
@@ -341,198 +422,225 @@ class StartupStatusLogger:
|
|
|
341
422
|
self.logger.info(" 1. See docs/MCP_SETUP.md for setup instructions")
|
|
342
423
|
self.logger.info(" 2. Run: claude-mpm doctor --check mcp to verify")
|
|
343
424
|
self.logger.info(" 3. Restart Claude Code after configuration")
|
|
344
|
-
|
|
425
|
+
|
|
345
426
|
def _check_pipx_installation(self) -> bool:
|
|
346
427
|
"""Check if claude-mpm was installed via pipx."""
|
|
347
428
|
try:
|
|
348
429
|
# Check if running from a pipx venv
|
|
349
430
|
if "pipx" in sys.executable.lower():
|
|
350
431
|
return True
|
|
351
|
-
|
|
432
|
+
|
|
352
433
|
# Check if claude-mpm-mcp command exists and is from pipx
|
|
353
434
|
mcp_cmd = shutil.which("claude-mpm-mcp")
|
|
354
435
|
if mcp_cmd and "pipx" in mcp_cmd.lower():
|
|
355
436
|
return True
|
|
356
|
-
|
|
437
|
+
|
|
357
438
|
# Try to check pipx list
|
|
358
439
|
result = subprocess.run(
|
|
359
|
-
["pipx", "list"],
|
|
360
|
-
capture_output=True,
|
|
361
|
-
text=True,
|
|
362
|
-
timeout=2
|
|
440
|
+
["pipx", "list"], capture_output=True, text=True, timeout=2, check=False
|
|
363
441
|
)
|
|
364
442
|
if result.returncode == 0 and "claude-mpm" in result.stdout:
|
|
365
443
|
return True
|
|
366
|
-
|
|
444
|
+
|
|
367
445
|
except Exception:
|
|
368
446
|
pass
|
|
369
|
-
|
|
447
|
+
|
|
370
448
|
return False
|
|
371
449
|
|
|
372
450
|
|
|
373
451
|
def setup_startup_logging(project_root: Optional[Path] = None) -> Path:
|
|
374
452
|
"""
|
|
375
453
|
Set up logging to both console and file for startup.
|
|
376
|
-
|
|
454
|
+
|
|
377
455
|
WHY: Capture all startup logs (INFO, WARNING, ERROR, DEBUG) to timestamped
|
|
378
456
|
files for later analysis by the doctor command. This helps diagnose
|
|
379
457
|
startup issues that users may not notice in the console output.
|
|
380
|
-
|
|
458
|
+
|
|
381
459
|
DESIGN DECISIONS:
|
|
382
460
|
- Use ISO-like timestamp format for easy sorting and reading
|
|
383
461
|
- Store in .claude-mpm/logs/startup/ directory
|
|
384
462
|
- Keep all historical startup logs for pattern analysis
|
|
385
463
|
- Add file handler to root logger to capture ALL module logs
|
|
386
|
-
|
|
464
|
+
|
|
387
465
|
Args:
|
|
388
466
|
project_root: Root directory for the project (defaults to cwd)
|
|
389
|
-
|
|
467
|
+
|
|
390
468
|
Returns:
|
|
391
469
|
Path to the created log file
|
|
392
470
|
"""
|
|
393
471
|
if project_root is None:
|
|
394
472
|
project_root = Path.cwd()
|
|
395
|
-
|
|
473
|
+
|
|
396
474
|
# Create log directory
|
|
397
475
|
log_dir = project_root / ".claude-mpm" / "logs" / "startup"
|
|
398
476
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
399
|
-
|
|
477
|
+
|
|
400
478
|
# Generate timestamp for log file
|
|
401
479
|
timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
|
402
480
|
log_file = log_dir / f"startup-{timestamp}.log"
|
|
403
|
-
|
|
481
|
+
|
|
404
482
|
# Create file handler with detailed formatting
|
|
405
|
-
file_handler = logging.FileHandler(log_file, encoding=
|
|
483
|
+
file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
|
406
484
|
file_handler.setLevel(logging.DEBUG) # Capture all levels to file
|
|
407
|
-
|
|
485
|
+
|
|
408
486
|
# Format with timestamp, logger name, level, and message
|
|
409
487
|
formatter = logging.Formatter(
|
|
410
|
-
|
|
411
|
-
datefmt=
|
|
488
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
489
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
412
490
|
)
|
|
413
491
|
file_handler.setFormatter(formatter)
|
|
414
|
-
|
|
492
|
+
|
|
415
493
|
# Add to claude_mpm logger to capture all our logs
|
|
416
494
|
# (Don't add to root logger to avoid duplicates from propagation)
|
|
417
495
|
claude_logger = logging.getLogger("claude_mpm")
|
|
418
496
|
claude_logger.addHandler(file_handler)
|
|
419
497
|
claude_logger.setLevel(logging.DEBUG) # Ensure all levels are captured
|
|
420
|
-
|
|
498
|
+
|
|
421
499
|
# Log startup header
|
|
422
500
|
logger = get_logger("startup")
|
|
423
|
-
logger.info("="*60)
|
|
501
|
+
logger.info("=" * 60)
|
|
424
502
|
logger.info(f"Claude MPM Startup - {datetime.now().isoformat()}")
|
|
425
503
|
logger.info(f"Log file: {log_file}")
|
|
426
|
-
logger.info("="*60)
|
|
427
|
-
|
|
504
|
+
logger.info("=" * 60)
|
|
505
|
+
|
|
428
506
|
# Log system information
|
|
429
507
|
logger.info(f"Python: {sys.version}")
|
|
430
508
|
logger.info(f"Platform: {sys.platform}")
|
|
431
509
|
logger.info(f"CWD: {Path.cwd()}")
|
|
432
510
|
logger.info(f"Project root: {project_root}")
|
|
433
|
-
|
|
511
|
+
|
|
512
|
+
# Log initial memory usage
|
|
513
|
+
if PSUTIL_AVAILABLE:
|
|
514
|
+
try:
|
|
515
|
+
process = psutil.Process()
|
|
516
|
+
memory_info = process.memory_info()
|
|
517
|
+
rss_mb = memory_info.rss / (1024 * 1024)
|
|
518
|
+
vms_mb = memory_info.vms / (1024 * 1024)
|
|
519
|
+
logger.info(f"Initial Memory: RSS={rss_mb:.1f}MB, VMS={vms_mb:.1f}MB")
|
|
520
|
+
except Exception as e:
|
|
521
|
+
logger.debug(f"Failed to get initial memory info: {e}")
|
|
522
|
+
|
|
434
523
|
return log_file
|
|
435
524
|
|
|
436
525
|
|
|
437
|
-
def cleanup_old_startup_logs(
|
|
438
|
-
|
|
439
|
-
|
|
526
|
+
def cleanup_old_startup_logs(
|
|
527
|
+
project_root: Optional[Path] = None, keep_count: Optional[int] = None
|
|
528
|
+
) -> int:
|
|
440
529
|
"""
|
|
441
|
-
Clean up old startup log files.
|
|
442
|
-
|
|
443
|
-
WHY:
|
|
444
|
-
|
|
445
|
-
|
|
530
|
+
Clean up old startup log files using time-based retention.
|
|
531
|
+
|
|
532
|
+
WHY: This function now delegates to LogManager for unified log management
|
|
533
|
+
with time-based retention instead of count-based.
|
|
534
|
+
|
|
446
535
|
DESIGN DECISIONS:
|
|
447
|
-
-
|
|
448
|
-
-
|
|
449
|
-
-
|
|
450
|
-
|
|
536
|
+
- Delegates to LogManager for consistency
|
|
537
|
+
- Converts count to hours (48 hours default)
|
|
538
|
+
- Maintains backward compatibility
|
|
539
|
+
|
|
451
540
|
Args:
|
|
452
541
|
project_root: Root directory for the project
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
542
|
+
keep_count: Ignored (kept for backward compatibility)
|
|
543
|
+
|
|
456
544
|
Returns:
|
|
457
545
|
Number of log files deleted
|
|
458
546
|
"""
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
return
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
547
|
+
try:
|
|
548
|
+
from ..core.log_manager import get_log_manager
|
|
549
|
+
|
|
550
|
+
log_manager = get_log_manager()
|
|
551
|
+
|
|
552
|
+
# Use LogManager's time-based cleanup (48 hours default)
|
|
553
|
+
return log_manager.cleanup_old_startup_logs(project_root)
|
|
554
|
+
except ImportError:
|
|
555
|
+
# Fallback to old implementation if LogManager not available
|
|
556
|
+
# Get retention count from configuration if not specified
|
|
557
|
+
if keep_count is None:
|
|
558
|
+
from claude_mpm.core.config_constants import ConfigConstants
|
|
559
|
+
|
|
560
|
+
keep_count = (
|
|
561
|
+
ConfigConstants.get_logging_setting("startup_logs_retention_count")
|
|
562
|
+
or 10
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
if project_root is None:
|
|
566
|
+
project_root = Path.cwd()
|
|
567
|
+
|
|
568
|
+
log_dir = project_root / ".claude-mpm" / "logs" / "startup"
|
|
569
|
+
|
|
570
|
+
if not log_dir.exists():
|
|
571
|
+
return 0
|
|
572
|
+
|
|
573
|
+
# Get all startup log files
|
|
574
|
+
log_files = sorted(
|
|
575
|
+
log_dir.glob("startup-*.log"), key=lambda p: p.stat().st_mtime, reverse=True
|
|
576
|
+
) # Newest first
|
|
577
|
+
|
|
578
|
+
if len(log_files) <= keep_count:
|
|
579
|
+
return 0 # Already within limit
|
|
580
|
+
|
|
581
|
+
# Delete older files beyond keep_count
|
|
582
|
+
deleted_count = 0
|
|
583
|
+
for log_file in log_files[
|
|
584
|
+
keep_count:
|
|
585
|
+
]: # Keep only the most recent keep_count files
|
|
481
586
|
try:
|
|
482
587
|
log_file.unlink()
|
|
483
588
|
deleted_count += 1
|
|
484
589
|
except Exception:
|
|
485
590
|
pass # Ignore deletion errors
|
|
486
|
-
|
|
487
|
-
|
|
591
|
+
|
|
592
|
+
return deleted_count
|
|
488
593
|
|
|
489
594
|
|
|
490
595
|
def get_latest_startup_log(project_root: Optional[Path] = None) -> Optional[Path]:
|
|
491
596
|
"""
|
|
492
597
|
Get the path to the most recent startup log file.
|
|
493
|
-
|
|
598
|
+
|
|
494
599
|
Args:
|
|
495
600
|
project_root: Root directory for the project
|
|
496
|
-
|
|
601
|
+
|
|
497
602
|
Returns:
|
|
498
603
|
Path to latest log file or None if no logs exist
|
|
499
604
|
"""
|
|
500
605
|
if project_root is None:
|
|
501
606
|
project_root = Path.cwd()
|
|
502
|
-
|
|
607
|
+
|
|
503
608
|
log_dir = project_root / ".claude-mpm" / "logs" / "startup"
|
|
504
|
-
|
|
609
|
+
|
|
505
610
|
if not log_dir.exists():
|
|
506
611
|
return None
|
|
507
|
-
|
|
508
|
-
log_files = sorted(
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
612
|
+
|
|
613
|
+
log_files = sorted(
|
|
614
|
+
log_dir.glob("startup-*.log"), key=lambda p: p.stat().st_mtime, reverse=True
|
|
615
|
+
)
|
|
616
|
+
|
|
512
617
|
return log_files[0] if log_files else None
|
|
513
618
|
|
|
514
619
|
|
|
515
620
|
def log_startup_status(monitor_mode: bool = False, websocket_port: int = 8765) -> None:
|
|
516
621
|
"""
|
|
517
622
|
Log comprehensive startup status for MCP server and monitor setup.
|
|
518
|
-
|
|
623
|
+
|
|
519
624
|
This function should be called during application startup to provide
|
|
520
625
|
detailed information about MCP and monitor setup status.
|
|
521
|
-
|
|
626
|
+
|
|
522
627
|
Args:
|
|
523
628
|
monitor_mode: Whether monitor mode is enabled
|
|
524
629
|
websocket_port: WebSocket port for monitoring
|
|
525
630
|
"""
|
|
526
631
|
try:
|
|
527
632
|
status_logger = StartupStatusLogger("cli")
|
|
528
|
-
|
|
633
|
+
|
|
634
|
+
# Log memory status at startup
|
|
635
|
+
status_logger.log_memory_status()
|
|
636
|
+
|
|
529
637
|
# Log MCP server status
|
|
530
638
|
status_logger.log_mcp_server_status()
|
|
531
|
-
|
|
639
|
+
|
|
532
640
|
# Log monitor setup status
|
|
533
641
|
status_logger.log_monitor_setup_status(monitor_mode, websocket_port)
|
|
534
|
-
|
|
642
|
+
|
|
535
643
|
except Exception as e:
|
|
536
644
|
# Don't let logging failures prevent startup
|
|
537
645
|
logger = get_logger("cli")
|
|
538
|
-
logger.debug(f"Startup status logging failed: {e}")
|
|
646
|
+
logger.debug(f"Startup status logging failed: {e}")
|