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
claude_mpm/cli/commands/run.py
CHANGED
|
@@ -13,27 +13,23 @@ DESIGN DECISIONS:
|
|
|
13
13
|
- Support multiple output formats (json, yaml, table, text)
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
import logging
|
|
17
|
-
import os
|
|
18
16
|
import subprocess
|
|
19
17
|
import sys
|
|
20
|
-
import time
|
|
21
|
-
import webbrowser
|
|
22
18
|
from datetime import datetime
|
|
23
|
-
from typing import
|
|
19
|
+
from typing import Optional
|
|
24
20
|
|
|
25
21
|
from ...constants import LogLevel
|
|
26
|
-
from ...core.config import Config
|
|
27
22
|
from ...core.logger import get_logger
|
|
28
|
-
from ...core.
|
|
29
|
-
from ...
|
|
30
|
-
from ...services.
|
|
31
|
-
from ...
|
|
23
|
+
from ...core.unified_paths import get_scripts_dir
|
|
24
|
+
from ...services.cli.dashboard_launcher import DashboardLauncher
|
|
25
|
+
from ...services.cli.session_manager import SessionManager
|
|
26
|
+
from ...services.cli.socketio_manager import SocketIOManager
|
|
27
|
+
from ...services.cli.startup_checker import StartupCheckerService
|
|
32
28
|
from ..shared import BaseCommand, CommandResult
|
|
33
29
|
from ..startup_logging import (
|
|
34
|
-
|
|
30
|
+
cleanup_old_startup_logs,
|
|
31
|
+
log_startup_status,
|
|
35
32
|
setup_startup_logging,
|
|
36
|
-
cleanup_old_startup_logs
|
|
37
33
|
)
|
|
38
34
|
from ..utils import get_user_input, list_agent_versions_at_startup
|
|
39
35
|
|
|
@@ -159,7 +155,7 @@ def create_session_context(session_id, session_manager):
|
|
|
159
155
|
|
|
160
156
|
base_context = create_simple_context()
|
|
161
157
|
|
|
162
|
-
session_data = session_manager.
|
|
158
|
+
session_data = session_manager.get_session_info(session_id)
|
|
163
159
|
if not session_data:
|
|
164
160
|
return base_context
|
|
165
161
|
|
|
@@ -207,14 +203,22 @@ class RunCommand(BaseCommand):
|
|
|
207
203
|
# Execute the main run logic
|
|
208
204
|
success = self._execute_run_session(args)
|
|
209
205
|
|
|
206
|
+
# Log memory stats at session completion
|
|
207
|
+
from ..startup_logging import log_memory_stats
|
|
208
|
+
|
|
209
|
+
log_memory_stats(self.logger, "Session End Memory")
|
|
210
|
+
|
|
210
211
|
if success:
|
|
211
|
-
return CommandResult.success_result(
|
|
212
|
-
|
|
213
|
-
|
|
212
|
+
return CommandResult.success_result(
|
|
213
|
+
"Claude session completed successfully"
|
|
214
|
+
)
|
|
215
|
+
return CommandResult.error_result("Claude session failed", exit_code=1)
|
|
214
216
|
|
|
215
217
|
except KeyboardInterrupt:
|
|
216
218
|
self.logger.info("Session interrupted by user")
|
|
217
|
-
return CommandResult.error_result(
|
|
219
|
+
return CommandResult.error_result(
|
|
220
|
+
"Session cancelled by user", exit_code=130
|
|
221
|
+
)
|
|
218
222
|
except Exception as e:
|
|
219
223
|
self.logger.error(f"Error running Claude session: {e}", exc_info=True)
|
|
220
224
|
return CommandResult.error_result(f"Error running Claude session: {e}")
|
|
@@ -248,7 +252,9 @@ class RunCommand(BaseCommand):
|
|
|
248
252
|
self._check_claude_json_memory(args)
|
|
249
253
|
|
|
250
254
|
# Handle session management
|
|
251
|
-
session_manager, resume_session_id, resume_context =
|
|
255
|
+
session_manager, resume_session_id, resume_context = (
|
|
256
|
+
self._setup_session_management(args)
|
|
257
|
+
)
|
|
252
258
|
|
|
253
259
|
# Handle dependency checking
|
|
254
260
|
self._handle_dependency_checking(args)
|
|
@@ -260,7 +266,9 @@ class RunCommand(BaseCommand):
|
|
|
260
266
|
runner = self._setup_claude_runner(args, monitor_mode, websocket_port)
|
|
261
267
|
|
|
262
268
|
# Create context and run session
|
|
263
|
-
context = self._create_session_context(
|
|
269
|
+
context = self._create_session_context(
|
|
270
|
+
args, session_manager, resume_session_id, resume_context
|
|
271
|
+
)
|
|
264
272
|
|
|
265
273
|
# Execute the session
|
|
266
274
|
return self._execute_session(args, runner, context)
|
|
@@ -271,23 +279,29 @@ class RunCommand(BaseCommand):
|
|
|
271
279
|
|
|
272
280
|
def _check_configuration_health(self):
|
|
273
281
|
"""Check configuration health at startup."""
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
282
|
+
# Use new StartupCheckerService
|
|
283
|
+
from ...core.config import Config
|
|
284
|
+
|
|
285
|
+
config_service = Config()
|
|
286
|
+
checker = StartupCheckerService(config_service)
|
|
287
|
+
warnings = checker.check_configuration()
|
|
288
|
+
checker.display_warnings(warnings)
|
|
277
289
|
|
|
278
290
|
def _check_claude_json_memory(self, args):
|
|
279
291
|
"""Check .claude.json file size and warn about memory issues."""
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
292
|
+
# Use new StartupCheckerService
|
|
293
|
+
from ...core.config import Config
|
|
294
|
+
|
|
295
|
+
config_service = Config()
|
|
296
|
+
checker = StartupCheckerService(config_service)
|
|
297
|
+
resume_enabled = getattr(args, "mpm_resume", False)
|
|
298
|
+
warning = checker.check_memory(resume_enabled)
|
|
299
|
+
if warning:
|
|
300
|
+
checker.display_warnings([warning])
|
|
283
301
|
|
|
284
302
|
def _setup_session_management(self, args):
|
|
285
303
|
"""Setup session management and handle resumption."""
|
|
286
|
-
|
|
287
|
-
from ...core.session_manager import SessionManager
|
|
288
|
-
except ImportError:
|
|
289
|
-
from claude_mpm.core.session_manager import SessionManager
|
|
290
|
-
|
|
304
|
+
# Use the new SessionManager service from the CLI services layer
|
|
291
305
|
session_manager = SessionManager()
|
|
292
306
|
resume_session_id = None
|
|
293
307
|
resume_context = None
|
|
@@ -297,11 +311,15 @@ class RunCommand(BaseCommand):
|
|
|
297
311
|
# Resume the last interactive session
|
|
298
312
|
resume_session_id = session_manager.get_last_interactive_session()
|
|
299
313
|
if resume_session_id:
|
|
300
|
-
session_data = session_manager.
|
|
314
|
+
session_data = session_manager.get_session_info(resume_session_id)
|
|
301
315
|
if session_data:
|
|
302
316
|
resume_context = session_data.get("context", "default")
|
|
303
|
-
self.logger.info(
|
|
304
|
-
|
|
317
|
+
self.logger.info(
|
|
318
|
+
f"Resuming session {resume_session_id} (context: {resume_context})"
|
|
319
|
+
)
|
|
320
|
+
print(
|
|
321
|
+
f"🔄 Resuming session {resume_session_id[:8]}... (created: {session_data.get('created_at', 'unknown')})"
|
|
322
|
+
)
|
|
305
323
|
else:
|
|
306
324
|
self.logger.warning(f"Session {resume_session_id} not found")
|
|
307
325
|
else:
|
|
@@ -310,11 +328,15 @@ class RunCommand(BaseCommand):
|
|
|
310
328
|
else:
|
|
311
329
|
# Resume specific session by ID
|
|
312
330
|
resume_session_id = args.mpm_resume
|
|
313
|
-
session_data = session_manager.
|
|
331
|
+
session_data = session_manager.get_session_info(resume_session_id)
|
|
314
332
|
if session_data:
|
|
315
333
|
resume_context = session_data.get("context", "default")
|
|
316
|
-
self.logger.info(
|
|
317
|
-
|
|
334
|
+
self.logger.info(
|
|
335
|
+
f"Resuming session {resume_session_id} (context: {resume_context})"
|
|
336
|
+
)
|
|
337
|
+
print(
|
|
338
|
+
f"🔄 Resuming session {resume_session_id[:8]}... (context: {resume_context})"
|
|
339
|
+
)
|
|
318
340
|
else:
|
|
319
341
|
self.logger.error(f"Session {resume_session_id} not found")
|
|
320
342
|
print(f"❌ Session {resume_session_id} not found")
|
|
@@ -355,17 +377,25 @@ class RunCommand(BaseCommand):
|
|
|
355
377
|
# Check dependencies and prompt for installation if needed
|
|
356
378
|
missing_deps = loader.check_dependencies()
|
|
357
379
|
if missing_deps:
|
|
358
|
-
self.logger.info(
|
|
380
|
+
self.logger.info(
|
|
381
|
+
f"Found {len(missing_deps)} missing dependencies"
|
|
382
|
+
)
|
|
359
383
|
|
|
360
384
|
# Prompt user for installation
|
|
361
|
-
print(
|
|
385
|
+
print(
|
|
386
|
+
f"\n📦 Found {len(missing_deps)} missing dependencies:"
|
|
387
|
+
)
|
|
362
388
|
for dep in missing_deps[:5]: # Show first 5
|
|
363
389
|
print(f" • {dep}")
|
|
364
390
|
if len(missing_deps) > 5:
|
|
365
391
|
print(f" ... and {len(missing_deps) - 5} more")
|
|
366
392
|
|
|
367
|
-
response =
|
|
368
|
-
|
|
393
|
+
response = (
|
|
394
|
+
input("\nInstall missing dependencies? (y/N): ")
|
|
395
|
+
.strip()
|
|
396
|
+
.lower()
|
|
397
|
+
)
|
|
398
|
+
if response in ["y", "yes"]:
|
|
369
399
|
loader.auto_install = True
|
|
370
400
|
loader.install_dependencies(missing_deps)
|
|
371
401
|
print("✅ Dependencies installed successfully")
|
|
@@ -375,8 +405,12 @@ class RunCommand(BaseCommand):
|
|
|
375
405
|
# Just check without prompting
|
|
376
406
|
missing_deps = loader.check_dependencies()
|
|
377
407
|
if missing_deps:
|
|
378
|
-
self.logger.warning(
|
|
379
|
-
|
|
408
|
+
self.logger.warning(
|
|
409
|
+
f"Found {len(missing_deps)} missing dependencies"
|
|
410
|
+
)
|
|
411
|
+
print(
|
|
412
|
+
f"⚠️ Found {len(missing_deps)} missing dependencies. Use --force-check-dependencies to install."
|
|
413
|
+
)
|
|
380
414
|
|
|
381
415
|
# Update cache
|
|
382
416
|
smart_checker.update_cache(deployment_hash)
|
|
@@ -389,38 +423,43 @@ class RunCommand(BaseCommand):
|
|
|
389
423
|
self.logger.warning(f"Dependency check failed: {e}")
|
|
390
424
|
|
|
391
425
|
def _setup_monitoring(self, args):
|
|
392
|
-
"""Setup monitoring configuration."""
|
|
426
|
+
"""Setup monitoring configuration using SocketIOManager."""
|
|
393
427
|
monitor_mode = getattr(args, "monitor", False)
|
|
394
428
|
websocket_port = 8765 # Default port
|
|
395
429
|
|
|
396
430
|
if monitor_mode:
|
|
397
|
-
#
|
|
398
|
-
|
|
399
|
-
|
|
431
|
+
# Use SocketIOManager for server management
|
|
432
|
+
socketio_manager = SocketIOManager(self.logger)
|
|
433
|
+
|
|
434
|
+
# Check dependencies
|
|
435
|
+
deps_ok, error_msg = socketio_manager.ensure_dependencies()
|
|
436
|
+
if not deps_ok:
|
|
437
|
+
self.logger.warning(
|
|
438
|
+
f"Socket.IO dependencies not available: {error_msg}, disabling monitor mode"
|
|
439
|
+
)
|
|
400
440
|
monitor_mode = False
|
|
401
441
|
else:
|
|
402
|
-
#
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
self.logger.warning(f"Could not open browser: {e}")
|
|
442
|
+
# Find available port and start server
|
|
443
|
+
websocket_port = socketio_manager.find_available_port(8765)
|
|
444
|
+
success, server_info = socketio_manager.start_server(
|
|
445
|
+
port=websocket_port
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
if not success:
|
|
449
|
+
self.logger.warning(
|
|
450
|
+
"Failed to start Socket.IO server, disabling monitor mode"
|
|
451
|
+
)
|
|
452
|
+
monitor_mode = False
|
|
453
|
+
else:
|
|
454
|
+
# Use DashboardLauncher for browser opening only
|
|
455
|
+
dashboard_launcher = DashboardLauncher(self.logger)
|
|
456
|
+
monitor_url = dashboard_launcher.get_dashboard_url(websocket_port)
|
|
457
|
+
|
|
458
|
+
# Try to open browser
|
|
459
|
+
browser_opened = dashboard_launcher._open_browser(monitor_url)
|
|
460
|
+
args._browser_opened_by_cli = browser_opened
|
|
461
|
+
|
|
462
|
+
if not browser_opened:
|
|
424
463
|
print(f"💡 Monitor interface available at: {monitor_url}")
|
|
425
464
|
|
|
426
465
|
return monitor_mode, websocket_port
|
|
@@ -461,11 +500,15 @@ class RunCommand(BaseCommand):
|
|
|
461
500
|
# Set browser opening flag for monitor mode
|
|
462
501
|
if monitor_mode:
|
|
463
502
|
runner._should_open_monitor_browser = True
|
|
464
|
-
runner._browser_opened_by_cli = getattr(
|
|
503
|
+
runner._browser_opened_by_cli = getattr(
|
|
504
|
+
args, "_browser_opened_by_cli", False
|
|
505
|
+
)
|
|
465
506
|
|
|
466
507
|
return runner
|
|
467
508
|
|
|
468
|
-
def _create_session_context(
|
|
509
|
+
def _create_session_context(
|
|
510
|
+
self, args, session_manager, resume_session_id, resume_context
|
|
511
|
+
):
|
|
469
512
|
"""Create session context."""
|
|
470
513
|
try:
|
|
471
514
|
from ...core.claude_runner import create_simple_context
|
|
@@ -476,14 +519,16 @@ class RunCommand(BaseCommand):
|
|
|
476
519
|
# For resumed sessions, create enhanced context with session information
|
|
477
520
|
context = create_session_context(resume_session_id, session_manager)
|
|
478
521
|
# Update session usage
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
522
|
+
session = session_manager.load_session(resume_session_id)
|
|
523
|
+
if session:
|
|
524
|
+
session.last_used = datetime.now().isoformat()
|
|
525
|
+
session.use_count += 1
|
|
526
|
+
session_manager.save_session(session)
|
|
482
527
|
else:
|
|
483
528
|
# Create a new session for tracking
|
|
484
|
-
|
|
529
|
+
new_session = session_manager.create_session("default")
|
|
485
530
|
context = create_simple_context()
|
|
486
|
-
self.logger.info(f"Created new session {
|
|
531
|
+
self.logger.info(f"Created new session {new_session.id}")
|
|
487
532
|
|
|
488
533
|
return context
|
|
489
534
|
|
|
@@ -501,18 +546,19 @@ class RunCommand(BaseCommand):
|
|
|
501
546
|
if not success:
|
|
502
547
|
self.logger.error("Session failed")
|
|
503
548
|
return False
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
subprocess.run([sys.executable, str(wrapper_path)])
|
|
511
|
-
else:
|
|
512
|
-
self.logger.warning("Interactive wrapper not found, falling back to normal mode")
|
|
513
|
-
runner.run_interactive(context)
|
|
549
|
+
# Interactive mode
|
|
550
|
+
elif getattr(args, "intercept_commands", False):
|
|
551
|
+
wrapper_path = get_scripts_dir() / "interactive_wrapper.py"
|
|
552
|
+
if wrapper_path.exists():
|
|
553
|
+
print("Starting interactive session with command interception...")
|
|
554
|
+
subprocess.run([sys.executable, str(wrapper_path)], check=False)
|
|
514
555
|
else:
|
|
556
|
+
self.logger.warning(
|
|
557
|
+
"Interactive wrapper not found, falling back to normal mode"
|
|
558
|
+
)
|
|
515
559
|
runner.run_interactive(context)
|
|
560
|
+
else:
|
|
561
|
+
runner.run_interactive(context)
|
|
516
562
|
|
|
517
563
|
return True
|
|
518
564
|
|
|
@@ -520,17 +566,6 @@ class RunCommand(BaseCommand):
|
|
|
520
566
|
self.logger.error(f"Session execution failed: {e}")
|
|
521
567
|
return False
|
|
522
568
|
|
|
523
|
-
def _is_socketio_server_running(self, port: int) -> bool:
|
|
524
|
-
"""Check if Socket.IO server is running on the specified port."""
|
|
525
|
-
try:
|
|
526
|
-
import socket
|
|
527
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
528
|
-
s.settimeout(1)
|
|
529
|
-
result = s.connect_ex(('localhost', port))
|
|
530
|
-
return result == 0
|
|
531
|
-
except Exception:
|
|
532
|
-
return False
|
|
533
|
-
|
|
534
569
|
|
|
535
570
|
def run_session(args):
|
|
536
571
|
"""
|
|
@@ -561,14 +596,14 @@ def run_session_legacy(args):
|
|
|
561
596
|
args: Parsed command line arguments
|
|
562
597
|
"""
|
|
563
598
|
# Set up startup logging to file early in the process
|
|
564
|
-
|
|
565
|
-
|
|
599
|
+
setup_startup_logging(Path.cwd())
|
|
600
|
+
|
|
566
601
|
logger = get_logger("cli")
|
|
567
602
|
if args.logging != LogLevel.OFF.value:
|
|
568
603
|
logger.info("Starting Claude MPM session")
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
# Clean up old startup logs (
|
|
604
|
+
# Log file already announced in startup_logging.py when created
|
|
605
|
+
|
|
606
|
+
# Clean up old startup logs (using configured retention count)
|
|
572
607
|
try:
|
|
573
608
|
deleted_count = cleanup_old_startup_logs(Path.cwd())
|
|
574
609
|
if deleted_count > 0:
|
|
@@ -590,12 +625,10 @@ def run_session_legacy(args):
|
|
|
590
625
|
|
|
591
626
|
try:
|
|
592
627
|
from ...core.claude_runner import ClaudeRunner, create_simple_context
|
|
593
|
-
from ...core.session_manager import SessionManager
|
|
594
628
|
except ImportError:
|
|
595
629
|
from claude_mpm.core.claude_runner import ClaudeRunner, create_simple_context
|
|
596
|
-
from claude_mpm.core.session_manager import SessionManager
|
|
597
630
|
|
|
598
|
-
# Handle session resumption
|
|
631
|
+
# Handle session resumption using the new SessionManager service
|
|
599
632
|
session_manager = SessionManager()
|
|
600
633
|
resume_session_id = None
|
|
601
634
|
resume_context = None
|
|
@@ -605,7 +638,7 @@ def run_session_legacy(args):
|
|
|
605
638
|
# Resume the last interactive session
|
|
606
639
|
resume_session_id = session_manager.get_last_interactive_session()
|
|
607
640
|
if resume_session_id:
|
|
608
|
-
session_data = session_manager.
|
|
641
|
+
session_data = session_manager.get_session_info(resume_session_id)
|
|
609
642
|
if session_data:
|
|
610
643
|
resume_context = session_data.get("context", "default")
|
|
611
644
|
logger.info(
|
|
@@ -622,7 +655,7 @@ def run_session_legacy(args):
|
|
|
622
655
|
else:
|
|
623
656
|
# Resume specific session by ID
|
|
624
657
|
resume_session_id = args.mpm_resume
|
|
625
|
-
session_data = session_manager.
|
|
658
|
+
session_data = session_manager.get_session_info(resume_session_id)
|
|
626
659
|
if session_data:
|
|
627
660
|
resume_context = session_data.get("context", "default")
|
|
628
661
|
logger.info(
|
|
@@ -637,6 +670,14 @@ def run_session_legacy(args):
|
|
|
637
670
|
print("💡 Use 'claude-mpm sessions' to list available sessions")
|
|
638
671
|
return
|
|
639
672
|
|
|
673
|
+
# Deploy MPM slash commands to user's Claude configuration
|
|
674
|
+
try:
|
|
675
|
+
from ...services.command_deployment_service import deploy_commands_on_startup
|
|
676
|
+
|
|
677
|
+
deploy_commands_on_startup(force=False)
|
|
678
|
+
except Exception as e:
|
|
679
|
+
logger.debug(f"Failed to deploy MPM commands (non-critical): {e}")
|
|
680
|
+
|
|
640
681
|
# Skip native agents if disabled
|
|
641
682
|
if getattr(args, "no_native_agents", False):
|
|
642
683
|
print("Native agents disabled")
|
|
@@ -689,7 +730,7 @@ def run_session_legacy(args):
|
|
|
689
730
|
|
|
690
731
|
if can_prompt and missing_count > 0:
|
|
691
732
|
# Interactive prompt for installation
|
|
692
|
-
print(
|
|
733
|
+
print("\n📦 Missing dependencies detected:")
|
|
693
734
|
for dep in results["summary"]["missing_python"][:5]:
|
|
694
735
|
print(f" - {dep}")
|
|
695
736
|
if missing_count > 5:
|
|
@@ -774,17 +815,17 @@ def run_session_legacy(args):
|
|
|
774
815
|
# Create simple runner
|
|
775
816
|
enable_tickets = not args.no_tickets
|
|
776
817
|
raw_claude_args = getattr(args, "claude_args", []) or []
|
|
777
|
-
|
|
818
|
+
|
|
778
819
|
# Add --resume to claude_args if the flag is set
|
|
779
820
|
resume_flag_present = getattr(args, "resume", False)
|
|
780
821
|
if resume_flag_present:
|
|
781
822
|
logger.info("📌 --resume flag detected in args")
|
|
782
823
|
if "--resume" not in raw_claude_args:
|
|
783
|
-
raw_claude_args = ["--resume"
|
|
824
|
+
raw_claude_args = ["--resume", *raw_claude_args]
|
|
784
825
|
logger.info("✅ Added --resume to claude_args")
|
|
785
826
|
else:
|
|
786
827
|
logger.info("ℹ️ --resume already in claude_args")
|
|
787
|
-
|
|
828
|
+
|
|
788
829
|
# Filter out claude-mpm specific flags before passing to Claude CLI
|
|
789
830
|
logger.debug(f"Pre-filter claude_args: {raw_claude_args}")
|
|
790
831
|
claude_args = filter_claude_mpm_args(raw_claude_args)
|
|
@@ -794,9 +835,9 @@ def run_session_legacy(args):
|
|
|
794
835
|
if raw_claude_args != claude_args:
|
|
795
836
|
filtered_out = list(set(raw_claude_args) - set(claude_args))
|
|
796
837
|
logger.debug(f"Filtered out MPM-specific args: {filtered_out}")
|
|
797
|
-
|
|
838
|
+
|
|
798
839
|
logger.info(f"Final claude_args being passed: {claude_args}")
|
|
799
|
-
|
|
840
|
+
|
|
800
841
|
# Explicit verification of --resume flag
|
|
801
842
|
if resume_flag_present:
|
|
802
843
|
if "--resume" in claude_args:
|
|
@@ -814,11 +855,14 @@ def run_session_legacy(args):
|
|
|
814
855
|
|
|
815
856
|
# Display Socket.IO server info if enabled
|
|
816
857
|
if enable_websocket:
|
|
817
|
-
#
|
|
858
|
+
# Use SocketIOManager for server management
|
|
859
|
+
socketio_manager = SocketIOManager(logger)
|
|
860
|
+
|
|
861
|
+
# Check dependencies
|
|
818
862
|
print("🔧 Checking Socket.IO dependencies...")
|
|
819
|
-
|
|
863
|
+
deps_ok, error_msg = socketio_manager.ensure_dependencies()
|
|
820
864
|
|
|
821
|
-
if not
|
|
865
|
+
if not deps_ok:
|
|
822
866
|
print(f"❌ Failed to install Socket.IO dependencies: {error_msg}")
|
|
823
867
|
print(
|
|
824
868
|
" Please install manually: pip install python-socketio aiohttp python-engineio"
|
|
@@ -828,32 +872,36 @@ def run_session_legacy(args):
|
|
|
828
872
|
else:
|
|
829
873
|
print("✓ Socket.IO dependencies ready")
|
|
830
874
|
|
|
831
|
-
|
|
832
|
-
import socketio
|
|
833
|
-
|
|
834
|
-
print(f"✓ Socket.IO server enabled at http://localhost:{websocket_port}")
|
|
835
|
-
if launch_method == "exec":
|
|
836
|
-
print(
|
|
837
|
-
" Note: Socket.IO monitoring using exec mode with Claude Code hooks"
|
|
838
|
-
)
|
|
839
|
-
|
|
840
|
-
# Launch Socket.IO dashboard if in monitor mode
|
|
875
|
+
# Find available port and start server if in monitor mode
|
|
841
876
|
if monitor_mode:
|
|
842
|
-
|
|
843
|
-
|
|
877
|
+
websocket_port = socketio_manager.find_available_port(websocket_port)
|
|
878
|
+
success, server_info = socketio_manager.start_server(
|
|
879
|
+
port=websocket_port
|
|
844
880
|
)
|
|
845
|
-
|
|
846
|
-
|
|
881
|
+
|
|
882
|
+
if success:
|
|
883
|
+
print(f"✓ Socket.IO server enabled at {server_info.url}")
|
|
884
|
+
if launch_method == "exec":
|
|
885
|
+
print(
|
|
886
|
+
" Note: Socket.IO monitoring using exec mode with Claude Code hooks"
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
# Use DashboardLauncher for browser opening
|
|
890
|
+
dashboard_launcher = DashboardLauncher(logger)
|
|
891
|
+
monitor_url = dashboard_launcher.get_dashboard_url(websocket_port)
|
|
892
|
+
browser_opened = dashboard_launcher._open_browser(monitor_url)
|
|
893
|
+
args._browser_opened_by_cli = browser_opened
|
|
894
|
+
|
|
895
|
+
if not browser_opened:
|
|
896
|
+
print(f"💡 Monitor interface available at: {monitor_url}")
|
|
897
|
+
else:
|
|
898
|
+
print("⚠️ Failed to launch Socket.IO monitor")
|
|
847
899
|
print(
|
|
848
900
|
f" You can manually run: python scripts/launch_socketio_dashboard.py --port {websocket_port}"
|
|
849
901
|
)
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
print(f"⚠️ Socket.IO still not available after installation attempt: {e}")
|
|
854
|
-
print(" This might be a virtual environment issue.")
|
|
855
|
-
print(" Try: pip install python-socketio aiohttp python-engineio")
|
|
856
|
-
print(" Or: pip install claude-mpm[monitor]")
|
|
902
|
+
args._browser_opened_by_cli = False
|
|
903
|
+
else:
|
|
904
|
+
print(f"✓ Socket.IO ready (port: {websocket_port})")
|
|
857
905
|
|
|
858
906
|
runner = ClaudeRunner(
|
|
859
907
|
enable_tickets=enable_tickets,
|
|
@@ -880,16 +928,16 @@ def run_session_legacy(args):
|
|
|
880
928
|
# For resumed sessions, create enhanced context with session information
|
|
881
929
|
context = create_session_context(resume_session_id, session_manager)
|
|
882
930
|
# Update session usage
|
|
883
|
-
session_manager.
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
931
|
+
session = session_manager.load_session(resume_session_id)
|
|
932
|
+
if session:
|
|
933
|
+
session.last_used = datetime.now().isoformat()
|
|
934
|
+
session.use_count += 1
|
|
935
|
+
session_manager.save_session(session)
|
|
888
936
|
else:
|
|
889
937
|
# Create a new session for tracking
|
|
890
|
-
|
|
938
|
+
new_session = session_manager.create_session("default")
|
|
891
939
|
context = create_simple_context()
|
|
892
|
-
logger.info(f"Created new session {
|
|
940
|
+
logger.info(f"Created new session {new_session.id}")
|
|
893
941
|
|
|
894
942
|
# For monitor mode, we handled everything in launch_socketio_monitor
|
|
895
943
|
# No need for ClaudeRunner browser delegation
|
|
@@ -905,429 +953,72 @@ def run_session_legacy(args):
|
|
|
905
953
|
success = runner.run_oneshot(user_input, context)
|
|
906
954
|
if not success:
|
|
907
955
|
logger.error("Session failed")
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
subprocess.run([sys.executable, str(wrapper_path)])
|
|
915
|
-
else:
|
|
916
|
-
logger.warning(
|
|
917
|
-
"Interactive wrapper not found, falling back to normal mode"
|
|
918
|
-
)
|
|
919
|
-
runner.run_interactive(context)
|
|
956
|
+
# Interactive mode
|
|
957
|
+
elif getattr(args, "intercept_commands", False):
|
|
958
|
+
wrapper_path = get_scripts_dir() / "interactive_wrapper.py"
|
|
959
|
+
if wrapper_path.exists():
|
|
960
|
+
print("Starting interactive session with command interception...")
|
|
961
|
+
subprocess.run([sys.executable, str(wrapper_path)], check=False)
|
|
920
962
|
else:
|
|
963
|
+
logger.warning("Interactive wrapper not found, falling back to normal mode")
|
|
921
964
|
runner.run_interactive(context)
|
|
965
|
+
else:
|
|
966
|
+
runner.run_interactive(context)
|
|
922
967
|
|
|
923
968
|
|
|
969
|
+
# Legacy helper functions - now delegating to SocketIOManager
|
|
924
970
|
def launch_socketio_monitor(port, logger):
|
|
925
|
-
"""Launch the Socket.IO monitoring dashboard."""
|
|
926
|
-
|
|
971
|
+
"""Launch the Socket.IO monitoring dashboard (legacy compatibility)."""
|
|
972
|
+
socketio_manager = SocketIOManager(logger)
|
|
973
|
+
success, server_info = socketio_manager.start_server(port=port)
|
|
927
974
|
|
|
928
|
-
|
|
929
|
-
|
|
975
|
+
if success:
|
|
976
|
+
# Open browser using DashboardLauncher
|
|
977
|
+
launcher = DashboardLauncher(logger)
|
|
978
|
+
browser_opened = launcher._open_browser(server_info.url)
|
|
979
|
+
return success, browser_opened
|
|
930
980
|
|
|
931
|
-
|
|
932
|
-
# Socket.IO monitoring functions moved to socketio_monitor.py
|
|
981
|
+
return False, False
|
|
933
982
|
|
|
934
983
|
|
|
935
984
|
def _check_socketio_server_running(port, logger):
|
|
936
|
-
"""Check if a Socket.IO server is running on the specified port."""
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
monitor = SocketIOMonitor(logger)
|
|
940
|
-
return monitor.check_server_running(port)
|
|
985
|
+
"""Check if a Socket.IO server is running on the specified port (legacy compatibility)."""
|
|
986
|
+
socketio_manager = SocketIOManager(logger)
|
|
987
|
+
return socketio_manager.is_server_running(port)
|
|
941
988
|
|
|
942
989
|
|
|
943
990
|
def _start_standalone_socketio_server(port, logger):
|
|
944
|
-
"""Start a standalone Socket.IO server
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
return monitor.start_standalone_server(port)
|
|
949
|
-
"""
|
|
950
|
-
Start a standalone Socket.IO server using the Python daemon.
|
|
951
|
-
|
|
952
|
-
WHY: For monitor mode, we want a persistent server that runs independently
|
|
953
|
-
of the Claude session. This allows users to monitor multiple sessions and
|
|
954
|
-
keeps the dashboard available even when Claude isn't running.
|
|
955
|
-
|
|
956
|
-
DESIGN DECISION: We use a pure Python daemon script to manage the server
|
|
957
|
-
process. This avoids Node.js dependencies (like PM2) and provides proper
|
|
958
|
-
process management with PID tracking.
|
|
959
|
-
|
|
960
|
-
Args:
|
|
961
|
-
port: Port number for the server
|
|
962
|
-
logger: Logger instance for output
|
|
963
|
-
|
|
964
|
-
Returns:
|
|
965
|
-
bool: True if server started successfully, False otherwise
|
|
966
|
-
"""
|
|
967
|
-
try:
|
|
968
|
-
import subprocess
|
|
969
|
-
|
|
970
|
-
from ...core.unified_paths import get_scripts_dir
|
|
971
|
-
|
|
972
|
-
# Get path to daemon script in package
|
|
973
|
-
daemon_script = get_package_root() / "scripts" / "socketio_daemon.py"
|
|
974
|
-
|
|
975
|
-
if not daemon_script.exists():
|
|
976
|
-
logger.error(f"Socket.IO daemon script not found: {daemon_script}")
|
|
977
|
-
return False
|
|
978
|
-
|
|
979
|
-
logger.info(f"Starting Socket.IO server daemon on port {port}")
|
|
980
|
-
|
|
981
|
-
# Start the daemon
|
|
982
|
-
result = subprocess.run(
|
|
983
|
-
[sys.executable, str(daemon_script), "start"],
|
|
984
|
-
capture_output=True,
|
|
985
|
-
text=True,
|
|
986
|
-
)
|
|
987
|
-
|
|
988
|
-
if result.returncode != 0:
|
|
989
|
-
logger.error(f"Failed to start Socket.IO daemon: {result.stderr}")
|
|
990
|
-
return False
|
|
991
|
-
|
|
992
|
-
# Wait for server using event-based polling instead of fixed delays
|
|
993
|
-
# WHY: Replace fixed sleep delays with active polling for faster startup detection
|
|
994
|
-
max_wait_time = 15 # Maximum 15 seconds
|
|
995
|
-
poll_interval = 0.1 # Start with 100ms polling
|
|
996
|
-
|
|
997
|
-
logger.info(f"Waiting up to {max_wait_time} seconds for server to be ready...")
|
|
998
|
-
|
|
999
|
-
# Give daemon minimal time to fork
|
|
1000
|
-
time.sleep(0.2) # Reduced from 0.5s
|
|
1001
|
-
|
|
1002
|
-
start_time = time.time()
|
|
1003
|
-
attempt = 0
|
|
1004
|
-
|
|
1005
|
-
while time.time() - start_time < max_wait_time:
|
|
1006
|
-
attempt += 1
|
|
1007
|
-
elapsed = time.time() - start_time
|
|
1008
|
-
|
|
1009
|
-
logger.debug(
|
|
1010
|
-
f"Checking server readiness (attempt {attempt}, elapsed {elapsed:.1f}s)"
|
|
1011
|
-
)
|
|
1012
|
-
|
|
1013
|
-
# Adaptive polling - start fast, slow down over time
|
|
1014
|
-
if elapsed < 2:
|
|
1015
|
-
poll_interval = 0.1 # 100ms for first 2 seconds
|
|
1016
|
-
elif elapsed < 5:
|
|
1017
|
-
poll_interval = 0.25 # 250ms for next 3 seconds
|
|
1018
|
-
else:
|
|
1019
|
-
poll_interval = 0.5 # 500ms after 5 seconds
|
|
1020
|
-
|
|
1021
|
-
time.sleep(poll_interval)
|
|
1022
|
-
|
|
1023
|
-
# Check if the daemon server is accepting connections
|
|
1024
|
-
if _check_socketio_server_running(port, logger):
|
|
1025
|
-
logger.info(
|
|
1026
|
-
f"✅ Standalone Socket.IO server started successfully on port {port}"
|
|
1027
|
-
)
|
|
1028
|
-
logger.info(f"🕐 Server ready after {attempt} attempts ({elapsed:.1f}s)")
|
|
1029
|
-
return True
|
|
1030
|
-
else:
|
|
1031
|
-
logger.debug(
|
|
1032
|
-
f"Server not yet accepting connections on attempt {attempt}"
|
|
1033
|
-
)
|
|
1034
|
-
|
|
1035
|
-
# Timeout reached
|
|
1036
|
-
elapsed_total = time.time() - start_time
|
|
1037
|
-
logger.error(
|
|
1038
|
-
f"❌ Socket.IO server health check failed after {max_wait_time}s timeout ({attempt} attempts)"
|
|
1039
|
-
)
|
|
1040
|
-
logger.warning(
|
|
1041
|
-
f"⏱️ Server may still be starting - try waiting a few more seconds"
|
|
1042
|
-
)
|
|
1043
|
-
logger.warning(
|
|
1044
|
-
f"💡 The daemon process might be running but not yet accepting HTTP connections"
|
|
1045
|
-
)
|
|
1046
|
-
logger.error(f"🔧 Troubleshooting steps:")
|
|
1047
|
-
logger.error(f" - Wait a few more seconds and try again")
|
|
1048
|
-
logger.error(f" - Check for port conflicts: lsof -i :{port}")
|
|
1049
|
-
logger.error(f" - Try a different port with --websocket-port")
|
|
1050
|
-
logger.error(f" - Verify dependencies: pip install python-socketio aiohttp")
|
|
1051
|
-
return False
|
|
1052
|
-
|
|
1053
|
-
except Exception as e:
|
|
1054
|
-
logger.error(f"❌ Failed to start standalone Socket.IO server: {e}")
|
|
1055
|
-
import traceback
|
|
1056
|
-
|
|
1057
|
-
logger.error(f"📋 Stack trace: {traceback.format_exc()}")
|
|
1058
|
-
logger.error(
|
|
1059
|
-
f"💡 This may be a dependency issue - try: pip install python-socketio aiohttp"
|
|
1060
|
-
)
|
|
1061
|
-
return False
|
|
991
|
+
"""Start a standalone Socket.IO server (legacy compatibility)."""
|
|
992
|
+
socketio_manager = SocketIOManager(logger)
|
|
993
|
+
success, _ = socketio_manager.start_server(port=port)
|
|
994
|
+
return success
|
|
1062
995
|
|
|
1063
996
|
|
|
1064
997
|
def open_in_browser_tab(url, logger):
|
|
1065
998
|
"""Open URL in browser, attempting to reuse existing tabs when possible."""
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
monitor = SocketIOMonitor(logger)
|
|
1069
|
-
return monitor.open_in_browser_tab(url)
|
|
1070
|
-
"""
|
|
1071
|
-
Open URL in browser, attempting to reuse existing tabs when possible.
|
|
1072
|
-
|
|
1073
|
-
WHY: Users prefer reusing browser tabs instead of opening new ones constantly.
|
|
1074
|
-
This function attempts platform-specific solutions for tab reuse.
|
|
1075
|
-
|
|
1076
|
-
DESIGN DECISION: We try different methods based on platform capabilities,
|
|
1077
|
-
falling back to standard webbrowser.open() if needed.
|
|
1078
|
-
|
|
1079
|
-
Args:
|
|
1080
|
-
url: URL to open
|
|
1081
|
-
logger: Logger instance for output
|
|
1082
|
-
"""
|
|
1083
|
-
try:
|
|
1084
|
-
# Platform-specific optimizations for tab reuse
|
|
1085
|
-
import platform
|
|
1086
|
-
|
|
1087
|
-
system = platform.system().lower()
|
|
1088
|
-
|
|
1089
|
-
if system == "darwin": # macOS
|
|
1090
|
-
# Just use the standard webbrowser module on macOS
|
|
1091
|
-
# The AppleScript approach is too unreliable
|
|
1092
|
-
webbrowser.open(url, new=0, autoraise=True) # new=0 tries to reuse window
|
|
1093
|
-
logger.info("Opened browser on macOS")
|
|
1094
|
-
|
|
1095
|
-
elif system == "linux":
|
|
1096
|
-
# On Linux, try to use existing browser session
|
|
1097
|
-
try:
|
|
1098
|
-
# This is a best-effort approach for common browsers
|
|
1099
|
-
webbrowser.get().open(
|
|
1100
|
-
url, new=0
|
|
1101
|
-
) # new=0 tries to reuse existing window
|
|
1102
|
-
logger.info("Attempted Linux browser tab reuse")
|
|
1103
|
-
except Exception:
|
|
1104
|
-
webbrowser.open(url, autoraise=True)
|
|
1105
|
-
|
|
1106
|
-
elif system == "windows":
|
|
1107
|
-
# On Windows, try to use existing browser
|
|
1108
|
-
try:
|
|
1109
|
-
webbrowser.get().open(
|
|
1110
|
-
url, new=0
|
|
1111
|
-
) # new=0 tries to reuse existing window
|
|
1112
|
-
logger.info("Attempted Windows browser tab reuse")
|
|
1113
|
-
except Exception:
|
|
1114
|
-
webbrowser.open(url, autoraise=True)
|
|
1115
|
-
else:
|
|
1116
|
-
# Unknown platform, use standard opening
|
|
1117
|
-
webbrowser.open(url, autoraise=True)
|
|
1118
|
-
|
|
1119
|
-
except Exception as e:
|
|
1120
|
-
logger.warning(f"Browser opening failed: {e}")
|
|
1121
|
-
# Final fallback
|
|
1122
|
-
webbrowser.open(url)
|
|
999
|
+
launcher = DashboardLauncher(logger)
|
|
1000
|
+
return launcher._open_browser(url)
|
|
1123
1001
|
|
|
1124
1002
|
|
|
1125
1003
|
def _check_claude_json_memory(args, logger):
|
|
1126
1004
|
"""Check .claude.json file size and warn about memory issues."""
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
checker = RunConfigChecker(logger)
|
|
1130
|
-
checker.check_claude_json_memory(args)
|
|
1131
|
-
"""Check .claude.json file size and warn about memory issues.
|
|
1005
|
+
# Use new StartupCheckerService
|
|
1006
|
+
from ...core.config import Config
|
|
1132
1007
|
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
- Suggest cleanup command for remediation
|
|
1140
|
-
- Allow bypass with --force flag
|
|
1141
|
-
- Only check when using --resume
|
|
1142
|
-
|
|
1143
|
-
Args:
|
|
1144
|
-
args: Parsed command line arguments
|
|
1145
|
-
logger: Logger instance for output
|
|
1146
|
-
"""
|
|
1147
|
-
# Only check if using --mpm-resume
|
|
1148
|
-
if not hasattr(args, "mpm_resume") or not args.mpm_resume:
|
|
1149
|
-
return
|
|
1150
|
-
|
|
1151
|
-
claude_json_path = Path.home() / ".claude.json"
|
|
1152
|
-
|
|
1153
|
-
# Check if file exists
|
|
1154
|
-
if not claude_json_path.exists():
|
|
1155
|
-
logger.debug("No .claude.json file found")
|
|
1156
|
-
return
|
|
1157
|
-
|
|
1158
|
-
# Check file size
|
|
1159
|
-
file_size = claude_json_path.stat().st_size
|
|
1160
|
-
|
|
1161
|
-
# Format size for display
|
|
1162
|
-
def format_size(size_bytes):
|
|
1163
|
-
for unit in ["B", "KB", "MB", "GB"]:
|
|
1164
|
-
if size_bytes < 1024.0:
|
|
1165
|
-
return f"{size_bytes:.1f}{unit}"
|
|
1166
|
-
size_bytes /= 1024.0
|
|
1167
|
-
return f"{size_bytes:.1f}TB"
|
|
1168
|
-
|
|
1169
|
-
# Get thresholds from configuration
|
|
1170
|
-
try:
|
|
1171
|
-
config_loader = ConfigLoader()
|
|
1172
|
-
config = config_loader.load_main_config()
|
|
1173
|
-
memory_config = config.get("memory_management", {})
|
|
1174
|
-
warning_threshold = (
|
|
1175
|
-
memory_config.get("claude_json_warning_threshold_kb", 500) * 1024
|
|
1176
|
-
)
|
|
1177
|
-
critical_threshold = (
|
|
1178
|
-
memory_config.get("claude_json_critical_threshold_kb", 1024) * 1024
|
|
1179
|
-
)
|
|
1180
|
-
except Exception as e:
|
|
1181
|
-
logger.debug(f"Could not load memory configuration: {e}")
|
|
1182
|
-
# Fall back to defaults
|
|
1183
|
-
warning_threshold = 500 * 1024 # 500KB
|
|
1184
|
-
critical_threshold = 1024 * 1024 # 1MB
|
|
1185
|
-
|
|
1186
|
-
if file_size > critical_threshold:
|
|
1187
|
-
print(
|
|
1188
|
-
f"\n⚠️ CRITICAL: Large .claude.json file detected ({format_size(file_size)})"
|
|
1189
|
-
)
|
|
1190
|
-
print(f" This WILL cause memory issues when using --resume")
|
|
1191
|
-
print(f" Claude Code may consume 2GB+ of memory\n")
|
|
1192
|
-
|
|
1193
|
-
if not getattr(args, "force", False):
|
|
1194
|
-
print(" Recommended actions:")
|
|
1195
|
-
print(" 1. Run 'claude-mpm cleanup-memory' to archive old conversations")
|
|
1196
|
-
print(" 2. Use --force to bypass this warning (not recommended)")
|
|
1197
|
-
sys.stdout.flush() # Ensure prompt is displayed before input
|
|
1198
|
-
|
|
1199
|
-
# Check if we're in a TTY environment for proper input handling
|
|
1200
|
-
if not sys.stdin.isatty():
|
|
1201
|
-
# In non-TTY environment (like pipes), use readline
|
|
1202
|
-
print(
|
|
1203
|
-
"\n Would you like to continue anyway? [y/N]: ",
|
|
1204
|
-
end="",
|
|
1205
|
-
flush=True,
|
|
1206
|
-
)
|
|
1207
|
-
try:
|
|
1208
|
-
response = sys.stdin.readline().strip().lower()
|
|
1209
|
-
# Handle various line endings and control characters
|
|
1210
|
-
response = response.replace("\r", "").replace("\n", "").strip()
|
|
1211
|
-
except (EOFError, KeyboardInterrupt):
|
|
1212
|
-
response = "n"
|
|
1213
|
-
else:
|
|
1214
|
-
# In TTY environment, use normal input()
|
|
1215
|
-
print(
|
|
1216
|
-
"\n Would you like to continue anyway? [y/N]: ",
|
|
1217
|
-
end="",
|
|
1218
|
-
flush=True,
|
|
1219
|
-
)
|
|
1220
|
-
try:
|
|
1221
|
-
response = input().strip().lower()
|
|
1222
|
-
except (EOFError, KeyboardInterrupt):
|
|
1223
|
-
response = "n"
|
|
1224
|
-
|
|
1225
|
-
if response != "y":
|
|
1226
|
-
print(
|
|
1227
|
-
"\n✅ Session cancelled. Run 'claude-mpm cleanup-memory' to fix this issue."
|
|
1228
|
-
)
|
|
1229
|
-
sys.exit(0)
|
|
1230
|
-
|
|
1231
|
-
elif file_size > warning_threshold:
|
|
1232
|
-
print(
|
|
1233
|
-
f"\n⚠️ Warning: .claude.json file is getting large ({format_size(file_size)})"
|
|
1234
|
-
)
|
|
1235
|
-
print(" This may cause memory issues when using --resume")
|
|
1236
|
-
print(
|
|
1237
|
-
" 💡 Consider running 'claude-mpm cleanup-memory' to archive old conversations\n"
|
|
1238
|
-
)
|
|
1239
|
-
# Just warn, don't block execution
|
|
1240
|
-
|
|
1241
|
-
logger.info(f".claude.json size: {format_size(file_size)}")
|
|
1008
|
+
config_service = Config()
|
|
1009
|
+
checker = StartupCheckerService(config_service)
|
|
1010
|
+
resume_enabled = getattr(args, "mpm_resume", False)
|
|
1011
|
+
warning = checker.check_memory(resume_enabled)
|
|
1012
|
+
if warning:
|
|
1013
|
+
checker.display_warnings([warning])
|
|
1242
1014
|
|
|
1243
1015
|
|
|
1244
1016
|
def _check_configuration_health(logger):
|
|
1245
1017
|
"""Check configuration health at startup and warn about issues."""
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
checker = RunConfigChecker(logger)
|
|
1249
|
-
checker.check_configuration_health()
|
|
1250
|
-
"""Check configuration health at startup and warn about issues.
|
|
1251
|
-
|
|
1252
|
-
WHY: Configuration errors can cause silent failures, especially for response
|
|
1253
|
-
logging. This function proactively checks configuration at startup and warns
|
|
1254
|
-
users about any issues, providing actionable guidance.
|
|
1255
|
-
|
|
1256
|
-
DESIGN DECISIONS:
|
|
1257
|
-
- Non-blocking: Issues are logged as warnings, not errors
|
|
1258
|
-
- Actionable: Provides specific commands to fix issues
|
|
1259
|
-
- Focused: Only checks critical configuration that affects runtime
|
|
1260
|
-
|
|
1261
|
-
Args:
|
|
1262
|
-
logger: Logger instance for output
|
|
1263
|
-
"""
|
|
1264
|
-
try:
|
|
1265
|
-
# Load configuration using ConfigLoader
|
|
1266
|
-
config_loader = ConfigLoader()
|
|
1267
|
-
config = config_loader.load_main_config()
|
|
1268
|
-
|
|
1269
|
-
# Validate configuration
|
|
1270
|
-
is_valid, errors, warnings = config.validate_configuration()
|
|
1271
|
-
|
|
1272
|
-
# Get configuration status for additional context
|
|
1273
|
-
status = config.get_configuration_status()
|
|
1274
|
-
|
|
1275
|
-
# Report critical errors that will affect functionality
|
|
1276
|
-
if errors:
|
|
1277
|
-
logger.warning("⚠️ Configuration issues detected:")
|
|
1278
|
-
for error in errors[:3]: # Show first 3 errors
|
|
1279
|
-
logger.warning(f" • {error}")
|
|
1280
|
-
if len(errors) > 3:
|
|
1281
|
-
logger.warning(f" • ... and {len(errors) - 3} more")
|
|
1282
|
-
logger.info(
|
|
1283
|
-
"💡 Run 'claude-mpm config validate' to see all issues and fixes"
|
|
1284
|
-
)
|
|
1018
|
+
# Use new StartupCheckerService
|
|
1019
|
+
from ...core.config import Config
|
|
1285
1020
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
"Response logging is disabled (response_logging.enabled=false)"
|
|
1291
|
-
)
|
|
1292
|
-
else:
|
|
1293
|
-
# Check if session directory is writable
|
|
1294
|
-
session_dir = Path(
|
|
1295
|
-
config.get(
|
|
1296
|
-
"response_logging.session_directory", ".claude-mpm/responses"
|
|
1297
|
-
)
|
|
1298
|
-
)
|
|
1299
|
-
if not session_dir.is_absolute():
|
|
1300
|
-
session_dir = Path.cwd() / session_dir
|
|
1301
|
-
|
|
1302
|
-
if not session_dir.exists():
|
|
1303
|
-
try:
|
|
1304
|
-
session_dir.mkdir(parents=True, exist_ok=True)
|
|
1305
|
-
logger.debug(f"Created response logging directory: {session_dir}")
|
|
1306
|
-
except Exception as e:
|
|
1307
|
-
logger.warning(
|
|
1308
|
-
f"Cannot create response logging directory {session_dir}: {e}"
|
|
1309
|
-
)
|
|
1310
|
-
logger.info("💡 Fix with: mkdir -p " + str(session_dir))
|
|
1311
|
-
elif not os.access(session_dir, os.W_OK):
|
|
1312
|
-
logger.warning(
|
|
1313
|
-
f"Response logging directory is not writable: {session_dir}"
|
|
1314
|
-
)
|
|
1315
|
-
logger.info("💡 Fix with: chmod 755 " + str(session_dir))
|
|
1316
|
-
|
|
1317
|
-
# Report non-critical warnings (only in debug mode)
|
|
1318
|
-
if warnings and logger.isEnabledFor(logging.DEBUG):
|
|
1319
|
-
logger.debug("Configuration warnings:")
|
|
1320
|
-
for warning in warnings:
|
|
1321
|
-
logger.debug(f" • {warning}")
|
|
1322
|
-
|
|
1323
|
-
# Log loaded configuration source for debugging
|
|
1324
|
-
if status.get("loaded_from") and status["loaded_from"] != "defaults":
|
|
1325
|
-
logger.debug(f"Configuration loaded from: {status['loaded_from']}")
|
|
1326
|
-
|
|
1327
|
-
except Exception as e:
|
|
1328
|
-
# Don't let configuration check errors prevent startup
|
|
1329
|
-
logger.debug(f"Configuration check failed (non-critical): {e}")
|
|
1330
|
-
# Only show user-facing message if it's likely to affect them
|
|
1331
|
-
if "yaml" in str(e).lower():
|
|
1332
|
-
logger.warning("⚠️ Configuration file may have YAML syntax errors")
|
|
1333
|
-
logger.info("💡 Validate with: claude-mpm config validate")
|
|
1021
|
+
config_service = Config()
|
|
1022
|
+
checker = StartupCheckerService(config_service)
|
|
1023
|
+
warnings = checker.check_configuration()
|
|
1024
|
+
checker.display_warnings(warnings)
|