claude-mpm 4.1.1__py3-none-any.whl → 4.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/__main__.py +1 -1
- claude_mpm/agents/BASE_PM.md +74 -46
- claude_mpm/agents/INSTRUCTIONS.md +11 -153
- claude_mpm/agents/WORKFLOW.md +61 -321
- claude_mpm/agents/__init__.py +11 -11
- claude_mpm/agents/agent_loader.py +23 -20
- claude_mpm/agents/agent_loader_integration.py +1 -1
- claude_mpm/agents/agents_metadata.py +27 -0
- claude_mpm/agents/async_agent_loader.py +5 -8
- claude_mpm/agents/base_agent_loader.py +36 -25
- claude_mpm/agents/frontmatter_validator.py +6 -6
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +9 -9
- claude_mpm/agents/templates/api_qa.json +47 -2
- claude_mpm/agents/templates/engineer.json +33 -11
- claude_mpm/agents/templates/imagemagick.json +256 -0
- claude_mpm/agents/templates/qa.json +41 -2
- claude_mpm/agents/templates/ticketing.json +5 -5
- claude_mpm/agents/templates/web_qa.json +50 -2
- claude_mpm/cli/__init__.py +51 -46
- claude_mpm/cli/__main__.py +1 -1
- claude_mpm/cli/commands/__init__.py +10 -12
- claude_mpm/cli/commands/agent_manager.py +186 -181
- claude_mpm/cli/commands/agents.py +648 -1098
- claude_mpm/cli/commands/aggregate.py +30 -29
- claude_mpm/cli/commands/cleanup.py +50 -44
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
- claude_mpm/cli/commands/config.py +162 -127
- claude_mpm/cli/commands/doctor.py +52 -62
- claude_mpm/cli/commands/info.py +37 -25
- claude_mpm/cli/commands/mcp.py +3 -7
- claude_mpm/cli/commands/mcp_command_router.py +14 -18
- claude_mpm/cli/commands/mcp_install_commands.py +28 -23
- claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
- claude_mpm/cli/commands/mcp_server_commands.py +23 -17
- claude_mpm/cli/commands/memory.py +339 -967
- claude_mpm/cli/commands/monitor.py +117 -88
- claude_mpm/cli/commands/run.py +233 -542
- claude_mpm/cli/commands/socketio_monitor.py +17 -19
- claude_mpm/cli/commands/tickets.py +92 -92
- claude_mpm/cli/parser.py +1 -5
- claude_mpm/cli/parsers/__init__.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
- claude_mpm/cli/parsers/agents_parser.py +2 -3
- claude_mpm/cli/parsers/base_parser.py +7 -5
- claude_mpm/cli/parsers/mcp_parser.py +4 -2
- claude_mpm/cli/parsers/monitor_parser.py +26 -18
- claude_mpm/cli/shared/__init__.py +10 -10
- claude_mpm/cli/shared/argument_patterns.py +57 -71
- claude_mpm/cli/shared/base_command.py +61 -53
- claude_mpm/cli/shared/error_handling.py +62 -58
- claude_mpm/cli/shared/output_formatters.py +78 -77
- claude_mpm/cli/startup_logging.py +280 -172
- claude_mpm/cli/utils.py +10 -11
- claude_mpm/cli_module/__init__.py +1 -1
- claude_mpm/cli_module/args.py +1 -1
- claude_mpm/cli_module/migration_example.py +5 -5
- claude_mpm/config/__init__.py +9 -9
- claude_mpm/config/agent_config.py +15 -14
- claude_mpm/config/experimental_features.py +4 -4
- claude_mpm/config/paths.py +0 -1
- claude_mpm/config/socketio_config.py +5 -6
- claude_mpm/constants.py +1 -2
- claude_mpm/core/__init__.py +8 -8
- claude_mpm/core/agent_name_normalizer.py +1 -1
- claude_mpm/core/agent_registry.py +22 -29
- claude_mpm/core/agent_session_manager.py +3 -3
- claude_mpm/core/base_service.py +7 -15
- claude_mpm/core/cache.py +4 -6
- claude_mpm/core/claude_runner.py +85 -113
- claude_mpm/core/config.py +43 -28
- claude_mpm/core/config_aliases.py +0 -9
- claude_mpm/core/config_constants.py +52 -30
- claude_mpm/core/constants.py +0 -1
- claude_mpm/core/container.py +18 -27
- claude_mpm/core/exceptions.py +2 -2
- claude_mpm/core/factories.py +10 -12
- claude_mpm/core/framework_loader.py +500 -680
- claude_mpm/core/hook_manager.py +26 -22
- claude_mpm/core/hook_performance_config.py +58 -47
- claude_mpm/core/injectable_service.py +1 -1
- claude_mpm/core/interactive_session.py +61 -152
- claude_mpm/core/interfaces.py +1 -100
- claude_mpm/core/lazy.py +5 -5
- claude_mpm/core/log_manager.py +587 -0
- claude_mpm/core/logger.py +125 -8
- claude_mpm/core/logging_config.py +15 -17
- claude_mpm/core/minimal_framework_loader.py +5 -8
- claude_mpm/core/oneshot_session.py +15 -33
- claude_mpm/core/optimized_agent_loader.py +4 -6
- claude_mpm/core/optimized_startup.py +2 -1
- claude_mpm/core/output_style_manager.py +147 -106
- claude_mpm/core/pm_hook_interceptor.py +0 -1
- claude_mpm/core/service_registry.py +11 -8
- claude_mpm/core/session_manager.py +1 -2
- claude_mpm/core/shared/__init__.py +1 -1
- claude_mpm/core/shared/config_loader.py +101 -97
- claude_mpm/core/shared/path_resolver.py +72 -68
- claude_mpm/core/shared/singleton_manager.py +56 -50
- claude_mpm/core/socketio_pool.py +26 -6
- claude_mpm/core/tool_access_control.py +4 -5
- claude_mpm/core/typing_utils.py +50 -59
- claude_mpm/core/unified_agent_registry.py +14 -19
- claude_mpm/core/unified_config.py +4 -6
- claude_mpm/core/unified_paths.py +197 -109
- claude_mpm/dashboard/open_dashboard.py +2 -4
- claude_mpm/experimental/cli_enhancements.py +51 -36
- claude_mpm/generators/agent_profile_generator.py +2 -4
- claude_mpm/hooks/base_hook.py +1 -2
- claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
- claude_mpm/hooks/claude_hooks/event_handlers.py +99 -154
- claude_mpm/hooks/claude_hooks/hook_handler.py +110 -720
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
- claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
- claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
- claude_mpm/hooks/memory_integration_hook.py +5 -5
- claude_mpm/hooks/tool_call_interceptor.py +1 -1
- claude_mpm/hooks/validation_hooks.py +4 -4
- claude_mpm/init.py +4 -9
- claude_mpm/models/__init__.py +2 -2
- claude_mpm/models/agent_session.py +11 -14
- claude_mpm/scripts/mcp_server.py +20 -11
- claude_mpm/scripts/mcp_wrapper.py +5 -5
- claude_mpm/scripts/mpm_doctor.py +321 -0
- claude_mpm/scripts/socketio_daemon.py +28 -25
- claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
- claude_mpm/scripts/socketio_server_manager.py +116 -95
- claude_mpm/services/__init__.py +49 -49
- claude_mpm/services/agent_capabilities_service.py +12 -18
- claude_mpm/services/agents/__init__.py +22 -22
- claude_mpm/services/agents/agent_builder.py +140 -119
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
- claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
- claude_mpm/services/agents/deployment/agent_deployment.py +129 -511
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
- claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
- claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
- claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
- claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
- claude_mpm/services/agents/deployment/agent_validator.py +0 -1
- claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
- claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
- claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
- claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
- claude_mpm/services/agents/deployment/config/__init__.py +1 -1
- claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
- claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
- claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
- claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
- claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
- claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
- claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
- claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
- claude_mpm/services/agents/deployment/results/__init__.py +1 -1
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
- claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
- claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
- claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
- claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
- claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
- claude_mpm/services/agents/loading/__init__.py +1 -1
- claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
- claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
- claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
- claude_mpm/services/agents/management/__init__.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
- claude_mpm/services/agents/management/agent_management_service.py +5 -9
- claude_mpm/services/agents/memory/__init__.py +4 -4
- claude_mpm/services/agents/memory/agent_memory_manager.py +157 -503
- claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
- claude_mpm/services/agents/memory/content_manager.py +44 -38
- claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
- claude_mpm/services/agents/memory/memory_file_service.py +103 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
- claude_mpm/services/agents/memory/template_generator.py +4 -6
- claude_mpm/services/agents/registry/__init__.py +11 -7
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
- claude_mpm/services/agents/registry/modification_tracker.py +3 -6
- claude_mpm/services/async_session_logger.py +1 -2
- claude_mpm/services/claude_session_logger.py +1 -2
- claude_mpm/services/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +407 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +589 -0
- claude_mpm/services/cli/dashboard_launcher.py +424 -0
- claude_mpm/services/cli/memory_crud_service.py +617 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/session_manager.py +513 -0
- claude_mpm/services/cli/socketio_manager.py +498 -0
- claude_mpm/services/cli/startup_checker.py +370 -0
- claude_mpm/services/command_deployment_service.py +173 -0
- claude_mpm/services/command_handler_service.py +20 -22
- claude_mpm/services/core/__init__.py +25 -25
- claude_mpm/services/core/base.py +0 -5
- claude_mpm/services/core/cache_manager.py +311 -0
- claude_mpm/services/core/interfaces/__init__.py +32 -32
- claude_mpm/services/core/interfaces/agent.py +0 -21
- claude_mpm/services/core/interfaces/communication.py +0 -27
- claude_mpm/services/core/interfaces/infrastructure.py +0 -56
- claude_mpm/services/core/interfaces/service.py +0 -29
- claude_mpm/services/core/memory_manager.py +637 -0
- claude_mpm/services/core/path_resolver.py +498 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- claude_mpm/services/diagnostics/__init__.py +1 -1
- claude_mpm/services/diagnostics/checks/__init__.py +6 -6
- claude_mpm/services/diagnostics/checks/agent_check.py +152 -97
- claude_mpm/services/diagnostics/checks/base_check.py +12 -16
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
- claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
- claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
- claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
- claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
- claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
- claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
- claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
- claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
- claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
- claude_mpm/services/diagnostics/models.py +21 -19
- claude_mpm/services/event_aggregator.py +10 -17
- claude_mpm/services/event_bus/__init__.py +1 -1
- claude_mpm/services/event_bus/config.py +54 -35
- claude_mpm/services/event_bus/event_bus.py +76 -71
- claude_mpm/services/event_bus/relay.py +74 -64
- claude_mpm/services/events/__init__.py +11 -11
- claude_mpm/services/events/consumers/__init__.py +3 -3
- claude_mpm/services/events/consumers/dead_letter.py +71 -63
- claude_mpm/services/events/consumers/logging.py +39 -37
- claude_mpm/services/events/consumers/metrics.py +56 -57
- claude_mpm/services/events/consumers/socketio.py +82 -81
- claude_mpm/services/events/core.py +110 -99
- claude_mpm/services/events/interfaces.py +56 -72
- claude_mpm/services/events/producers/__init__.py +1 -1
- claude_mpm/services/events/producers/hook.py +38 -38
- claude_mpm/services/events/producers/system.py +46 -44
- claude_mpm/services/exceptions.py +81 -80
- claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
- claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
- claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
- claude_mpm/services/hook_service.py +6 -9
- claude_mpm/services/infrastructure/__init__.py +1 -1
- claude_mpm/services/infrastructure/context_preservation.py +8 -12
- claude_mpm/services/infrastructure/monitoring.py +21 -23
- claude_mpm/services/mcp_gateway/__init__.py +37 -37
- claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
- claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
- claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
- claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
- claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
- claude_mpm/services/mcp_gateway/core/base.py +0 -3
- claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
- claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
- claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
- claude_mpm/services/mcp_gateway/main.py +2 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
- claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
- claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
- claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
- claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
- claude_mpm/services/memory/__init__.py +3 -3
- claude_mpm/services/memory/builder.py +3 -6
- claude_mpm/services/memory/cache/__init__.py +1 -1
- claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/memory/indexed_memory.py +5 -7
- claude_mpm/services/memory/optimizer.py +7 -10
- claude_mpm/services/memory/router.py +8 -9
- claude_mpm/services/memory_hook_service.py +48 -34
- claude_mpm/services/monitor_build_service.py +77 -73
- claude_mpm/services/port_manager.py +130 -108
- claude_mpm/services/project/analyzer.py +12 -10
- claude_mpm/services/project/registry.py +11 -11
- claude_mpm/services/recovery_manager.py +10 -19
- claude_mpm/services/response_tracker.py +0 -1
- claude_mpm/services/runner_configuration_service.py +19 -20
- claude_mpm/services/session_management_service.py +7 -11
- claude_mpm/services/shared/__init__.py +1 -1
- claude_mpm/services/shared/async_service_base.py +58 -50
- claude_mpm/services/shared/config_service_base.py +73 -67
- claude_mpm/services/shared/lifecycle_service_base.py +82 -78
- claude_mpm/services/shared/manager_base.py +94 -82
- claude_mpm/services/shared/service_factory.py +96 -98
- claude_mpm/services/socketio/__init__.py +3 -3
- claude_mpm/services/socketio/client_proxy.py +5 -5
- claude_mpm/services/socketio/event_normalizer.py +199 -181
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +5 -4
- claude_mpm/services/socketio/handlers/connection.py +163 -136
- claude_mpm/services/socketio/handlers/file.py +13 -14
- claude_mpm/services/socketio/handlers/git.py +12 -7
- claude_mpm/services/socketio/handlers/hook.py +49 -44
- claude_mpm/services/socketio/handlers/memory.py +0 -1
- claude_mpm/services/socketio/handlers/project.py +0 -1
- claude_mpm/services/socketio/handlers/registry.py +37 -19
- claude_mpm/services/socketio/migration_utils.py +98 -84
- claude_mpm/services/socketio/server/__init__.py +1 -1
- claude_mpm/services/socketio/server/broadcaster.py +81 -87
- claude_mpm/services/socketio/server/core.py +65 -54
- claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
- claude_mpm/services/socketio/server/main.py +64 -38
- claude_mpm/services/socketio_client_manager.py +10 -12
- claude_mpm/services/subprocess_launcher_service.py +4 -7
- claude_mpm/services/system_instructions_service.py +13 -14
- claude_mpm/services/ticket_manager.py +2 -2
- claude_mpm/services/utility_service.py +5 -13
- claude_mpm/services/version_control/__init__.py +16 -16
- claude_mpm/services/version_control/branch_strategy.py +5 -8
- claude_mpm/services/version_control/conflict_resolution.py +9 -23
- claude_mpm/services/version_control/git_operations.py +5 -7
- claude_mpm/services/version_control/semantic_versioning.py +16 -17
- claude_mpm/services/version_control/version_parser.py +13 -18
- claude_mpm/services/version_service.py +10 -11
- claude_mpm/storage/__init__.py +1 -1
- claude_mpm/storage/state_storage.py +22 -28
- claude_mpm/utils/__init__.py +6 -6
- claude_mpm/utils/agent_dependency_loader.py +47 -33
- claude_mpm/utils/config_manager.py +11 -14
- claude_mpm/utils/dependency_cache.py +1 -1
- claude_mpm/utils/dependency_manager.py +13 -17
- claude_mpm/utils/dependency_strategies.py +8 -10
- claude_mpm/utils/environment_context.py +3 -9
- claude_mpm/utils/error_handler.py +3 -13
- claude_mpm/utils/file_utils.py +1 -1
- claude_mpm/utils/path_operations.py +8 -12
- claude_mpm/utils/robust_installer.py +110 -33
- claude_mpm/utils/subprocess_utils.py +5 -6
- claude_mpm/validation/agent_validator.py +3 -6
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
- claude_mpm-4.1.3.dist-info/RECORD +528 -0
- claude_mpm/cli/commands/run_config_checker.py +0 -160
- claude_mpm-4.1.1.dist-info/RECORD +0 -494
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""Duplicate event detection service for Claude hook handler.
|
|
2
|
+
|
|
3
|
+
This service manages:
|
|
4
|
+
- Event key generation
|
|
5
|
+
- Duplicate event detection within time windows
|
|
6
|
+
- Recent event tracking
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import threading
|
|
10
|
+
import time
|
|
11
|
+
from collections import deque
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DuplicateEventDetector:
|
|
15
|
+
"""Detects and filters duplicate events."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self, max_recent_events: int = 10, duplicate_window_seconds: float = 0.1
|
|
19
|
+
):
|
|
20
|
+
"""Initialize duplicate event detector.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
max_recent_events: Maximum number of recent events to track
|
|
24
|
+
duplicate_window_seconds: Time window in seconds for duplicate detection
|
|
25
|
+
"""
|
|
26
|
+
# Track recent events to detect duplicates
|
|
27
|
+
self._recent_events = deque(maxlen=max_recent_events)
|
|
28
|
+
self._events_lock = threading.Lock()
|
|
29
|
+
self.duplicate_window_seconds = duplicate_window_seconds
|
|
30
|
+
|
|
31
|
+
def is_duplicate(self, event: dict) -> bool:
|
|
32
|
+
"""Check if an event is a duplicate of a recent event.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
event: The event dictionary to check
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
True if the event is a duplicate, False otherwise
|
|
39
|
+
"""
|
|
40
|
+
event_key = self.generate_event_key(event)
|
|
41
|
+
current_time = time.time()
|
|
42
|
+
|
|
43
|
+
with self._events_lock:
|
|
44
|
+
# Check if we've seen this event recently
|
|
45
|
+
for recent_key, recent_time in self._recent_events:
|
|
46
|
+
if (
|
|
47
|
+
recent_key == event_key
|
|
48
|
+
and (current_time - recent_time) < self.duplicate_window_seconds
|
|
49
|
+
):
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
# Not a duplicate, record it
|
|
53
|
+
self._recent_events.append((event_key, current_time))
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
def generate_event_key(self, event: dict) -> str:
|
|
57
|
+
"""Generate a unique key for an event to detect duplicates.
|
|
58
|
+
|
|
59
|
+
WHY: Claude Code may call the hook multiple times for the same event
|
|
60
|
+
because the hook is registered for multiple event types. We need to
|
|
61
|
+
detect and skip duplicate processing while still returning continue.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
event: The event dictionary
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
A unique string key for the event
|
|
68
|
+
"""
|
|
69
|
+
# Create a key from event type, session_id, and key data
|
|
70
|
+
hook_type = event.get("hook_event_name", "unknown")
|
|
71
|
+
session_id = event.get("session_id", "")
|
|
72
|
+
|
|
73
|
+
# Add type-specific data to make the key unique
|
|
74
|
+
if hook_type == "PreToolUse":
|
|
75
|
+
tool_name = event.get("tool_name", "")
|
|
76
|
+
# For some tools, include parameters to distinguish calls
|
|
77
|
+
if tool_name == "Task":
|
|
78
|
+
tool_input = event.get("tool_input", {})
|
|
79
|
+
agent = tool_input.get("subagent_type", "")
|
|
80
|
+
prompt_preview = (
|
|
81
|
+
tool_input.get("prompt", "") or tool_input.get("description", "")
|
|
82
|
+
)[:50]
|
|
83
|
+
return f"{hook_type}:{session_id}:{tool_name}:{agent}:{prompt_preview}"
|
|
84
|
+
return f"{hook_type}:{session_id}:{tool_name}"
|
|
85
|
+
|
|
86
|
+
if hook_type == "UserPromptSubmit":
|
|
87
|
+
prompt_preview = event.get("prompt", "")[:50]
|
|
88
|
+
return f"{hook_type}:{session_id}:{prompt_preview}"
|
|
89
|
+
|
|
90
|
+
# For other events, just use type and session
|
|
91
|
+
return f"{hook_type}:{session_id}"
|
|
92
|
+
|
|
93
|
+
def clear_old_events(self):
|
|
94
|
+
"""Clear events older than the duplicate window."""
|
|
95
|
+
current_time = time.time()
|
|
96
|
+
cutoff_time = current_time - self.duplicate_window_seconds
|
|
97
|
+
|
|
98
|
+
with self._events_lock:
|
|
99
|
+
# Create a new deque with only recent events
|
|
100
|
+
recent_only = deque(
|
|
101
|
+
(key, timestamp)
|
|
102
|
+
for key, timestamp in self._recent_events
|
|
103
|
+
if timestamp > cutoff_time
|
|
104
|
+
)
|
|
105
|
+
recent_only.maxlen = self._recent_events.maxlen
|
|
106
|
+
self._recent_events = recent_only
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"""State management service for Claude hook handler.
|
|
2
|
+
|
|
3
|
+
This service manages:
|
|
4
|
+
- Agent delegation tracking
|
|
5
|
+
- Git branch caching
|
|
6
|
+
- Session state management
|
|
7
|
+
- Cleanup of old entries
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
import time
|
|
13
|
+
from collections import deque
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
# Import constants for configuration
|
|
18
|
+
try:
|
|
19
|
+
from claude_mpm.core.constants import TimeoutConfig
|
|
20
|
+
except ImportError:
|
|
21
|
+
# Fallback values if constants module not available
|
|
22
|
+
class TimeoutConfig:
|
|
23
|
+
QUICK_TIMEOUT = 2.0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Debug mode is enabled by default for better visibility into hook processing
|
|
27
|
+
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class StateManagerService:
|
|
31
|
+
"""Manages state for the Claude hook handler."""
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
"""Initialize state management service."""
|
|
35
|
+
# Maximum sizes for tracking
|
|
36
|
+
self.MAX_DELEGATION_TRACKING = 200
|
|
37
|
+
self.MAX_PROMPT_TRACKING = 100
|
|
38
|
+
self.MAX_CACHE_AGE_SECONDS = 300
|
|
39
|
+
self.CLEANUP_INTERVAL_EVENTS = 100
|
|
40
|
+
|
|
41
|
+
# Agent delegation tracking
|
|
42
|
+
# Store recent Task delegations: session_id -> agent_type
|
|
43
|
+
self.active_delegations = {}
|
|
44
|
+
# Use deque to limit memory usage (keep last 100 delegations)
|
|
45
|
+
self.delegation_history = deque(maxlen=100)
|
|
46
|
+
# Store delegation request data for response correlation: session_id -> request_data
|
|
47
|
+
self.delegation_requests = {}
|
|
48
|
+
|
|
49
|
+
# Git branch cache (to avoid repeated subprocess calls)
|
|
50
|
+
self._git_branch_cache = {}
|
|
51
|
+
self._git_branch_cache_time = {}
|
|
52
|
+
|
|
53
|
+
# Store current user prompts for comprehensive response tracking
|
|
54
|
+
self.pending_prompts = {} # session_id -> prompt data
|
|
55
|
+
|
|
56
|
+
# Track events for periodic cleanup
|
|
57
|
+
self.events_processed = 0
|
|
58
|
+
self.last_cleanup = time.time()
|
|
59
|
+
|
|
60
|
+
def track_delegation(
|
|
61
|
+
self, session_id: str, agent_type: str, request_data: Optional[dict] = None
|
|
62
|
+
):
|
|
63
|
+
"""Track a new agent delegation with optional request data for response correlation."""
|
|
64
|
+
if DEBUG:
|
|
65
|
+
import sys
|
|
66
|
+
|
|
67
|
+
print(
|
|
68
|
+
f" - session_id: {session_id[:16] if session_id else 'None'}...",
|
|
69
|
+
file=sys.stderr,
|
|
70
|
+
)
|
|
71
|
+
print(f" - agent_type: {agent_type}", file=sys.stderr)
|
|
72
|
+
print(f" - request_data provided: {bool(request_data)}", file=sys.stderr)
|
|
73
|
+
print(
|
|
74
|
+
f" - delegation_requests size before: {len(self.delegation_requests)}",
|
|
75
|
+
file=sys.stderr,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if session_id and agent_type and agent_type != "unknown":
|
|
79
|
+
self.active_delegations[session_id] = agent_type
|
|
80
|
+
key = f"{session_id}:{datetime.now().timestamp()}"
|
|
81
|
+
self.delegation_history.append((key, agent_type))
|
|
82
|
+
|
|
83
|
+
# Store request data for response tracking correlation
|
|
84
|
+
if request_data:
|
|
85
|
+
self.delegation_requests[session_id] = {
|
|
86
|
+
"agent_type": agent_type,
|
|
87
|
+
"request": request_data,
|
|
88
|
+
"timestamp": datetime.now().isoformat(),
|
|
89
|
+
}
|
|
90
|
+
if DEBUG:
|
|
91
|
+
import sys
|
|
92
|
+
|
|
93
|
+
print(
|
|
94
|
+
f" - ✅ Stored in delegation_requests[{session_id[:16]}...]",
|
|
95
|
+
file=sys.stderr,
|
|
96
|
+
)
|
|
97
|
+
print(
|
|
98
|
+
f" - delegation_requests size after: {len(self.delegation_requests)}",
|
|
99
|
+
file=sys.stderr,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Clean up old delegations (older than 5 minutes)
|
|
103
|
+
cutoff_time = datetime.now().timestamp() - 300
|
|
104
|
+
keys_to_remove = []
|
|
105
|
+
for sid in list(self.active_delegations.keys()):
|
|
106
|
+
# Check if this is an old entry by looking in history
|
|
107
|
+
found_recent = False
|
|
108
|
+
for hist_key, _ in reversed(self.delegation_history):
|
|
109
|
+
if hist_key.startswith(sid):
|
|
110
|
+
_, timestamp = hist_key.split(":", 1)
|
|
111
|
+
if float(timestamp) > cutoff_time:
|
|
112
|
+
found_recent = True
|
|
113
|
+
break
|
|
114
|
+
if not found_recent:
|
|
115
|
+
keys_to_remove.append(sid)
|
|
116
|
+
|
|
117
|
+
for key in keys_to_remove:
|
|
118
|
+
if key in self.active_delegations:
|
|
119
|
+
del self.active_delegations[key]
|
|
120
|
+
if key in self.delegation_requests:
|
|
121
|
+
del self.delegation_requests[key]
|
|
122
|
+
|
|
123
|
+
def get_delegation_agent_type(self, session_id: str) -> str:
|
|
124
|
+
"""Get the agent type for a session's active delegation."""
|
|
125
|
+
# First try exact session match
|
|
126
|
+
if session_id and session_id in self.active_delegations:
|
|
127
|
+
return self.active_delegations[session_id]
|
|
128
|
+
|
|
129
|
+
# Then try to find in recent history
|
|
130
|
+
if session_id:
|
|
131
|
+
for key, agent_type in reversed(self.delegation_history):
|
|
132
|
+
if key.startswith(session_id):
|
|
133
|
+
return agent_type
|
|
134
|
+
|
|
135
|
+
return "unknown"
|
|
136
|
+
|
|
137
|
+
def cleanup_old_entries(self):
|
|
138
|
+
"""Clean up old entries to prevent memory growth."""
|
|
139
|
+
datetime.now().timestamp() - self.MAX_CACHE_AGE_SECONDS
|
|
140
|
+
|
|
141
|
+
# Clean up delegation tracking dictionaries
|
|
142
|
+
for storage in [self.active_delegations, self.delegation_requests]:
|
|
143
|
+
if len(storage) > self.MAX_DELEGATION_TRACKING:
|
|
144
|
+
# Keep only the most recent entries
|
|
145
|
+
sorted_keys = sorted(storage.keys())
|
|
146
|
+
excess = len(storage) - self.MAX_DELEGATION_TRACKING
|
|
147
|
+
for key in sorted_keys[:excess]:
|
|
148
|
+
del storage[key]
|
|
149
|
+
|
|
150
|
+
# Clean up pending prompts
|
|
151
|
+
if len(self.pending_prompts) > self.MAX_PROMPT_TRACKING:
|
|
152
|
+
sorted_keys = sorted(self.pending_prompts.keys())
|
|
153
|
+
excess = len(self.pending_prompts) - self.MAX_PROMPT_TRACKING
|
|
154
|
+
for key in sorted_keys[:excess]:
|
|
155
|
+
del self.pending_prompts[key]
|
|
156
|
+
|
|
157
|
+
# Clean up git branch cache
|
|
158
|
+
expired_keys = [
|
|
159
|
+
key
|
|
160
|
+
for key, cache_time in self._git_branch_cache_time.items()
|
|
161
|
+
if datetime.now().timestamp() - cache_time > self.MAX_CACHE_AGE_SECONDS
|
|
162
|
+
]
|
|
163
|
+
for key in expired_keys:
|
|
164
|
+
self._git_branch_cache.pop(key, None)
|
|
165
|
+
self._git_branch_cache_time.pop(key, None)
|
|
166
|
+
|
|
167
|
+
def get_git_branch(self, working_dir: Optional[str] = None) -> str:
|
|
168
|
+
"""Get git branch for the given directory with caching.
|
|
169
|
+
|
|
170
|
+
WHY caching approach:
|
|
171
|
+
- Avoids repeated subprocess calls which are expensive
|
|
172
|
+
- Caches results for 30 seconds per directory
|
|
173
|
+
- Falls back gracefully if git command fails
|
|
174
|
+
- Returns 'Unknown' for non-git directories
|
|
175
|
+
"""
|
|
176
|
+
# Use current working directory if not specified
|
|
177
|
+
if not working_dir:
|
|
178
|
+
working_dir = os.getcwd()
|
|
179
|
+
|
|
180
|
+
# Check cache first (cache for 30 seconds)
|
|
181
|
+
current_time = datetime.now().timestamp()
|
|
182
|
+
cache_key = working_dir
|
|
183
|
+
|
|
184
|
+
if (
|
|
185
|
+
cache_key in self._git_branch_cache
|
|
186
|
+
and cache_key in self._git_branch_cache_time
|
|
187
|
+
and current_time - self._git_branch_cache_time[cache_key] < 30
|
|
188
|
+
):
|
|
189
|
+
return self._git_branch_cache[cache_key]
|
|
190
|
+
|
|
191
|
+
# Try to get git branch
|
|
192
|
+
try:
|
|
193
|
+
# Change to the working directory temporarily
|
|
194
|
+
original_cwd = os.getcwd()
|
|
195
|
+
os.chdir(working_dir)
|
|
196
|
+
|
|
197
|
+
# Run git command to get current branch
|
|
198
|
+
result = subprocess.run(
|
|
199
|
+
["git", "branch", "--show-current"],
|
|
200
|
+
capture_output=True,
|
|
201
|
+
text=True,
|
|
202
|
+
timeout=TimeoutConfig.QUICK_TIMEOUT,
|
|
203
|
+
check=False, # Quick timeout to avoid hanging
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Restore original directory
|
|
207
|
+
os.chdir(original_cwd)
|
|
208
|
+
|
|
209
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
210
|
+
branch = result.stdout.strip()
|
|
211
|
+
# Cache the result
|
|
212
|
+
self._git_branch_cache[cache_key] = branch
|
|
213
|
+
self._git_branch_cache_time[cache_key] = current_time
|
|
214
|
+
return branch
|
|
215
|
+
# Not a git repository or no branch
|
|
216
|
+
self._git_branch_cache[cache_key] = "Unknown"
|
|
217
|
+
self._git_branch_cache_time[cache_key] = current_time
|
|
218
|
+
return "Unknown"
|
|
219
|
+
|
|
220
|
+
except (
|
|
221
|
+
subprocess.TimeoutExpired,
|
|
222
|
+
subprocess.CalledProcessError,
|
|
223
|
+
FileNotFoundError,
|
|
224
|
+
OSError,
|
|
225
|
+
):
|
|
226
|
+
# Git not available or command failed
|
|
227
|
+
self._git_branch_cache[cache_key] = "Unknown"
|
|
228
|
+
self._git_branch_cache_time[cache_key] = current_time
|
|
229
|
+
return "Unknown"
|
|
230
|
+
|
|
231
|
+
def find_matching_request(self, session_id: str) -> Optional[dict]:
|
|
232
|
+
"""Find matching request data for a session, with fuzzy matching fallback."""
|
|
233
|
+
# First try exact match
|
|
234
|
+
request_info = self.delegation_requests.get(session_id)
|
|
235
|
+
|
|
236
|
+
# If exact match fails, try partial matching
|
|
237
|
+
if not request_info and session_id:
|
|
238
|
+
if DEBUG:
|
|
239
|
+
import sys
|
|
240
|
+
|
|
241
|
+
print(
|
|
242
|
+
f" - Trying fuzzy match for session {session_id[:16]}...",
|
|
243
|
+
file=sys.stderr,
|
|
244
|
+
)
|
|
245
|
+
# Try to find a session that matches the first 8-16 characters
|
|
246
|
+
for stored_sid in list(self.delegation_requests.keys()):
|
|
247
|
+
if (
|
|
248
|
+
stored_sid.startswith(session_id[:8])
|
|
249
|
+
or session_id.startswith(stored_sid[:8])
|
|
250
|
+
or (
|
|
251
|
+
len(session_id) >= 16
|
|
252
|
+
and len(stored_sid) >= 16
|
|
253
|
+
and stored_sid[:16] == session_id[:16]
|
|
254
|
+
)
|
|
255
|
+
):
|
|
256
|
+
if DEBUG:
|
|
257
|
+
import sys
|
|
258
|
+
|
|
259
|
+
print(
|
|
260
|
+
f" - ✅ Fuzzy match found: {stored_sid[:16]}...",
|
|
261
|
+
file=sys.stderr,
|
|
262
|
+
)
|
|
263
|
+
request_info = self.delegation_requests.get(stored_sid)
|
|
264
|
+
# Update the key to use the current session_id for consistency
|
|
265
|
+
if request_info:
|
|
266
|
+
self.delegation_requests[session_id] = request_info
|
|
267
|
+
# Optionally remove the old key to avoid duplicates
|
|
268
|
+
if stored_sid != session_id:
|
|
269
|
+
del self.delegation_requests[stored_sid]
|
|
270
|
+
break
|
|
271
|
+
|
|
272
|
+
return request_info
|
|
273
|
+
|
|
274
|
+
def remove_request(self, session_id: str):
|
|
275
|
+
"""Remove request data for a session."""
|
|
276
|
+
if session_id in self.delegation_requests:
|
|
277
|
+
del self.delegation_requests[session_id]
|
|
278
|
+
|
|
279
|
+
def increment_events_processed(self) -> bool:
|
|
280
|
+
"""Increment events processed counter and return True if cleanup is needed."""
|
|
281
|
+
self.events_processed += 1
|
|
282
|
+
return self.events_processed % self.CLEANUP_INTERVAL_EVENTS == 0
|