claude-mpm 3.9.11__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 +1 -1
- 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 +79 -51
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +20 -20
- 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 +140 -905
- 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 -1156
- 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 +71 -73
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +35 -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 +233 -199
- 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 +30 -13
- 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 +13 -20
- 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 +87 -84
- claude_mpm/services/mcp_gateway/main.py +287 -137
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
- 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 +109 -119
- 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 +19 -533
- 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 +2 -2
- claude_mpm/storage/state_storage.py +177 -181
- 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.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/cli/commands/run_guarded.py +0 -511
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/config/memory_guardian_yaml.py +0 -335
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/core/memory_aware_runner.py +0 -353
- 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/graceful_degradation.py +0 -616
- claude_mpm/services/infrastructure/health_monitor.py +0 -775
- claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
- claude_mpm/services/infrastructure/memory_guardian.py +0 -944
- claude_mpm/services/infrastructure/restart_protection.py +0 -642
- claude_mpm/services/infrastructure/state_manager.py +0 -774
- claude_mpm/services/mcp_gateway/manager.py +0 -334
- 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.11.dist-info/RECORD +0 -306
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -0,0 +1,170 @@ | |
| 1 | 
            +
            """Hook event handlers for Socket.IO.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            WHY: This module handles hook events from Claude to track session information,
         | 
| 4 | 
            +
            agent delegations, and other hook-based activity for the system heartbeat.
         | 
| 5 | 
            +
            """
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            from datetime import datetime
         | 
| 8 | 
            +
            from typing import Any, Dict, Optional
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            from .base import BaseEventHandler
         | 
| 11 | 
            +
             | 
| 12 | 
            +
             | 
| 13 | 
            +
            class HookEventHandler(BaseEventHandler):
         | 
| 14 | 
            +
                """Handles hook events from Claude for session tracking.
         | 
| 15 | 
            +
                
         | 
| 16 | 
            +
                WHY: Hook events provide rich information about Claude's activity including
         | 
| 17 | 
            +
                session starts/stops, agent delegations, and tool usage. This handler
         | 
| 18 | 
            +
                extracts session information to populate the system heartbeat data.
         | 
| 19 | 
            +
                """
         | 
| 20 | 
            +
                
         | 
| 21 | 
            +
                def register_events(self) -> None:
         | 
| 22 | 
            +
                    """Register hook event handlers.
         | 
| 23 | 
            +
                    
         | 
| 24 | 
            +
                    NOTE: This handler no longer registers a claude_event handler directly
         | 
| 25 | 
            +
                    to avoid conflicts with ConnectionEventHandler. Instead, it processes
         | 
| 26 | 
            +
                    hook events passed from ConnectionEventHandler.
         | 
| 27 | 
            +
                    """
         | 
| 28 | 
            +
                    # No direct event registration - events are passed from ConnectionEventHandler
         | 
| 29 | 
            +
                    pass
         | 
| 30 | 
            +
                
         | 
| 31 | 
            +
                async def process_hook_event(self, data: Dict[str, Any]) -> None:
         | 
| 32 | 
            +
                    """Process a hook event received from ConnectionEventHandler.
         | 
| 33 | 
            +
                    
         | 
| 34 | 
            +
                    WHY: This method is called by ConnectionEventHandler when it receives
         | 
| 35 | 
            +
                    a claude_event with type 'hook'. This separation avoids handler conflicts.
         | 
| 36 | 
            +
                    
         | 
| 37 | 
            +
                    Args:
         | 
| 38 | 
            +
                        data: The complete event data including type, event, and data fields
         | 
| 39 | 
            +
                    """
         | 
| 40 | 
            +
                    if not isinstance(data, dict):
         | 
| 41 | 
            +
                        return
         | 
| 42 | 
            +
                        
         | 
| 43 | 
            +
                    # Extract hook event details
         | 
| 44 | 
            +
                    hook_event = data.get("event")
         | 
| 45 | 
            +
                    hook_data = data.get("data", {})
         | 
| 46 | 
            +
                    
         | 
| 47 | 
            +
                    # Add the event to history for replay
         | 
| 48 | 
            +
                    self.add_to_history("hook", {
         | 
| 49 | 
            +
                        "event": hook_event,
         | 
| 50 | 
            +
                        "data": hook_data,
         | 
| 51 | 
            +
                        "timestamp": datetime.now().isoformat()
         | 
| 52 | 
            +
                    })
         | 
| 53 | 
            +
                    
         | 
| 54 | 
            +
                    # Broadcast the event to all connected clients
         | 
| 55 | 
            +
                    await self.broadcast_event("claude_event", data)
         | 
| 56 | 
            +
                    
         | 
| 57 | 
            +
                    # Track sessions based on hook events
         | 
| 58 | 
            +
                    if hook_event == "subagent_start":
         | 
| 59 | 
            +
                        await self._handle_subagent_start(hook_data)
         | 
| 60 | 
            +
                    elif hook_event == "subagent_stop":
         | 
| 61 | 
            +
                        await self._handle_subagent_stop(hook_data)
         | 
| 62 | 
            +
                    elif hook_event == "user_prompt":
         | 
| 63 | 
            +
                        await self._handle_user_prompt(hook_data)
         | 
| 64 | 
            +
                    elif hook_event == "pre_tool":
         | 
| 65 | 
            +
                        await self._handle_pre_tool(hook_data)
         | 
| 66 | 
            +
                    
         | 
| 67 | 
            +
                    self.logger.debug(f"Processed hook event: {hook_event}")
         | 
| 68 | 
            +
                            
         | 
| 69 | 
            +
                async def _handle_subagent_start(self, data: Dict[str, Any]):
         | 
| 70 | 
            +
                    """Handle subagent start events.
         | 
| 71 | 
            +
                    
         | 
| 72 | 
            +
                    WHY: When a subagent starts, we track it as an active session
         | 
| 73 | 
            +
                    with the agent type and start time for the heartbeat.
         | 
| 74 | 
            +
                    """
         | 
| 75 | 
            +
                    session_id = data.get("session_id")
         | 
| 76 | 
            +
                    agent_type = data.get("agent_type", "unknown")
         | 
| 77 | 
            +
                    
         | 
| 78 | 
            +
                    if not session_id:
         | 
| 79 | 
            +
                        return
         | 
| 80 | 
            +
                        
         | 
| 81 | 
            +
                    # Update or create session tracking
         | 
| 82 | 
            +
                    if hasattr(self.server, 'active_sessions'):
         | 
| 83 | 
            +
                        self.server.active_sessions[session_id] = {
         | 
| 84 | 
            +
                            "session_id": session_id,
         | 
| 85 | 
            +
                            "start_time": datetime.now().isoformat(),
         | 
| 86 | 
            +
                            "agent": agent_type,
         | 
| 87 | 
            +
                            "status": "active",
         | 
| 88 | 
            +
                            "prompt": data.get("prompt", "")[:100],  # First 100 chars
         | 
| 89 | 
            +
                            "last_activity": datetime.now().isoformat(),
         | 
| 90 | 
            +
                        }
         | 
| 91 | 
            +
                        
         | 
| 92 | 
            +
                        self.logger.debug(
         | 
| 93 | 
            +
                            f"Tracked subagent start: session={session_id[:8]}..., agent={agent_type}"
         | 
| 94 | 
            +
                        )
         | 
| 95 | 
            +
                        
         | 
| 96 | 
            +
                async def _handle_subagent_stop(self, data: Dict[str, Any]):
         | 
| 97 | 
            +
                    """Handle subagent stop events.
         | 
| 98 | 
            +
                    
         | 
| 99 | 
            +
                    WHY: When a subagent stops, we update its status or remove it
         | 
| 100 | 
            +
                    from active sessions depending on the stop reason.
         | 
| 101 | 
            +
                    """
         | 
| 102 | 
            +
                    session_id = data.get("session_id")
         | 
| 103 | 
            +
                    
         | 
| 104 | 
            +
                    if not session_id:
         | 
| 105 | 
            +
                        return
         | 
| 106 | 
            +
                        
         | 
| 107 | 
            +
                    # Update session status
         | 
| 108 | 
            +
                    if hasattr(self.server, 'active_sessions'):
         | 
| 109 | 
            +
                        if session_id in self.server.active_sessions:
         | 
| 110 | 
            +
                            # Mark as completed rather than removing immediately
         | 
| 111 | 
            +
                            self.server.active_sessions[session_id]["status"] = "completed"
         | 
| 112 | 
            +
                            self.server.active_sessions[session_id]["last_activity"] = datetime.now().isoformat()
         | 
| 113 | 
            +
                            
         | 
| 114 | 
            +
                            self.logger.debug(
         | 
| 115 | 
            +
                                f"Marked session completed: session={session_id[:8]}..."
         | 
| 116 | 
            +
                            )
         | 
| 117 | 
            +
                            
         | 
| 118 | 
            +
                async def _handle_user_prompt(self, data: Dict[str, Any]):
         | 
| 119 | 
            +
                    """Handle user prompt events.
         | 
| 120 | 
            +
                    
         | 
| 121 | 
            +
                    WHY: User prompts indicate the start of a new interaction,
         | 
| 122 | 
            +
                    which we track as a PM session if no delegation occurs.
         | 
| 123 | 
            +
                    """
         | 
| 124 | 
            +
                    session_id = data.get("session_id")
         | 
| 125 | 
            +
                    
         | 
| 126 | 
            +
                    if not session_id:
         | 
| 127 | 
            +
                        return
         | 
| 128 | 
            +
                        
         | 
| 129 | 
            +
                    # Create or update PM session
         | 
| 130 | 
            +
                    if hasattr(self.server, 'active_sessions'):
         | 
| 131 | 
            +
                        if session_id not in self.server.active_sessions:
         | 
| 132 | 
            +
                            self.server.active_sessions[session_id] = {
         | 
| 133 | 
            +
                                "session_id": session_id,
         | 
| 134 | 
            +
                                "start_time": datetime.now().isoformat(),
         | 
| 135 | 
            +
                                "agent": "pm",  # Default to PM
         | 
| 136 | 
            +
                                "status": "active",
         | 
| 137 | 
            +
                                "prompt": data.get("prompt_text", "")[:100],
         | 
| 138 | 
            +
                                "working_directory": data.get("working_directory", ""),
         | 
| 139 | 
            +
                                "last_activity": datetime.now().isoformat(),
         | 
| 140 | 
            +
                            }
         | 
| 141 | 
            +
                        else:
         | 
| 142 | 
            +
                            # Update last activity
         | 
| 143 | 
            +
                            self.server.active_sessions[session_id]["last_activity"] = datetime.now().isoformat()
         | 
| 144 | 
            +
                            
         | 
| 145 | 
            +
                async def _handle_pre_tool(self, data: Dict[str, Any]):
         | 
| 146 | 
            +
                    """Handle pre-tool events.
         | 
| 147 | 
            +
                    
         | 
| 148 | 
            +
                    WHY: Pre-tool events with Task delegation indicate agent changes
         | 
| 149 | 
            +
                    that we need to track for accurate session information.
         | 
| 150 | 
            +
                    """
         | 
| 151 | 
            +
                    if data.get("tool_name") != "Task":
         | 
| 152 | 
            +
                        return
         | 
| 153 | 
            +
                        
         | 
| 154 | 
            +
                    session_id = data.get("session_id")
         | 
| 155 | 
            +
                    delegation_details = data.get("delegation_details", {})
         | 
| 156 | 
            +
                    agent_type = delegation_details.get("agent_type", "unknown")
         | 
| 157 | 
            +
                    
         | 
| 158 | 
            +
                    if not session_id:
         | 
| 159 | 
            +
                        return
         | 
| 160 | 
            +
                        
         | 
| 161 | 
            +
                    # Update session with new agent
         | 
| 162 | 
            +
                    if hasattr(self.server, 'active_sessions'):
         | 
| 163 | 
            +
                        if session_id in self.server.active_sessions:
         | 
| 164 | 
            +
                            self.server.active_sessions[session_id]["agent"] = agent_type
         | 
| 165 | 
            +
                            self.server.active_sessions[session_id]["status"] = "delegated"
         | 
| 166 | 
            +
                            self.server.active_sessions[session_id]["last_activity"] = datetime.now().isoformat()
         | 
| 167 | 
            +
                            
         | 
| 168 | 
            +
                            self.logger.debug(
         | 
| 169 | 
            +
                                f"Updated session delegation: session={session_id[:8]}..., agent={agent_type}"
         | 
| 170 | 
            +
                            )
         | 
| @@ -10,18 +10,18 @@ from .base import BaseEventHandler | |
| 10 10 |  | 
| 11 11 | 
             
            class MemoryEventHandler(BaseEventHandler):
         | 
| 12 12 | 
             
                """Handles memory-related Socket.IO events.
         | 
| 13 | 
            -
             | 
| 13 | 
            +
             | 
| 14 14 | 
             
                WHY: Agent memory management events will be handled here as the system
         | 
| 15 15 | 
             
                grows. This provides a clean separation for memory-specific functionality.
         | 
| 16 16 | 
             
                """
         | 
| 17 | 
            -
             | 
| 17 | 
            +
             | 
| 18 18 | 
             
                def register_events(self):
         | 
| 19 19 | 
             
                    """Register memory-related event handlers.
         | 
| 20 | 
            -
             | 
| 20 | 
            +
             | 
| 21 21 | 
             
                    Currently memory events are handled through broadcast methods
         | 
| 22 22 | 
             
                    rather than Socket.IO event handlers, but this provides a place
         | 
| 23 23 | 
             
                    for future interactive memory management features.
         | 
| 24 24 | 
             
                    """
         | 
| 25 25 | 
             
                    # Future memory management events will be registered here
         | 
| 26 26 | 
             
                    # For example: query_memory, clear_memory, export_memory, etc.
         | 
| 27 | 
            -
                    pass
         | 
| 27 | 
            +
                    pass
         | 
| @@ -10,16 +10,16 @@ from .base import BaseEventHandler | |
| 10 10 |  | 
| 11 11 | 
             
            class ProjectEventHandler(BaseEventHandler):
         | 
| 12 12 | 
             
                """Handles project-related Socket.IO events.
         | 
| 13 | 
            -
             | 
| 13 | 
            +
             | 
| 14 14 | 
             
                WHY: Project management events will be handled here as the system
         | 
| 15 15 | 
             
                grows. This provides a clean separation for project-specific functionality.
         | 
| 16 16 | 
             
                """
         | 
| 17 | 
            -
             | 
| 17 | 
            +
             | 
| 18 18 | 
             
                def register_events(self):
         | 
| 19 19 | 
             
                    """Register project-related event handlers.
         | 
| 20 | 
            -
             | 
| 20 | 
            +
             | 
| 21 21 | 
             
                    Currently no project-specific events are defined, but this
         | 
| 22 22 | 
             
                    handler is ready for future expansion.
         | 
| 23 23 | 
             
                    """
         | 
| 24 24 | 
             
                    # Future project events will be registered here
         | 
| 25 | 
            -
                    pass
         | 
| 25 | 
            +
                    pass
         | 
| @@ -5,66 +5,73 @@ providing a clean interface for the SocketIOServer to register all | |
| 5 5 | 
             
            events without knowing the details of each handler.
         | 
| 6 6 | 
             
            """
         | 
| 7 7 |  | 
| 8 | 
            -
            from typing import List, Type, Optional, TYPE_CHECKING
         | 
| 9 8 | 
             
            from logging import Logger
         | 
| 10 | 
            -
            from  | 
| 9 | 
            +
            from typing import TYPE_CHECKING, List, Optional, Type
         | 
| 11 10 |  | 
| 11 | 
            +
            from ....core.logger import get_logger
         | 
| 12 12 | 
             
            from .base import BaseEventHandler
         | 
| 13 13 |  | 
| 14 14 | 
             
            if TYPE_CHECKING:
         | 
| 15 15 | 
             
                from ..server import SocketIOServer
         | 
| 16 | 
            +
             | 
| 16 17 | 
             
            from .connection import ConnectionEventHandler
         | 
| 17 | 
            -
            from .project import ProjectEventHandler
         | 
| 18 | 
            -
            from .memory import MemoryEventHandler
         | 
| 19 18 | 
             
            from .file import FileEventHandler
         | 
| 20 19 | 
             
            from .git import GitEventHandler
         | 
| 20 | 
            +
            from .hook import HookEventHandler
         | 
| 21 | 
            +
            from .memory import MemoryEventHandler
         | 
| 22 | 
            +
            from .project import ProjectEventHandler
         | 
| 21 23 |  | 
| 22 24 |  | 
| 23 25 | 
             
            class EventHandlerRegistry:
         | 
| 24 26 | 
             
                """Manages registration of Socket.IO event handlers.
         | 
| 25 | 
            -
             | 
| 27 | 
            +
             | 
| 26 28 | 
             
                WHY: The registry pattern allows us to easily add, remove, or modify
         | 
| 27 29 | 
             
                event handlers without changing the SocketIOServer implementation.
         | 
| 28 30 | 
             
                It provides a single point of configuration for all event handlers.
         | 
| 29 31 | 
             
                """
         | 
| 30 | 
            -
             | 
| 32 | 
            +
             | 
| 31 33 | 
             
                # Default handler classes in registration order
         | 
| 32 34 | 
             
                DEFAULT_HANDLERS: List[Type[BaseEventHandler]] = [
         | 
| 33 35 | 
             
                    ConnectionEventHandler,  # Connection management first
         | 
| 34 | 
            -
                     | 
| 35 | 
            -
                     | 
| 36 | 
            -
                     | 
| 37 | 
            -
                     | 
| 36 | 
            +
                    HookEventHandler,  # Hook events for session tracking
         | 
| 37 | 
            +
                    GitEventHandler,  # Git operations
         | 
| 38 | 
            +
                    FileEventHandler,  # File operations
         | 
| 39 | 
            +
                    ProjectEventHandler,  # Project management (future)
         | 
| 40 | 
            +
                    MemoryEventHandler,  # Memory management (future)
         | 
| 38 41 | 
             
                ]
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                def __init__(self, server:  | 
| 42 | 
            +
             | 
| 43 | 
            +
                def __init__(self, server: "SocketIOServer") -> None:
         | 
| 41 44 | 
             
                    """Initialize the registry.
         | 
| 42 | 
            -
             | 
| 45 | 
            +
             | 
| 43 46 | 
             
                    Args:
         | 
| 44 47 | 
             
                        server: The SocketIOServer instance
         | 
| 45 48 | 
             
                    """
         | 
| 46 | 
            -
                    self.server:  | 
| 49 | 
            +
                    self.server: "SocketIOServer" = server
         | 
| 47 50 | 
             
                    self.logger: Logger = get_logger("EventHandlerRegistry")
         | 
| 48 51 | 
             
                    self.handlers: List[BaseEventHandler] = []
         | 
| 49 52 | 
             
                    self._initialized: bool = False
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                def initialize( | 
| 53 | 
            +
             | 
| 54 | 
            +
                def initialize(
         | 
| 55 | 
            +
                    self, handler_classes: Optional[List[Type[BaseEventHandler]]] = None
         | 
| 56 | 
            +
                ) -> None:
         | 
| 52 57 | 
             
                    """Initialize all event handlers.
         | 
| 53 | 
            -
             | 
| 58 | 
            +
             | 
| 54 59 | 
             
                    WHY: This creates instances of all handler classes and prepares
         | 
| 55 60 | 
             
                    them for event registration. Using a list of classes allows
         | 
| 56 61 | 
             
                    customization of which handlers to use.
         | 
| 57 | 
            -
             | 
| 62 | 
            +
             | 
| 58 63 | 
             
                    Args:
         | 
| 59 64 | 
             
                        handler_classes: Optional list of handler classes to use.
         | 
| 60 65 | 
             
                                       Defaults to DEFAULT_HANDLERS if not provided.
         | 
| 61 66 | 
             
                    """
         | 
| 62 67 | 
             
                    if self._initialized:
         | 
| 63 | 
            -
                        self.logger.warning( | 
| 68 | 
            +
                        self.logger.warning(
         | 
| 69 | 
            +
                            "Registry already initialized, skipping re-initialization"
         | 
| 70 | 
            +
                        )
         | 
| 64 71 | 
             
                        return
         | 
| 65 | 
            -
             | 
| 72 | 
            +
             | 
| 66 73 | 
             
                    handler_classes = handler_classes or self.DEFAULT_HANDLERS
         | 
| 67 | 
            -
             | 
| 74 | 
            +
             | 
| 68 75 | 
             
                    for handler_class in handler_classes:
         | 
| 69 76 | 
             
                        try:
         | 
| 70 77 | 
             
                            handler = handler_class(self.server)
         | 
| @@ -73,14 +80,15 @@ class EventHandlerRegistry: | |
| 73 80 | 
             
                        except Exception as e:
         | 
| 74 81 | 
             
                            self.logger.error(f"Failed to initialize {handler_class.__name__}: {e}")
         | 
| 75 82 | 
             
                            import traceback
         | 
| 83 | 
            +
             | 
| 76 84 | 
             
                            self.logger.error(f"Stack trace: {traceback.format_exc()}")
         | 
| 77 | 
            -
             | 
| 85 | 
            +
             | 
| 78 86 | 
             
                    self._initialized = True
         | 
| 79 87 | 
             
                    self.logger.info(f"Registry initialized with {len(self.handlers)} handlers")
         | 
| 80 | 
            -
             | 
| 88 | 
            +
             | 
| 81 89 | 
             
                def register_all_events(self) -> None:
         | 
| 82 90 | 
             
                    """Register all events from all handlers.
         | 
| 83 | 
            -
             | 
| 91 | 
            +
             | 
| 84 92 | 
             
                    WHY: This is the main method called by SocketIOServer to register
         | 
| 85 93 | 
             
                    all events. It delegates to each handler's register_events method,
         | 
| 86 94 | 
             
                    keeping the server code clean and simple.
         | 
| @@ -88,7 +96,7 @@ class EventHandlerRegistry: | |
| 88 96 | 
             
                    if not self._initialized:
         | 
| 89 97 | 
             
                        self.logger.error("Registry not initialized. Call initialize() first.")
         | 
| 90 98 | 
             
                        raise RuntimeError("EventHandlerRegistry not initialized")
         | 
| 91 | 
            -
             | 
| 99 | 
            +
             | 
| 92 100 | 
             
                    registered_count = 0
         | 
| 93 101 | 
             
                    for handler in self.handlers:
         | 
| 94 102 | 
             
                        try:
         | 
| @@ -97,27 +105,34 @@ class EventHandlerRegistry: | |
| 97 105 | 
             
                            self.logger.info(f"Registered events for {handler.__class__.__name__}")
         | 
| 98 106 | 
             
                        except NotImplementedError:
         | 
| 99 107 | 
             
                            # Handler has no events to register (like ProjectEventHandler)
         | 
| 100 | 
            -
                            self.logger.debug( | 
| 108 | 
            +
                            self.logger.debug(
         | 
| 109 | 
            +
                                f"No events to register for {handler.__class__.__name__}"
         | 
| 110 | 
            +
                            )
         | 
| 101 111 | 
             
                        except Exception as e:
         | 
| 102 | 
            -
                            self.logger.error( | 
| 112 | 
            +
                            self.logger.error(
         | 
| 113 | 
            +
                                f"Failed to register events for {handler.__class__.__name__}: {e}"
         | 
| 114 | 
            +
                            )
         | 
| 103 115 | 
             
                            import traceback
         | 
| 116 | 
            +
             | 
| 104 117 | 
             
                            self.logger.error(f"Stack trace: {traceback.format_exc()}")
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                    self.logger.info( | 
| 107 | 
            -
             | 
| 118 | 
            +
             | 
| 119 | 
            +
                    self.logger.info(
         | 
| 120 | 
            +
                        f"Successfully registered events from {registered_count} handlers"
         | 
| 121 | 
            +
                    )
         | 
| 122 | 
            +
             | 
| 108 123 | 
             
                def add_handler(self, handler_class: Type[BaseEventHandler]):
         | 
| 109 124 | 
             
                    """Add a custom handler to the registry.
         | 
| 110 | 
            -
             | 
| 125 | 
            +
             | 
| 111 126 | 
             
                    WHY: Allows dynamic addition of custom handlers for specific
         | 
| 112 127 | 
             
                    deployments or testing without modifying the default handlers.
         | 
| 113 | 
            -
             | 
| 128 | 
            +
             | 
| 114 129 | 
             
                    Args:
         | 
| 115 130 | 
             
                        handler_class: The handler class to add
         | 
| 116 131 | 
             
                    """
         | 
| 117 132 | 
             
                    if not self._initialized:
         | 
| 118 133 | 
             
                        self.logger.error("Registry not initialized. Call initialize() first.")
         | 
| 119 134 | 
             
                        raise RuntimeError("EventHandlerRegistry not initialized")
         | 
| 120 | 
            -
             | 
| 135 | 
            +
             | 
| 121 136 | 
             
                    try:
         | 
| 122 137 | 
             
                        handler = handler_class(self.server)
         | 
| 123 138 | 
             
                        self.handlers.append(handler)
         | 
| @@ -126,20 +141,20 @@ class EventHandlerRegistry: | |
| 126 141 | 
             
                    except Exception as e:
         | 
| 127 142 | 
             
                        self.logger.error(f"Failed to add handler {handler_class.__name__}: {e}")
         | 
| 128 143 | 
             
                        raise
         | 
| 129 | 
            -
             | 
| 144 | 
            +
             | 
| 130 145 | 
             
                def get_handler(self, handler_class: Type[BaseEventHandler]) -> BaseEventHandler:
         | 
| 131 146 | 
             
                    """Get a specific handler instance by class.
         | 
| 132 | 
            -
             | 
| 147 | 
            +
             | 
| 133 148 | 
             
                    WHY: Useful for testing or when specific handler functionality
         | 
| 134 149 | 
             
                    needs to be accessed directly.
         | 
| 135 | 
            -
             | 
| 150 | 
            +
             | 
| 136 151 | 
             
                    Args:
         | 
| 137 152 | 
             
                        handler_class: The handler class to find
         | 
| 138 | 
            -
             | 
| 153 | 
            +
             | 
| 139 154 | 
             
                    Returns:
         | 
| 140 155 | 
             
                        The handler instance or None if not found
         | 
| 141 156 | 
             
                    """
         | 
| 142 157 | 
             
                    for handler in self.handlers:
         | 
| 143 158 | 
             
                        if isinstance(handler, handler_class):
         | 
| 144 159 | 
             
                            return handler
         | 
| 145 | 
            -
                    return None
         | 
| 160 | 
            +
                    return None
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            """
         | 
| 2 | 
            +
            SocketIO Server package for claude-mpm.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            WHY: This package contains the modular SocketIO server components that were
         | 
| 5 | 
            +
            extracted from the monolithic socketio_server.py file.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            DESIGN DECISION: Split the massive SocketIOServer class into focused modules:
         | 
| 8 | 
            +
            - core.py: Server lifecycle and static file management
         | 
| 9 | 
            +
            - broadcaster.py: Event broadcasting to clients
         | 
| 10 | 
            +
            - event_registry.py: Event handler registration (if needed)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            This improves maintainability, testability, and code organization.
         | 
| 13 | 
            +
            """
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            from .broadcaster import SocketIOEventBroadcaster
         | 
| 16 | 
            +
            from .core import SocketIOServerCore
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            __all__ = ["SocketIOServerCore", "SocketIOEventBroadcaster"]
         | 
| @@ -0,0 +1,252 @@ | |
| 1 | 
            +
            """
         | 
| 2 | 
            +
            SocketIO Event Broadcaster for claude-mpm.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            WHY: This module contains all the broadcasting methods extracted from the
         | 
| 5 | 
            +
            monolithic socketio_server.py file. It handles sending events to connected
         | 
| 6 | 
            +
            clients for various Claude MPM activities.
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            DESIGN DECISION: Separated broadcasting logic from core server management
         | 
| 9 | 
            +
            to create focused, testable modules with single responsibilities.
         | 
| 10 | 
            +
            """
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            import asyncio
         | 
| 13 | 
            +
            from datetime import datetime
         | 
| 14 | 
            +
            from typing import Any, Dict, List, Optional, Set
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            from ....core.logging_config import get_logger
         | 
| 17 | 
            +
             | 
| 18 | 
            +
             | 
| 19 | 
            +
            class SocketIOEventBroadcaster:
         | 
| 20 | 
            +
                """Handles broadcasting events to connected Socket.IO clients.
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                WHY: This class encapsulates all the event broadcasting logic that was
         | 
| 23 | 
            +
                scattered throughout the monolithic SocketIOServer class.
         | 
| 24 | 
            +
                """
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def __init__(
         | 
| 27 | 
            +
                    self,
         | 
| 28 | 
            +
                    sio,
         | 
| 29 | 
            +
                    connected_clients: Set[str],
         | 
| 30 | 
            +
                    event_buffer,
         | 
| 31 | 
            +
                    buffer_lock,
         | 
| 32 | 
            +
                    stats: Dict[str, Any],
         | 
| 33 | 
            +
                    logger,
         | 
| 34 | 
            +
                    server=None,  # Add server reference for event history access
         | 
| 35 | 
            +
                ):
         | 
| 36 | 
            +
                    self.sio = sio
         | 
| 37 | 
            +
                    self.connected_clients = connected_clients
         | 
| 38 | 
            +
                    self.event_buffer = event_buffer
         | 
| 39 | 
            +
                    self.buffer_lock = buffer_lock
         | 
| 40 | 
            +
                    self.stats = stats
         | 
| 41 | 
            +
                    self.logger = logger
         | 
| 42 | 
            +
                    self.loop = None  # Will be set by main server
         | 
| 43 | 
            +
                    self.server = server  # Reference to main server for event history
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def broadcast_event(self, event_type: str, data: Dict[str, Any]):
         | 
| 46 | 
            +
                    """Broadcast an event to all connected clients."""
         | 
| 47 | 
            +
                    if not self.sio:
         | 
| 48 | 
            +
                        return
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    event = {
         | 
| 51 | 
            +
                        "type": event_type,
         | 
| 52 | 
            +
                        "timestamp": datetime.now().isoformat(),
         | 
| 53 | 
            +
                        "data": data,
         | 
| 54 | 
            +
                    }
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    # Buffer the event for reliability AND add to event history for new clients
         | 
| 57 | 
            +
                    with self.buffer_lock:
         | 
| 58 | 
            +
                        self.event_buffer.append(event)
         | 
| 59 | 
            +
                        self.stats["events_buffered"] += 1
         | 
| 60 | 
            +
                        
         | 
| 61 | 
            +
                        # Also add to event history if available (for client replay)
         | 
| 62 | 
            +
                        # Access through server reference to maintain single history source
         | 
| 63 | 
            +
                        if hasattr(self, 'server') and hasattr(self.server, 'event_history'):
         | 
| 64 | 
            +
                            self.server.event_history.append(event)
         | 
| 65 | 
            +
                            self.logger.debug(f"Added {event_type} to history (total: {len(self.server.event_history)})")
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    # Broadcast to all connected clients
         | 
| 68 | 
            +
                    try:
         | 
| 69 | 
            +
                        # Use run_coroutine_threadsafe to safely call from any thread
         | 
| 70 | 
            +
                        if hasattr(self, "loop") and self.loop and not self.loop.is_closed():
         | 
| 71 | 
            +
                            future = asyncio.run_coroutine_threadsafe(
         | 
| 72 | 
            +
                                self.sio.emit("claude_event", event), self.loop
         | 
| 73 | 
            +
                            )
         | 
| 74 | 
            +
                            # Don't wait for the result to avoid blocking
         | 
| 75 | 
            +
                            self.stats["events_sent"] += 1
         | 
| 76 | 
            +
                            self.logger.debug(f"Broadcasted event: {event_type}")
         | 
| 77 | 
            +
                        else:
         | 
| 78 | 
            +
                            self.logger.warning(
         | 
| 79 | 
            +
                                f"Cannot broadcast {event_type}: server loop not available"
         | 
| 80 | 
            +
                            )
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    except Exception as e:
         | 
| 83 | 
            +
                        self.logger.error(f"Failed to broadcast event {event_type}: {e}")
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                def session_started(self, session_id: str, launch_method: str, working_dir: str):
         | 
| 86 | 
            +
                    """Notify that a session has started."""
         | 
| 87 | 
            +
                    self.broadcast_event(
         | 
| 88 | 
            +
                        "session_started",
         | 
| 89 | 
            +
                        {
         | 
| 90 | 
            +
                            "session_id": session_id,
         | 
| 91 | 
            +
                            "launch_method": launch_method,
         | 
| 92 | 
            +
                            "working_dir": working_dir,
         | 
| 93 | 
            +
                            "timestamp": datetime.now().isoformat(),
         | 
| 94 | 
            +
                        },
         | 
| 95 | 
            +
                    )
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                def session_ended(self):
         | 
| 98 | 
            +
                    """Notify that a session has ended."""
         | 
| 99 | 
            +
                    self.broadcast_event("session_ended", {"timestamp": datetime.now().isoformat()})
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def claude_status_changed(
         | 
| 102 | 
            +
                    self, status: str, pid: Optional[int] = None, message: str = ""
         | 
| 103 | 
            +
                ):
         | 
| 104 | 
            +
                    """Notify Claude status change."""
         | 
| 105 | 
            +
                    self.broadcast_event(
         | 
| 106 | 
            +
                        "claude_status", {"status": status, "pid": pid, "message": message}
         | 
| 107 | 
            +
                    )
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                def claude_output(self, content: str, stream: str = "stdout"):
         | 
| 110 | 
            +
                    """Broadcast Claude output."""
         | 
| 111 | 
            +
                    self.broadcast_event("claude_output", {"content": content, "stream": stream})
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                def agent_delegated(self, agent: str, task: str, status: str = "started"):
         | 
| 114 | 
            +
                    """Notify agent delegation."""
         | 
| 115 | 
            +
                    self.broadcast_event(
         | 
| 116 | 
            +
                        "agent_delegated", {"agent": agent, "task": task, "status": status}
         | 
| 117 | 
            +
                    )
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                def todo_updated(self, todos: List[Dict[str, Any]]):
         | 
| 120 | 
            +
                    """Notify todo list update."""
         | 
| 121 | 
            +
                    # Limit the size of todo data to prevent large payloads
         | 
| 122 | 
            +
                    limited_todos = todos[:50] if len(todos) > 50 else todos
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                    self.broadcast_event(
         | 
| 125 | 
            +
                        "todo_updated",
         | 
| 126 | 
            +
                        {
         | 
| 127 | 
            +
                            "todos": limited_todos,
         | 
| 128 | 
            +
                            "total_count": len(todos),
         | 
| 129 | 
            +
                            "truncated": len(todos) > 50,
         | 
| 130 | 
            +
                        },
         | 
| 131 | 
            +
                    )
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                def ticket_created(self, ticket_id: str, title: str, priority: str = "medium"):
         | 
| 134 | 
            +
                    """Notify ticket creation."""
         | 
| 135 | 
            +
                    self.broadcast_event(
         | 
| 136 | 
            +
                        "ticket_created",
         | 
| 137 | 
            +
                        {"ticket_id": ticket_id, "title": title, "priority": priority},
         | 
| 138 | 
            +
                    )
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                def memory_loaded(self, agent_id: str, memory_size: int, sections_count: int):
         | 
| 141 | 
            +
                    """Notify when agent memory is loaded from file."""
         | 
| 142 | 
            +
                    self.broadcast_event(
         | 
| 143 | 
            +
                        "memory_loaded",
         | 
| 144 | 
            +
                        {
         | 
| 145 | 
            +
                            "agent_id": agent_id,
         | 
| 146 | 
            +
                            "memory_size": memory_size,
         | 
| 147 | 
            +
                            "sections_count": sections_count,
         | 
| 148 | 
            +
                        },
         | 
| 149 | 
            +
                    )
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                def memory_created(self, agent_id: str, template_type: str):
         | 
| 152 | 
            +
                    """Notify when new agent memory is created from template."""
         | 
| 153 | 
            +
                    self.broadcast_event(
         | 
| 154 | 
            +
                        "memory_created", {"agent_id": agent_id, "template_type": template_type}
         | 
| 155 | 
            +
                    )
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                def memory_updated(
         | 
| 158 | 
            +
                    self, agent_id: str, learning_type: str, content: str, section: str
         | 
| 159 | 
            +
                ):
         | 
| 160 | 
            +
                    """Notify when learning is added to agent memory."""
         | 
| 161 | 
            +
                    # Truncate content if too long to prevent large payloads
         | 
| 162 | 
            +
                    truncated_content = content[:500] + "..." if len(content) > 500 else content
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                    self.broadcast_event(
         | 
| 165 | 
            +
                        "memory_updated",
         | 
| 166 | 
            +
                        {
         | 
| 167 | 
            +
                            "agent_id": agent_id,
         | 
| 168 | 
            +
                            "learning_type": learning_type,
         | 
| 169 | 
            +
                            "content": truncated_content,
         | 
| 170 | 
            +
                            "section": section,
         | 
| 171 | 
            +
                            "content_length": len(content),
         | 
| 172 | 
            +
                            "truncated": len(content) > 500,
         | 
| 173 | 
            +
                        },
         | 
| 174 | 
            +
                    )
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                def memory_injected(self, agent_id: str, context_size: int):
         | 
| 177 | 
            +
                    """Notify when agent memory is injected into context."""
         | 
| 178 | 
            +
                    self.broadcast_event(
         | 
| 179 | 
            +
                        "memory_injected", {"agent_id": agent_id, "context_size": context_size}
         | 
| 180 | 
            +
                    )
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                def file_changed(
         | 
| 183 | 
            +
                    self, file_path: str, change_type: str, content: Optional[str] = None
         | 
| 184 | 
            +
                ):
         | 
| 185 | 
            +
                    """Notify file system changes."""
         | 
| 186 | 
            +
                    event_data = {"file_path": file_path, "change_type": change_type}
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                    # Include content for small files only
         | 
| 189 | 
            +
                    if content and len(content) < 1000:
         | 
| 190 | 
            +
                        event_data["content"] = content
         | 
| 191 | 
            +
                    elif content:
         | 
| 192 | 
            +
                        event_data["content_preview"] = content[:200] + "..."
         | 
| 193 | 
            +
                        event_data["content_length"] = len(content)
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                    self.broadcast_event("file_changed", event_data)
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                def git_operation(self, operation: str, details: Dict[str, Any]):
         | 
| 198 | 
            +
                    """Notify Git operations."""
         | 
| 199 | 
            +
                    self.broadcast_event(
         | 
| 200 | 
            +
                        "git_operation", {"operation": operation, "details": details}
         | 
| 201 | 
            +
                    )
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                def error_occurred(
         | 
| 204 | 
            +
                    self, error_type: str, message: str, details: Optional[Dict[str, Any]] = None
         | 
| 205 | 
            +
                ):
         | 
| 206 | 
            +
                    """Notify when errors occur."""
         | 
| 207 | 
            +
                    self.broadcast_event(
         | 
| 208 | 
            +
                        "error",
         | 
| 209 | 
            +
                        {"error_type": error_type, "message": message, "details": details or {}},
         | 
| 210 | 
            +
                    )
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                def performance_metric(self, metric_name: str, value: float, unit: str = ""):
         | 
| 213 | 
            +
                    """Broadcast performance metrics."""
         | 
| 214 | 
            +
                    self.broadcast_event(
         | 
| 215 | 
            +
                        "performance", {"metric": metric_name, "value": value, "unit": unit}
         | 
| 216 | 
            +
                    )
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                def system_status(self, status: Dict[str, Any]):
         | 
| 219 | 
            +
                    """Broadcast system status information."""
         | 
| 220 | 
            +
                    self.broadcast_event("system_status", status)
         | 
| 221 | 
            +
                
         | 
| 222 | 
            +
                def broadcast_system_heartbeat(self, heartbeat_data: Dict[str, Any]):
         | 
| 223 | 
            +
                    """Broadcast system heartbeat event.
         | 
| 224 | 
            +
                    
         | 
| 225 | 
            +
                    WHY: System events are separate from hook events to provide
         | 
| 226 | 
            +
                    server health monitoring independent of Claude activity.
         | 
| 227 | 
            +
                    """
         | 
| 228 | 
            +
                    if not self.sio:
         | 
| 229 | 
            +
                        return
         | 
| 230 | 
            +
                        
         | 
| 231 | 
            +
                    # Create system event with consistent format
         | 
| 232 | 
            +
                    event = {
         | 
| 233 | 
            +
                        "type": "system",
         | 
| 234 | 
            +
                        "event": "heartbeat",
         | 
| 235 | 
            +
                        "timestamp": datetime.now().isoformat(),
         | 
| 236 | 
            +
                        "data": heartbeat_data,
         | 
| 237 | 
            +
                    }
         | 
| 238 | 
            +
                    
         | 
| 239 | 
            +
                    # Broadcast to all connected clients
         | 
| 240 | 
            +
                    try:
         | 
| 241 | 
            +
                        if self.loop and not self.loop.is_closed():
         | 
| 242 | 
            +
                            future = asyncio.run_coroutine_threadsafe(
         | 
| 243 | 
            +
                                self.sio.emit("system_event", event), self.loop
         | 
| 244 | 
            +
                            )
         | 
| 245 | 
            +
                            self.logger.debug(
         | 
| 246 | 
            +
                                f"Broadcasted system heartbeat - clients: {len(self.connected_clients)}, "
         | 
| 247 | 
            +
                                f"uptime: {heartbeat_data.get('uptime_seconds', 0)}s"
         | 
| 248 | 
            +
                            )
         | 
| 249 | 
            +
                        else:
         | 
| 250 | 
            +
                            self.logger.warning("Cannot broadcast heartbeat: server loop not available")
         | 
| 251 | 
            +
                    except Exception as e:
         | 
| 252 | 
            +
                        self.logger.error(f"Failed to broadcast system heartbeat: {e}")
         |