claude-mpm 3.9.9__py3-none-any.whl → 4.0.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/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +155 -0
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +90 -49
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +21 -18
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +143 -762
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1150
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +36 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +571 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +40 -23
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +14 -21
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +97 -93
- claude_mpm/services/mcp_gateway/main.py +307 -127
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
- claude_mpm/services/mcp_gateway/server/{mcp_server.py → mcp_gateway.py} +149 -153
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +110 -121
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +20 -534
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +552 -0
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +51 -2
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/memory_guardian.py +0 -770
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.9.dist-info/RECORD +0 -293
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -0,0 +1,237 @@ | |
| 1 | 
            +
            """Memory hook service for registering memory-related hooks.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            This service handles:
         | 
| 4 | 
            +
            1. Memory hook registration with the hook service
         | 
| 5 | 
            +
            2. Memory management integration
         | 
| 6 | 
            +
            3. Hook lifecycle management
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            Extracted from ClaudeRunner to follow Single Responsibility Principle.
         | 
| 9 | 
            +
            """
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            from typing import Any, Dict
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            from claude_mpm.core.base_service import BaseService
         | 
| 14 | 
            +
            from claude_mpm.services.core.interfaces import MemoryHookInterface
         | 
| 15 | 
            +
             | 
| 16 | 
            +
             | 
| 17 | 
            +
            class MemoryHookService(BaseService, MemoryHookInterface):
         | 
| 18 | 
            +
                """Service for managing memory-related hooks."""
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def __init__(self, hook_service=None):
         | 
| 21 | 
            +
                    """Initialize the memory hook service.
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    Args:
         | 
| 24 | 
            +
                        hook_service: Hook service for registering hooks
         | 
| 25 | 
            +
                    """
         | 
| 26 | 
            +
                    super().__init__(name="memory_hook_service")
         | 
| 27 | 
            +
                    self.hook_service = hook_service
         | 
| 28 | 
            +
                    self.registered_hooks = []  # Track registered hook IDs
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                async def _initialize(self) -> None:
         | 
| 31 | 
            +
                    """Initialize the service. No special initialization needed."""
         | 
| 32 | 
            +
                    pass
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                async def _cleanup(self) -> None:
         | 
| 35 | 
            +
                    """Cleanup service resources. No cleanup needed."""
         | 
| 36 | 
            +
                    pass
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def register_memory_hooks(self):
         | 
| 39 | 
            +
                    """Register memory-related hooks with the hook service.
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    WHY: Memory management is a cross-cutting concern that needs to be
         | 
| 42 | 
            +
                    integrated at various points in the Claude interaction lifecycle.
         | 
| 43 | 
            +
                    These hooks ensure memory is properly managed and persisted.
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    DESIGN DECISION: We register hooks for key lifecycle events:
         | 
| 46 | 
            +
                    - Before Claude interaction: Load relevant memories
         | 
| 47 | 
            +
                    - After Claude interaction: Save new memories
         | 
| 48 | 
            +
                    - On error: Ensure memory state is preserved
         | 
| 49 | 
            +
                    """
         | 
| 50 | 
            +
                    if not self.hook_service:
         | 
| 51 | 
            +
                        self.logger.debug(
         | 
| 52 | 
            +
                            "Hook service not available, skipping memory hook registration"
         | 
| 53 | 
            +
                        )
         | 
| 54 | 
            +
                        return
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    try:
         | 
| 57 | 
            +
                        # Create hook objects for the actual HookService interface
         | 
| 58 | 
            +
                        from claude_mpm.hooks.base_hook import (
         | 
| 59 | 
            +
                            HookContext,
         | 
| 60 | 
            +
                            HookResult,
         | 
| 61 | 
            +
                            PostDelegationHook,
         | 
| 62 | 
            +
                            PreDelegationHook,
         | 
| 63 | 
            +
                        )
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                        # Create memory loading hook
         | 
| 66 | 
            +
                        class MemoryLoadHook(PreDelegationHook):
         | 
| 67 | 
            +
                            def __init__(self, memory_service):
         | 
| 68 | 
            +
                                super().__init__(name="memory_load", priority=10)
         | 
| 69 | 
            +
                                self.memory_service = memory_service
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                            def execute(self, context: HookContext) -> HookResult:
         | 
| 72 | 
            +
                                return self.memory_service._load_relevant_memories_hook(context)
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                        # Create memory saving hook
         | 
| 75 | 
            +
                        class MemorySaveHook(PostDelegationHook):
         | 
| 76 | 
            +
                            def __init__(self, memory_service):
         | 
| 77 | 
            +
                                super().__init__(name="memory_save", priority=90)
         | 
| 78 | 
            +
                                self.memory_service = memory_service
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                            def execute(self, context: HookContext) -> HookResult:
         | 
| 81 | 
            +
                                return self.memory_service._save_new_memories_hook(context)
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                        # Register the hook objects
         | 
| 84 | 
            +
                        load_hook = MemoryLoadHook(self)
         | 
| 85 | 
            +
                        save_hook = MemorySaveHook(self)
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                        success1 = self.hook_service.register_hook(load_hook)
         | 
| 88 | 
            +
                        success2 = self.hook_service.register_hook(save_hook)
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                        if success1:
         | 
| 91 | 
            +
                            self.registered_hooks.append("memory_load")
         | 
| 92 | 
            +
                        if success2:
         | 
| 93 | 
            +
                            self.registered_hooks.append("memory_save")
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                        self.logger.debug("Memory hooks registered successfully")
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    except Exception as e:
         | 
| 98 | 
            +
                        self.logger.warning(f"Failed to register memory hooks: {e}")
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def _load_relevant_memories_hook(self, context):
         | 
| 101 | 
            +
                    """Hook function to load relevant memories before Claude interaction.
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    Args:
         | 
| 104 | 
            +
                        context: Hook context containing interaction details
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    Returns:
         | 
| 107 | 
            +
                        HookResult with success status and any modifications
         | 
| 108 | 
            +
                    """
         | 
| 109 | 
            +
                    try:
         | 
| 110 | 
            +
                        # This would integrate with a memory service to load relevant memories
         | 
| 111 | 
            +
                        # For now, this is a placeholder for future memory integration
         | 
| 112 | 
            +
                        self.logger.debug("Loading relevant memories for interaction")
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                        # Example: Load memories based on context
         | 
| 115 | 
            +
                        # if hasattr(context, 'prompt'):
         | 
| 116 | 
            +
                        #     relevant_memories = memory_service.search_memories(context.prompt)
         | 
| 117 | 
            +
                        #     context.memories = relevant_memories
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                        from claude_mpm.hooks.base_hook import HookResult
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                        return HookResult(success=True, data=context.data, modified=False)
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                    except Exception as e:
         | 
| 124 | 
            +
                        self.logger.warning(f"Failed to load memories: {e}")
         | 
| 125 | 
            +
                        from claude_mpm.hooks.base_hook import HookResult
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                        return HookResult(
         | 
| 128 | 
            +
                            success=False, data=context.data, modified=False, error=str(e)
         | 
| 129 | 
            +
                        )
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                def _load_relevant_memories(self, context):
         | 
| 132 | 
            +
                    """Legacy hook function for backward compatibility."""
         | 
| 133 | 
            +
                    result = self._load_relevant_memories_hook(context)
         | 
| 134 | 
            +
                    return result.data
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                def _save_new_memories_hook(self, context):
         | 
| 137 | 
            +
                    """Hook function to save new memories after Claude interaction.
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                    Args:
         | 
| 140 | 
            +
                        context: Hook context containing interaction results
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                    Returns:
         | 
| 143 | 
            +
                        HookResult with success status and any modifications
         | 
| 144 | 
            +
                    """
         | 
| 145 | 
            +
                    try:
         | 
| 146 | 
            +
                        # This would integrate with a memory service to save new memories
         | 
| 147 | 
            +
                        # For now, this is a placeholder for future memory integration
         | 
| 148 | 
            +
                        self.logger.debug("Saving new memories from interaction")
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                        from claude_mpm.hooks.base_hook import HookResult
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                        return HookResult(success=True, data=context.data, modified=False)
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                    except Exception as e:
         | 
| 155 | 
            +
                        self.logger.warning(f"Failed to save memories: {e}")
         | 
| 156 | 
            +
                        from claude_mpm.hooks.base_hook import HookResult
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                        return HookResult(
         | 
| 159 | 
            +
                            success=False, data=context.data, modified=False, error=str(e)
         | 
| 160 | 
            +
                        )
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                def _save_new_memories(self, context):
         | 
| 163 | 
            +
                    """Legacy hook function for backward compatibility."""
         | 
| 164 | 
            +
                    result = self._save_new_memories_hook(context)
         | 
| 165 | 
            +
                    return result.data
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                def _preserve_memory_state(self, context):
         | 
| 168 | 
            +
                    """Hook function to preserve memory state on interaction error.
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                    Args:
         | 
| 171 | 
            +
                        context: Hook context containing error details
         | 
| 172 | 
            +
                    """
         | 
| 173 | 
            +
                    try:
         | 
| 174 | 
            +
                        # This would ensure memory state is preserved even if interaction fails
         | 
| 175 | 
            +
                        self.logger.debug("Preserving memory state after error")
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                    except Exception as e:
         | 
| 178 | 
            +
                        self.logger.warning(f"Failed to preserve memory state: {e}")
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                def unregister_memory_hooks(self):
         | 
| 181 | 
            +
                    """Unregister memory-related hooks from the hook service."""
         | 
| 182 | 
            +
                    if not self.hook_service:
         | 
| 183 | 
            +
                        return
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                    try:
         | 
| 186 | 
            +
                        self.hook_service.unregister_hook(
         | 
| 187 | 
            +
                            "before_claude_interaction", self._load_relevant_memories
         | 
| 188 | 
            +
                        )
         | 
| 189 | 
            +
                        self.hook_service.unregister_hook(
         | 
| 190 | 
            +
                            "after_claude_interaction", self._save_new_memories
         | 
| 191 | 
            +
                        )
         | 
| 192 | 
            +
                        self.hook_service.unregister_hook(
         | 
| 193 | 
            +
                            "on_interaction_error", self._preserve_memory_state
         | 
| 194 | 
            +
                        )
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                        self.logger.debug("Memory hooks unregistered successfully")
         | 
| 197 | 
            +
             | 
| 198 | 
            +
                    except Exception as e:
         | 
| 199 | 
            +
                        self.logger.warning(f"Failed to unregister memory hooks: {e}")
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                def is_memory_enabled(self) -> bool:
         | 
| 202 | 
            +
                    """Check if memory functionality is enabled.
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                    Returns:
         | 
| 205 | 
            +
                        bool: True if memory is enabled and available
         | 
| 206 | 
            +
                    """
         | 
| 207 | 
            +
                    # This would check if memory service is available and configured
         | 
| 208 | 
            +
                    # For now, return False as memory is not yet implemented
         | 
| 209 | 
            +
                    return False
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                def get_memory_status(self) -> dict:
         | 
| 212 | 
            +
                    """Get current memory system status.
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                    Returns:
         | 
| 215 | 
            +
                        dict: Memory system status information
         | 
| 216 | 
            +
                    """
         | 
| 217 | 
            +
                    return {
         | 
| 218 | 
            +
                        "enabled": self.is_memory_enabled(),
         | 
| 219 | 
            +
                        "hooks_registered": self.hook_service is not None,
         | 
| 220 | 
            +
                        "service_available": True,
         | 
| 221 | 
            +
                    }
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                def get_hook_status(self) -> Dict[str, Any]:
         | 
| 224 | 
            +
                    """Get status of registered memory hooks.
         | 
| 225 | 
            +
             | 
| 226 | 
            +
                    Returns:
         | 
| 227 | 
            +
                        Dictionary with hook status information
         | 
| 228 | 
            +
                    """
         | 
| 229 | 
            +
                    return {
         | 
| 230 | 
            +
                        "registered_hooks": self.registered_hooks,
         | 
| 231 | 
            +
                        "hook_service_available": self.hook_service is not None,
         | 
| 232 | 
            +
                        "memory_enabled": self.is_memory_enabled(),
         | 
| 233 | 
            +
                        "total_hooks": len(self.registered_hooks),
         | 
| 234 | 
            +
                        "status": "active" if self.registered_hooks else "inactive",
         | 
| 235 | 
            +
                    }
         | 
| 236 | 
            +
             | 
| 237 | 
            +
             | 
| @@ -0,0 +1,223 @@ | |
| 1 | 
            +
            #!/usr/bin/env python3
         | 
| 2 | 
            +
            """
         | 
| 3 | 
            +
            Port Manager for SocketIO Server
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Handles dynamic port selection, instance detection, and port availability checking.
         | 
| 6 | 
            +
            Ensures only one instance runs per port and provides fallback port selection.
         | 
| 7 | 
            +
            """
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            import json
         | 
| 10 | 
            +
            import os
         | 
| 11 | 
            +
            import socket
         | 
| 12 | 
            +
            import subprocess
         | 
| 13 | 
            +
            import time
         | 
| 14 | 
            +
            from pathlib import Path
         | 
| 15 | 
            +
            from typing import Dict, List, Optional, Tuple
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            import psutil
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            from ..core.logging_config import get_logger
         | 
| 20 | 
            +
             | 
| 21 | 
            +
             | 
| 22 | 
            +
            class PortManager:
         | 
| 23 | 
            +
                """Manages port allocation and instance detection for SocketIO servers."""
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                # Port range for SocketIO servers
         | 
| 26 | 
            +
                PORT_RANGE = range(8765, 8786)  # 8765-8785 (21 ports)
         | 
| 27 | 
            +
                DEFAULT_PORT = 8765
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def __init__(self, project_root: Optional[Path] = None):
         | 
| 30 | 
            +
                    self.logger = get_logger(__name__ + ".PortManager")
         | 
| 31 | 
            +
                    self.project_root = project_root or Path.cwd()
         | 
| 32 | 
            +
                    self.state_dir = self.project_root / ".claude-mpm"
         | 
| 33 | 
            +
                    self.state_dir.mkdir(exist_ok=True)
         | 
| 34 | 
            +
                    self.instances_file = self.state_dir / "socketio-instances.json"
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def is_port_available(self, port: int) -> bool:
         | 
| 37 | 
            +
                    """Check if a port is available for binding."""
         | 
| 38 | 
            +
                    try:
         | 
| 39 | 
            +
                        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
         | 
| 40 | 
            +
                            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         | 
| 41 | 
            +
                            result = sock.bind(("localhost", port))
         | 
| 42 | 
            +
                            return True
         | 
| 43 | 
            +
                    except OSError:
         | 
| 44 | 
            +
                        return False
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def is_claude_mpm_instance(self, port: int) -> Tuple[bool, Optional[Dict]]:
         | 
| 47 | 
            +
                    """Check if a port is being used by a claude-mpm SocketIO instance."""
         | 
| 48 | 
            +
                    instances = self.load_instances()
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    for instance_id, instance_info in instances.items():
         | 
| 51 | 
            +
                        if instance_info.get("port") == port:
         | 
| 52 | 
            +
                            # Check if the process is still running
         | 
| 53 | 
            +
                            pid = instance_info.get("pid")
         | 
| 54 | 
            +
                            if pid and self.is_process_running(pid):
         | 
| 55 | 
            +
                                # Verify it's actually our process
         | 
| 56 | 
            +
                                if self.is_our_socketio_process(pid):
         | 
| 57 | 
            +
                                    return True, instance_info
         | 
| 58 | 
            +
                            else:
         | 
| 59 | 
            +
                                # Process is dead, clean up the instance
         | 
| 60 | 
            +
                                self.remove_instance(instance_id)
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    return False, None
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def is_process_running(self, pid: int) -> bool:
         | 
| 65 | 
            +
                    """Check if a process with given PID is running."""
         | 
| 66 | 
            +
                    try:
         | 
| 67 | 
            +
                        return psutil.pid_exists(pid)
         | 
| 68 | 
            +
                    except Exception:
         | 
| 69 | 
            +
                        return False
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def is_our_socketio_process(self, pid: int) -> bool:
         | 
| 72 | 
            +
                    """Verify that a PID belongs to our SocketIO server."""
         | 
| 73 | 
            +
                    try:
         | 
| 74 | 
            +
                        process = psutil.Process(pid)
         | 
| 75 | 
            +
                        cmdline = " ".join(process.cmdline())
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                        # Check if it's a Python process running our SocketIO daemon
         | 
| 78 | 
            +
                        return "python" in cmdline.lower() and (
         | 
| 79 | 
            +
                            "socketio_daemon" in cmdline or "claude-mpm" in cmdline
         | 
| 80 | 
            +
                        )
         | 
| 81 | 
            +
                    except (psutil.NoSuchProcess, psutil.AccessDenied):
         | 
| 82 | 
            +
                        return False
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                def find_available_port(
         | 
| 85 | 
            +
                    self, preferred_port: Optional[int] = None
         | 
| 86 | 
            +
                ) -> Optional[int]:
         | 
| 87 | 
            +
                    """Find an available port, preferring the specified port if given."""
         | 
| 88 | 
            +
                    # Try preferred port first
         | 
| 89 | 
            +
                    if preferred_port and preferred_port in self.PORT_RANGE:
         | 
| 90 | 
            +
                        if self.is_port_available(preferred_port):
         | 
| 91 | 
            +
                            is_ours, instance_info = self.is_claude_mpm_instance(preferred_port)
         | 
| 92 | 
            +
                            if not is_ours:
         | 
| 93 | 
            +
                                return preferred_port
         | 
| 94 | 
            +
                            else:
         | 
| 95 | 
            +
                                self.logger.warning(
         | 
| 96 | 
            +
                                    f"Port {preferred_port} is already used by claude-mpm instance: {instance_info}"
         | 
| 97 | 
            +
                                )
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                    # Try default port
         | 
| 100 | 
            +
                    if self.is_port_available(self.DEFAULT_PORT):
         | 
| 101 | 
            +
                        is_ours, instance_info = self.is_claude_mpm_instance(self.DEFAULT_PORT)
         | 
| 102 | 
            +
                        if not is_ours:
         | 
| 103 | 
            +
                            return self.DEFAULT_PORT
         | 
| 104 | 
            +
                        else:
         | 
| 105 | 
            +
                            self.logger.info(
         | 
| 106 | 
            +
                                f"Default port {self.DEFAULT_PORT} is already used by claude-mpm instance"
         | 
| 107 | 
            +
                            )
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    # Try other ports in range
         | 
| 110 | 
            +
                    for port in self.PORT_RANGE:
         | 
| 111 | 
            +
                        if port == self.DEFAULT_PORT:
         | 
| 112 | 
            +
                            continue  # Already tried
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                        if self.is_port_available(port):
         | 
| 115 | 
            +
                            is_ours, instance_info = self.is_claude_mpm_instance(port)
         | 
| 116 | 
            +
                            if not is_ours:
         | 
| 117 | 
            +
                                self.logger.info(f"Selected available port: {port}")
         | 
| 118 | 
            +
                                return port
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                    self.logger.error(
         | 
| 121 | 
            +
                        f"No available ports in range {self.PORT_RANGE.start}-{self.PORT_RANGE.stop-1}"
         | 
| 122 | 
            +
                    )
         | 
| 123 | 
            +
                    return None
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                def register_instance(self, port: int, pid: int, host: str = "localhost") -> str:
         | 
| 126 | 
            +
                    """Register a new SocketIO server instance."""
         | 
| 127 | 
            +
                    instances = self.load_instances()
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    instance_id = f"socketio-{port}-{int(time.time())}"
         | 
| 130 | 
            +
                    instance_info = {
         | 
| 131 | 
            +
                        "port": port,
         | 
| 132 | 
            +
                        "pid": pid,
         | 
| 133 | 
            +
                        "host": host,
         | 
| 134 | 
            +
                        "start_time": time.time(),
         | 
| 135 | 
            +
                        "project_root": str(self.project_root),
         | 
| 136 | 
            +
                    }
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                    instances[instance_id] = instance_info
         | 
| 139 | 
            +
                    self.save_instances(instances)
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    self.logger.info(
         | 
| 142 | 
            +
                        f"Registered SocketIO instance {instance_id} on port {port} (PID: {pid})"
         | 
| 143 | 
            +
                    )
         | 
| 144 | 
            +
                    return instance_id
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                def remove_instance(self, instance_id: str) -> bool:
         | 
| 147 | 
            +
                    """Remove a SocketIO server instance registration."""
         | 
| 148 | 
            +
                    instances = self.load_instances()
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                    if instance_id in instances:
         | 
| 151 | 
            +
                        instance_info = instances.pop(instance_id)
         | 
| 152 | 
            +
                        self.save_instances(instances)
         | 
| 153 | 
            +
                        self.logger.info(
         | 
| 154 | 
            +
                            f"Removed SocketIO instance {instance_id} (port: {instance_info.get('port')})"
         | 
| 155 | 
            +
                        )
         | 
| 156 | 
            +
                        return True
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                    return False
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                def load_instances(self) -> Dict:
         | 
| 161 | 
            +
                    """Load registered instances from file."""
         | 
| 162 | 
            +
                    try:
         | 
| 163 | 
            +
                        if self.instances_file.exists():
         | 
| 164 | 
            +
                            with open(self.instances_file, "r") as f:
         | 
| 165 | 
            +
                                return json.load(f)
         | 
| 166 | 
            +
                    except Exception as e:
         | 
| 167 | 
            +
                        self.logger.warning(f"Failed to load instances file: {e}")
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                    return {}
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                def save_instances(self, instances: Dict) -> None:
         | 
| 172 | 
            +
                    """Save registered instances to file."""
         | 
| 173 | 
            +
                    try:
         | 
| 174 | 
            +
                        with open(self.instances_file, "w") as f:
         | 
| 175 | 
            +
                            json.dump(instances, f, indent=2)
         | 
| 176 | 
            +
                    except Exception as e:
         | 
| 177 | 
            +
                        self.logger.error(f"Failed to save instances file: {e}")
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                def cleanup_dead_instances(self) -> int:
         | 
| 180 | 
            +
                    """Clean up instances for processes that are no longer running."""
         | 
| 181 | 
            +
                    instances = self.load_instances()
         | 
| 182 | 
            +
                    dead_instances = []
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                    for instance_id, instance_info in instances.items():
         | 
| 185 | 
            +
                        pid = instance_info.get("pid")
         | 
| 186 | 
            +
                        if pid and not self.is_process_running(pid):
         | 
| 187 | 
            +
                            dead_instances.append(instance_id)
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                    for instance_id in dead_instances:
         | 
| 190 | 
            +
                        self.remove_instance(instance_id)
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                    if dead_instances:
         | 
| 193 | 
            +
                        self.logger.info(f"Cleaned up {len(dead_instances)} dead instances")
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                    return len(dead_instances)
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                def list_active_instances(self) -> List[Dict]:
         | 
| 198 | 
            +
                    """List all active SocketIO instances."""
         | 
| 199 | 
            +
                    instances = self.load_instances()
         | 
| 200 | 
            +
                    active_instances = []
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                    for instance_id, instance_info in instances.items():
         | 
| 203 | 
            +
                        pid = instance_info.get("pid")
         | 
| 204 | 
            +
                        if pid and self.is_process_running(pid):
         | 
| 205 | 
            +
                            instance_info["instance_id"] = instance_id
         | 
| 206 | 
            +
                            instance_info["running"] = True
         | 
| 207 | 
            +
                            active_instances.append(instance_info)
         | 
| 208 | 
            +
             | 
| 209 | 
            +
                    return active_instances
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                def get_instance_by_port(self, port: int) -> Optional[Dict]:
         | 
| 212 | 
            +
                    """Get instance information for a specific port."""
         | 
| 213 | 
            +
                    instances = self.load_instances()
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                    for instance_id, instance_info in instances.items():
         | 
| 216 | 
            +
                        if instance_info.get("port") == port:
         | 
| 217 | 
            +
                            pid = instance_info.get("pid")
         | 
| 218 | 
            +
                            if pid and self.is_process_running(pid):
         | 
| 219 | 
            +
                                instance_info["instance_id"] = instance_id
         | 
| 220 | 
            +
                                instance_info["running"] = True
         | 
| 221 | 
            +
                                return instance_info
         | 
| 222 | 
            +
             | 
| 223 | 
            +
                    return None
         |