claude-mpm 4.1.1__py3-none-any.whl → 4.1.2__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/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 +271 -268
- 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 +192 -141
- claude_mpm/cli/commands/monitor.py +117 -88
- claude_mpm/cli/commands/run.py +120 -84
- claude_mpm/cli/commands/run_config_checker.py +4 -5
- 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 +204 -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 +20 -23
- 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 +581 -280
- 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 -15
- 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 +93 -38
- claude_mpm/hooks/claude_hooks/hook_handler.py +130 -76
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
- 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/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 +136 -106
- 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/config/__init__.py +1 -1
- claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
- 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/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 +280 -160
- 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/template_generator.py +4 -6
- claude_mpm/services/agents/registry/__init__.py +10 -6
- 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/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/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/diagnostics/__init__.py +1 -1
- claude_mpm/services/diagnostics/checks/__init__.py +6 -6
- claude_mpm/services/diagnostics/checks/agent_check.py +89 -80
- 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.2.dist-info}/METADATA +1 -1
- claude_mpm-4.1.2.dist-info/RECORD +498 -0
- claude_mpm-4.1.1.dist-info/RECORD +0 -494
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/top_level.txt +0 -0
|
@@ -24,6 +24,7 @@ import time
|
|
|
24
24
|
from collections import deque
|
|
25
25
|
from datetime import datetime
|
|
26
26
|
from pathlib import Path
|
|
27
|
+
from typing import Optional
|
|
27
28
|
|
|
28
29
|
# Add parent path for imports
|
|
29
30
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
|
|
@@ -31,6 +32,7 @@ sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
|
|
|
31
32
|
# Import EventBus
|
|
32
33
|
try:
|
|
33
34
|
from claude_mpm.services.event_bus import EventBus
|
|
35
|
+
|
|
34
36
|
EVENTBUS_AVAILABLE = True
|
|
35
37
|
except ImportError:
|
|
36
38
|
EVENTBUS_AVAILABLE = False
|
|
@@ -44,16 +46,23 @@ except ImportError:
|
|
|
44
46
|
class EventNormalizer:
|
|
45
47
|
def normalize(self, event_data, source="hook"):
|
|
46
48
|
"""Simple fallback normalizer that returns event as-is."""
|
|
47
|
-
return type(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
return type(
|
|
50
|
+
"NormalizedEvent",
|
|
51
|
+
(),
|
|
52
|
+
{
|
|
53
|
+
"to_dict": lambda: {
|
|
54
|
+
"event": "claude_event",
|
|
55
|
+
"type": event_data.get("type", "unknown"),
|
|
56
|
+
"subtype": event_data.get("subtype", "generic"),
|
|
57
|
+
"timestamp": event_data.get(
|
|
58
|
+
"timestamp", datetime.now().isoformat()
|
|
59
|
+
),
|
|
60
|
+
"data": event_data.get("data", event_data),
|
|
61
|
+
"source": source,
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
|
|
57
66
|
|
|
58
67
|
# Import constants for configuration
|
|
59
68
|
try:
|
|
@@ -63,16 +72,17 @@ except ImportError:
|
|
|
63
72
|
class TimeoutConfig:
|
|
64
73
|
QUICK_TIMEOUT = 2.0
|
|
65
74
|
|
|
75
|
+
|
|
66
76
|
# Import other handler modules
|
|
67
77
|
try:
|
|
78
|
+
from .event_handlers import EventHandlers
|
|
68
79
|
from .memory_integration import MemoryHookManager
|
|
69
80
|
from .response_tracking import ResponseTrackingManager
|
|
70
|
-
from .event_handlers import EventHandlers
|
|
71
81
|
except ImportError:
|
|
72
82
|
# Fallback for direct execution
|
|
83
|
+
from event_handlers import EventHandlers
|
|
73
84
|
from memory_integration import MemoryHookManager
|
|
74
85
|
from response_tracking import ResponseTrackingManager
|
|
75
|
-
from event_handlers import EventHandlers
|
|
76
86
|
|
|
77
87
|
# Debug mode is enabled by default for better visibility into hook processing
|
|
78
88
|
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
@@ -88,74 +98,77 @@ _events_lock = threading.Lock()
|
|
|
88
98
|
|
|
89
99
|
class HookHandler:
|
|
90
100
|
"""Main hook handler class using EventBus for event emission.
|
|
91
|
-
|
|
101
|
+
|
|
92
102
|
WHY EventBus integration:
|
|
93
103
|
- Replaces direct Socket.IO connections with EventBus publishing
|
|
94
104
|
- Events are published once and consumed by multiple listeners
|
|
95
105
|
- Failures in one consumer don't affect others
|
|
96
106
|
- Simplified testing without Socket.IO dependencies
|
|
97
107
|
"""
|
|
98
|
-
|
|
108
|
+
|
|
99
109
|
# Tracking dictionaries with size limits
|
|
100
110
|
MAX_DELEGATION_TRACKING = 100
|
|
101
111
|
MAX_PROMPT_TRACKING = 50
|
|
102
112
|
MAX_CACHE_AGE_SECONDS = 1800 # 30 minutes
|
|
103
|
-
|
|
113
|
+
|
|
104
114
|
def __init__(self):
|
|
105
115
|
"""Initialize the hook handler with EventBus."""
|
|
106
116
|
# Initialize EventBus if available
|
|
107
117
|
self.event_bus = EventBus.get_instance() if EVENTBUS_AVAILABLE else None
|
|
108
118
|
self.event_normalizer = EventNormalizer()
|
|
109
|
-
|
|
119
|
+
|
|
110
120
|
# Initialize tracking managers
|
|
111
121
|
self.memory_manager = MemoryHookManager()
|
|
112
122
|
self.response_tracker = ResponseTrackingManager()
|
|
113
123
|
self.event_handlers = EventHandlers(self)
|
|
114
|
-
|
|
124
|
+
|
|
115
125
|
# Delegation tracking
|
|
116
126
|
self.active_delegations = {}
|
|
117
127
|
self.delegation_requests = {}
|
|
118
128
|
self.delegation_history = deque(maxlen=20)
|
|
119
|
-
|
|
129
|
+
|
|
120
130
|
# Prompt tracking
|
|
121
131
|
self.pending_prompts = {}
|
|
122
|
-
|
|
132
|
+
|
|
123
133
|
# Git branch caching
|
|
124
134
|
self._git_branch_cache = {}
|
|
125
135
|
self._git_branch_cache_time = {}
|
|
126
|
-
|
|
136
|
+
|
|
127
137
|
# Session tracking
|
|
128
138
|
self.current_session_id = None
|
|
129
|
-
|
|
139
|
+
|
|
130
140
|
# Cleanup old entries periodically
|
|
131
141
|
self._last_cleanup = time.time()
|
|
132
|
-
|
|
142
|
+
|
|
133
143
|
if self.event_bus:
|
|
134
144
|
logger_msg = "HookHandler initialized with EventBus"
|
|
135
145
|
else:
|
|
136
146
|
logger_msg = "HookHandler initialized (EventBus not available)"
|
|
137
|
-
|
|
147
|
+
|
|
138
148
|
if DEBUG:
|
|
139
149
|
print(f"🚀 {logger_msg}", file=sys.stderr)
|
|
140
|
-
|
|
150
|
+
|
|
141
151
|
def _emit_event(self, event_type: str, data: dict):
|
|
142
152
|
"""Emit an event through the EventBus.
|
|
143
|
-
|
|
153
|
+
|
|
144
154
|
WHY this approach:
|
|
145
155
|
- Single point of event emission
|
|
146
156
|
- Consistent event normalization
|
|
147
157
|
- Graceful fallback if EventBus unavailable
|
|
148
158
|
- Easy to add metrics and monitoring
|
|
149
|
-
|
|
159
|
+
|
|
150
160
|
Args:
|
|
151
161
|
event_type: The event type (e.g., 'pre_tool', 'subagent_stop')
|
|
152
162
|
data: The event data
|
|
153
163
|
"""
|
|
154
164
|
if not self.event_bus:
|
|
155
165
|
if DEBUG:
|
|
156
|
-
print(
|
|
166
|
+
print(
|
|
167
|
+
f"EventBus not available, cannot emit: hook.{event_type}",
|
|
168
|
+
file=sys.stderr,
|
|
169
|
+
)
|
|
157
170
|
return
|
|
158
|
-
|
|
171
|
+
|
|
159
172
|
try:
|
|
160
173
|
# Create event data for normalization
|
|
161
174
|
raw_event = {
|
|
@@ -164,86 +177,99 @@ class HookHandler:
|
|
|
164
177
|
"timestamp": datetime.now().isoformat(),
|
|
165
178
|
"data": data,
|
|
166
179
|
"source": "claude_hooks",
|
|
167
|
-
"session_id": data.get("sessionId", self.current_session_id)
|
|
180
|
+
"session_id": data.get("sessionId", self.current_session_id),
|
|
168
181
|
}
|
|
169
|
-
|
|
182
|
+
|
|
170
183
|
# Normalize the event
|
|
171
184
|
normalized_event = self.event_normalizer.normalize(raw_event, source="hook")
|
|
172
185
|
event_data = normalized_event.to_dict()
|
|
173
|
-
|
|
186
|
+
|
|
174
187
|
# Publish to EventBus
|
|
175
188
|
success = self.event_bus.publish(f"hook.{event_type}", event_data)
|
|
176
|
-
|
|
189
|
+
|
|
177
190
|
if DEBUG:
|
|
178
191
|
if success:
|
|
179
|
-
print(
|
|
192
|
+
print(
|
|
193
|
+
f"✅ Published to EventBus: hook.{event_type}", file=sys.stderr
|
|
194
|
+
)
|
|
180
195
|
else:
|
|
181
|
-
print(
|
|
182
|
-
|
|
196
|
+
print(
|
|
197
|
+
f"⚠️ EventBus rejected event: hook.{event_type}", file=sys.stderr
|
|
198
|
+
)
|
|
199
|
+
|
|
183
200
|
# Log important events
|
|
184
201
|
if DEBUG and event_type in ["subagent_stop", "pre_tool"]:
|
|
185
202
|
if event_type == "subagent_stop":
|
|
186
203
|
agent_type = data.get("agent_type", "unknown")
|
|
187
|
-
print(
|
|
204
|
+
print(
|
|
205
|
+
f"📤 Published SubagentStop for agent '{agent_type}'",
|
|
206
|
+
file=sys.stderr,
|
|
207
|
+
)
|
|
188
208
|
elif event_type == "pre_tool" and data.get("tool_name") == "Task":
|
|
189
209
|
delegation = data.get("delegation_details", {})
|
|
190
210
|
agent_type = delegation.get("agent_type", "unknown")
|
|
191
|
-
print(
|
|
192
|
-
|
|
211
|
+
print(
|
|
212
|
+
f"📤 Published Task delegation to agent '{agent_type}'",
|
|
213
|
+
file=sys.stderr,
|
|
214
|
+
)
|
|
215
|
+
|
|
193
216
|
except Exception as e:
|
|
194
217
|
if DEBUG:
|
|
195
|
-
print(
|
|
196
|
-
|
|
197
|
-
|
|
218
|
+
print(
|
|
219
|
+
f"❌ Failed to publish event hook.{event_type}: {e}",
|
|
220
|
+
file=sys.stderr,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
|
|
198
224
|
"""Get git branch for the given directory with caching."""
|
|
199
225
|
# Use current working directory if not specified
|
|
200
226
|
if not working_dir:
|
|
201
227
|
working_dir = os.getcwd()
|
|
202
|
-
|
|
228
|
+
|
|
203
229
|
# Check cache first (cache for 30 seconds)
|
|
204
230
|
current_time = time.time()
|
|
205
231
|
cache_key = working_dir
|
|
206
|
-
|
|
232
|
+
|
|
207
233
|
if (
|
|
208
234
|
cache_key in self._git_branch_cache
|
|
209
235
|
and cache_key in self._git_branch_cache_time
|
|
210
236
|
and current_time - self._git_branch_cache_time[cache_key] < 30
|
|
211
237
|
):
|
|
212
238
|
return self._git_branch_cache[cache_key]
|
|
213
|
-
|
|
239
|
+
|
|
214
240
|
# Try to get git branch
|
|
215
241
|
try:
|
|
216
242
|
# Change to the working directory temporarily
|
|
217
243
|
original_cwd = os.getcwd()
|
|
218
244
|
os.chdir(working_dir)
|
|
219
|
-
|
|
245
|
+
|
|
220
246
|
# Run git command to get current branch
|
|
221
247
|
result = subprocess.run(
|
|
222
248
|
["git", "branch", "--show-current"],
|
|
223
249
|
capture_output=True,
|
|
224
250
|
text=True,
|
|
225
|
-
timeout=TimeoutConfig.QUICK_TIMEOUT
|
|
251
|
+
timeout=TimeoutConfig.QUICK_TIMEOUT,
|
|
252
|
+
check=False,
|
|
226
253
|
)
|
|
227
|
-
|
|
254
|
+
|
|
228
255
|
# Restore original directory
|
|
229
256
|
os.chdir(original_cwd)
|
|
230
|
-
|
|
257
|
+
|
|
231
258
|
if result.returncode == 0 and result.stdout.strip():
|
|
232
259
|
branch = result.stdout.strip()
|
|
233
260
|
# Cache the result
|
|
234
261
|
self._git_branch_cache[cache_key] = branch
|
|
235
262
|
self._git_branch_cache_time[cache_key] = current_time
|
|
236
263
|
return branch
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
264
|
+
return "unknown"
|
|
265
|
+
|
|
240
266
|
except Exception:
|
|
241
267
|
return "unknown"
|
|
242
|
-
|
|
268
|
+
|
|
243
269
|
def _cleanup_old_entries(self):
|
|
244
270
|
"""Clean up old entries to prevent memory growth."""
|
|
245
|
-
|
|
246
|
-
|
|
271
|
+
time.time() - self.MAX_CACHE_AGE_SECONDS
|
|
272
|
+
|
|
247
273
|
# Clean up delegation tracking dictionaries
|
|
248
274
|
for storage in [self.active_delegations, self.delegation_requests]:
|
|
249
275
|
if len(storage) > self.MAX_DELEGATION_TRACKING:
|
|
@@ -252,14 +278,14 @@ class HookHandler:
|
|
|
252
278
|
excess = len(storage) - self.MAX_DELEGATION_TRACKING
|
|
253
279
|
for key in sorted_keys[:excess]:
|
|
254
280
|
del storage[key]
|
|
255
|
-
|
|
281
|
+
|
|
256
282
|
# Clean up pending prompts
|
|
257
283
|
if len(self.pending_prompts) > self.MAX_PROMPT_TRACKING:
|
|
258
284
|
sorted_keys = sorted(self.pending_prompts.keys())
|
|
259
285
|
excess = len(self.pending_prompts) - self.MAX_PROMPT_TRACKING
|
|
260
286
|
for key in sorted_keys[:excess]:
|
|
261
287
|
del self.pending_prompts[key]
|
|
262
|
-
|
|
288
|
+
|
|
263
289
|
# Clean up git branch cache
|
|
264
290
|
expired_keys = [
|
|
265
291
|
key
|
|
@@ -269,10 +295,10 @@ class HookHandler:
|
|
|
269
295
|
for key in expired_keys:
|
|
270
296
|
self._git_branch_cache.pop(key, None)
|
|
271
297
|
self._git_branch_cache_time.pop(key, None)
|
|
272
|
-
|
|
298
|
+
|
|
273
299
|
def handle_event(self, event: dict):
|
|
274
300
|
"""Process an event from Claude Code.
|
|
275
|
-
|
|
301
|
+
|
|
276
302
|
Args:
|
|
277
303
|
event: The event dictionary from Claude
|
|
278
304
|
"""
|
|
@@ -281,24 +307,26 @@ class HookHandler:
|
|
|
281
307
|
if current_time - self._last_cleanup > 300: # Every 5 minutes
|
|
282
308
|
self._cleanup_old_entries()
|
|
283
309
|
self._last_cleanup = current_time
|
|
284
|
-
|
|
310
|
+
|
|
285
311
|
# Extract event details
|
|
286
312
|
event_type = event.get("type", "")
|
|
287
313
|
event_name = event.get("name", "")
|
|
288
|
-
|
|
314
|
+
|
|
289
315
|
# Update session ID if present
|
|
290
316
|
if "sessionId" in event:
|
|
291
317
|
self.current_session_id = event["sessionId"]
|
|
292
|
-
|
|
318
|
+
|
|
293
319
|
# Detect duplicate events
|
|
294
|
-
event_signature =
|
|
320
|
+
event_signature = (
|
|
321
|
+
f"{event_type}:{event_name}:{json.dumps(event.get('data', ''))[:100]}"
|
|
322
|
+
)
|
|
295
323
|
with _events_lock:
|
|
296
324
|
if event_signature in _recent_events:
|
|
297
325
|
if DEBUG:
|
|
298
326
|
print(f"Skipping duplicate event: {event_type}", file=sys.stderr)
|
|
299
327
|
return
|
|
300
328
|
_recent_events.append(event_signature)
|
|
301
|
-
|
|
329
|
+
|
|
302
330
|
# Route to appropriate handler
|
|
303
331
|
if event_type == "Start":
|
|
304
332
|
self.event_handlers.handle_start(event)
|
|
@@ -321,15 +349,14 @@ class HookHandler:
|
|
|
321
349
|
elif event_type == "PromptCachingBetaStats":
|
|
322
350
|
# Ignore caching stats events
|
|
323
351
|
pass
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
print(f"Unhandled event type: {event_type}", file=sys.stderr)
|
|
352
|
+
# Log unhandled events in debug mode
|
|
353
|
+
elif DEBUG:
|
|
354
|
+
print(f"Unhandled event type: {event_type}", file=sys.stderr)
|
|
328
355
|
|
|
329
356
|
|
|
330
357
|
def get_handler() -> HookHandler:
|
|
331
358
|
"""Get or create the global hook handler instance.
|
|
332
|
-
|
|
359
|
+
|
|
333
360
|
Returns:
|
|
334
361
|
HookHandler: The singleton handler instance
|
|
335
362
|
"""
|
|
@@ -345,18 +372,18 @@ def main():
|
|
|
345
372
|
"""Main entry point for the hook handler."""
|
|
346
373
|
if DEBUG:
|
|
347
374
|
print("🎯 EventBus Hook Handler starting...", file=sys.stderr)
|
|
348
|
-
|
|
375
|
+
|
|
349
376
|
handler = get_handler()
|
|
350
|
-
|
|
377
|
+
|
|
351
378
|
# Set up signal handling for clean shutdown
|
|
352
379
|
def signal_handler(signum, frame):
|
|
353
380
|
if DEBUG:
|
|
354
381
|
print("\n👋 Hook handler shutting down...", file=sys.stderr)
|
|
355
382
|
sys.exit(0)
|
|
356
|
-
|
|
383
|
+
|
|
357
384
|
signal.signal(signal.SIGINT, signal_handler)
|
|
358
385
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
359
|
-
|
|
386
|
+
|
|
360
387
|
# Process events from stdin
|
|
361
388
|
try:
|
|
362
389
|
while True:
|
|
@@ -366,15 +393,15 @@ def main():
|
|
|
366
393
|
line = sys.stdin.readline()
|
|
367
394
|
if not line:
|
|
368
395
|
break
|
|
369
|
-
|
|
396
|
+
|
|
370
397
|
try:
|
|
371
398
|
event = json.loads(line.strip())
|
|
372
399
|
handler.handle_event(event)
|
|
373
|
-
|
|
400
|
+
|
|
374
401
|
# Acknowledge event
|
|
375
402
|
print(json.dumps({"status": "ok"}))
|
|
376
403
|
sys.stdout.flush()
|
|
377
|
-
|
|
404
|
+
|
|
378
405
|
except json.JSONDecodeError as e:
|
|
379
406
|
if DEBUG:
|
|
380
407
|
print(f"Invalid JSON: {e}", file=sys.stderr)
|
|
@@ -385,7 +412,7 @@ def main():
|
|
|
385
412
|
print(f"Error processing event: {e}", file=sys.stderr)
|
|
386
413
|
print(json.dumps({"status": "error", "message": str(e)}))
|
|
387
414
|
sys.stdout.flush()
|
|
388
|
-
|
|
415
|
+
|
|
389
416
|
except KeyboardInterrupt:
|
|
390
417
|
if DEBUG:
|
|
391
418
|
print("\n👋 Hook handler interrupted", file=sys.stderr)
|
|
@@ -395,4 +422,4 @@ def main():
|
|
|
395
422
|
|
|
396
423
|
|
|
397
424
|
if __name__ == "__main__":
|
|
398
|
-
main()
|
|
425
|
+
main()
|
|
@@ -8,7 +8,7 @@ including pre and post delegation hooks.
|
|
|
8
8
|
import os
|
|
9
9
|
import sys
|
|
10
10
|
from datetime import datetime
|
|
11
|
-
from typing import
|
|
11
|
+
from typing import Optional
|
|
12
12
|
|
|
13
13
|
# Debug mode
|
|
14
14
|
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
@@ -21,14 +21,12 @@ try:
|
|
|
21
21
|
|
|
22
22
|
paths.ensure_in_path()
|
|
23
23
|
|
|
24
|
-
from claude_mpm.core.config import Config
|
|
25
24
|
from claude_mpm.core.shared.config_loader import ConfigLoader
|
|
26
25
|
from claude_mpm.hooks.base_hook import HookContext, HookType
|
|
27
26
|
from claude_mpm.hooks.memory_integration_hook import (
|
|
28
27
|
MemoryPostDelegationHook,
|
|
29
28
|
MemoryPreDelegationHook,
|
|
30
29
|
)
|
|
31
|
-
from claude_mpm.services.hook_service import HookService
|
|
32
30
|
|
|
33
31
|
MEMORY_HOOKS_AVAILABLE = True
|
|
34
32
|
except Exception as e:
|
|
@@ -181,7 +179,7 @@ class MemoryHookManager:
|
|
|
181
179
|
if output:
|
|
182
180
|
result_content = str(output)
|
|
183
181
|
elif error:
|
|
184
|
-
result_content = f"Error: {
|
|
182
|
+
result_content = f"Error: {error!s}"
|
|
185
183
|
else:
|
|
186
184
|
result_content = f"Task completed with exit code: {exit_code}"
|
|
187
185
|
|
|
@@ -10,7 +10,7 @@ import os
|
|
|
10
10
|
import re
|
|
11
11
|
import sys
|
|
12
12
|
from datetime import datetime
|
|
13
|
-
from typing import
|
|
13
|
+
from typing import Optional
|
|
14
14
|
|
|
15
15
|
# Debug mode
|
|
16
16
|
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "true").lower() != "false"
|
|
@@ -53,7 +53,6 @@ class ResponseTrackingManager:
|
|
|
53
53
|
try:
|
|
54
54
|
# Create configuration with optional config file using ConfigLoader
|
|
55
55
|
config_file = os.environ.get("CLAUDE_PM_CONFIG_FILE")
|
|
56
|
-
from claude_mpm.core.config import Config
|
|
57
56
|
from claude_mpm.core.shared.config_loader import ConfigLoader, ConfigPattern
|
|
58
57
|
|
|
59
58
|
config_loader = ConfigLoader()
|
|
@@ -62,9 +61,11 @@ class ResponseTrackingManager:
|
|
|
62
61
|
pattern = ConfigPattern(
|
|
63
62
|
filenames=[os.path.basename(config_file)],
|
|
64
63
|
search_paths=[os.path.dirname(config_file)],
|
|
65
|
-
env_prefix="CLAUDE_MPM_"
|
|
64
|
+
env_prefix="CLAUDE_MPM_",
|
|
65
|
+
)
|
|
66
|
+
config = config_loader.load_config(
|
|
67
|
+
pattern, cache_key=f"response_tracking_{config_file}"
|
|
66
68
|
)
|
|
67
|
-
config = config_loader.load_config(pattern, cache_key=f"response_tracking_{config_file}")
|
|
68
69
|
else:
|
|
69
70
|
config = config_loader.load_main_config()
|
|
70
71
|
|
|
@@ -101,7 +102,9 @@ class ResponseTrackingManager:
|
|
|
101
102
|
|
|
102
103
|
except Exception as e:
|
|
103
104
|
if DEBUG:
|
|
104
|
-
print(
|
|
105
|
+
print(
|
|
106
|
+
f"❌ Failed to initialize response tracking: {e}", file=sys.stderr
|
|
107
|
+
)
|
|
105
108
|
# Don't fail the entire handler - response tracking is optional
|
|
106
109
|
|
|
107
110
|
def track_agent_response(
|
|
@@ -203,11 +206,13 @@ class ResponseTrackingManager:
|
|
|
203
206
|
"files_modified": structured_response.get("files_modified", []),
|
|
204
207
|
"tools_used": structured_response.get("tools_used", []),
|
|
205
208
|
"remember": structured_response.get("remember"),
|
|
206
|
-
"MEMORIES": structured_response.get(
|
|
209
|
+
"MEMORIES": structured_response.get(
|
|
210
|
+
"MEMORIES"
|
|
211
|
+
), # Complete memory replacement
|
|
207
212
|
}
|
|
208
|
-
|
|
209
|
-
# Log if MEMORIES field is present
|
|
210
|
-
if
|
|
213
|
+
|
|
214
|
+
# Log if MEMORIES field is present
|
|
215
|
+
if structured_response.get("MEMORIES"):
|
|
211
216
|
if DEBUG:
|
|
212
217
|
memories_count = len(structured_response["MEMORIES"])
|
|
213
218
|
print(
|
|
@@ -247,8 +252,7 @@ class ResponseTrackingManager:
|
|
|
247
252
|
)
|
|
248
253
|
|
|
249
254
|
# Clean up the request data after successful tracking
|
|
250
|
-
|
|
251
|
-
del delegation_requests[session_id]
|
|
255
|
+
delegation_requests.pop(session_id, None)
|
|
252
256
|
|
|
253
257
|
except Exception as e:
|
|
254
258
|
if DEBUG:
|
|
@@ -5,8 +5,6 @@ This module provides utilities for analyzing tool usage, extracting parameters,
|
|
|
5
5
|
and assessing security risks.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from typing import Any, Dict, List
|
|
9
|
-
|
|
10
8
|
|
|
11
9
|
def extract_tool_parameters(tool_name: str, tool_input: dict) -> dict:
|
|
12
10
|
"""Extract relevant parameters based on tool type.
|
|
@@ -99,7 +97,7 @@ def extract_tool_parameters(tool_name: str, tool_input: dict) -> dict:
|
|
|
99
97
|
"has_in_progress": any(t.get("status") == "in_progress" for t in todos),
|
|
100
98
|
"has_pending": any(t.get("status") == "pending" for t in todos),
|
|
101
99
|
"has_completed": any(t.get("status") == "completed" for t in todos),
|
|
102
|
-
"priorities": list(
|
|
100
|
+
"priorities": list({t.get("priority", "medium") for t in todos}),
|
|
103
101
|
}
|
|
104
102
|
)
|
|
105
103
|
|
|
@@ -144,18 +142,17 @@ def classify_tool_operation(tool_name: str, tool_input: dict) -> str:
|
|
|
144
142
|
"""Classify the type of operation being performed."""
|
|
145
143
|
if tool_name in ["Read", "LS", "Glob", "Grep", "NotebookRead"]:
|
|
146
144
|
return "read"
|
|
147
|
-
|
|
145
|
+
if tool_name in ["Write", "Edit", "MultiEdit", "NotebookEdit"]:
|
|
148
146
|
return "write"
|
|
149
|
-
|
|
147
|
+
if tool_name == "Bash":
|
|
150
148
|
return "execute"
|
|
151
|
-
|
|
149
|
+
if tool_name in ["WebFetch", "WebSearch"]:
|
|
152
150
|
return "network"
|
|
153
|
-
|
|
151
|
+
if tool_name == "TodoWrite":
|
|
154
152
|
return "task_management"
|
|
155
|
-
|
|
153
|
+
if tool_name == "Task":
|
|
156
154
|
return "delegation"
|
|
157
|
-
|
|
158
|
-
return "other"
|
|
155
|
+
return "other"
|
|
159
156
|
|
|
160
157
|
|
|
161
158
|
def assess_security_risk(tool_name: str, tool_input: dict) -> str:
|
|
@@ -174,21 +171,18 @@ def assess_security_risk(tool_name: str, tool_input: dict) -> str:
|
|
|
174
171
|
]
|
|
175
172
|
if any(pattern in command for pattern in dangerous_patterns):
|
|
176
173
|
return "high"
|
|
177
|
-
|
|
174
|
+
if any(word in command for word in ["install", "delete", "format", "kill"]):
|
|
178
175
|
return "medium"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
elif tool_name in ["Write", "Edit", "MultiEdit"]:
|
|
176
|
+
return "low"
|
|
177
|
+
if tool_name in ["Write", "Edit", "MultiEdit"]:
|
|
182
178
|
file_path = tool_input.get("file_path", "")
|
|
183
179
|
# Check for system file modifications
|
|
184
180
|
if any(path in file_path for path in ["/etc/", "/usr/", "/var/", "/sys/"]):
|
|
185
181
|
return "high"
|
|
186
|
-
|
|
182
|
+
if file_path.startswith("/"):
|
|
187
183
|
return "medium"
|
|
188
|
-
else:
|
|
189
|
-
return "low"
|
|
190
|
-
else:
|
|
191
184
|
return "low"
|
|
185
|
+
return "low"
|
|
192
186
|
|
|
193
187
|
|
|
194
188
|
def extract_tool_results(event: dict) -> dict:
|
|
@@ -13,7 +13,7 @@ agent outputs because:
|
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
import re
|
|
16
|
-
from typing import
|
|
16
|
+
from typing import Dict, List
|
|
17
17
|
|
|
18
18
|
from claude_mpm.core.config import Config
|
|
19
19
|
from claude_mpm.core.logger import get_logger
|
|
@@ -162,7 +162,7 @@ INSTRUCTIONS: Review your memory above before proceeding. Apply learned patterns
|
|
|
162
162
|
success=True,
|
|
163
163
|
data=context.data,
|
|
164
164
|
modified=False,
|
|
165
|
-
error=f"Memory injection failed: {
|
|
165
|
+
error=f"Memory injection failed: {e!s}",
|
|
166
166
|
)
|
|
167
167
|
|
|
168
168
|
|
|
@@ -319,7 +319,7 @@ class MemoryPostDelegationHook(PostDelegationHook):
|
|
|
319
319
|
success=True,
|
|
320
320
|
data=context.data,
|
|
321
321
|
modified=False,
|
|
322
|
-
error=f"Learning extraction failed: {
|
|
322
|
+
error=f"Learning extraction failed: {e!s}",
|
|
323
323
|
)
|
|
324
324
|
|
|
325
325
|
def _extract_learnings(self, text: str) -> Dict[str, List[str]]:
|
|
@@ -339,7 +339,7 @@ class MemoryPostDelegationHook(PostDelegationHook):
|
|
|
339
339
|
Returns:
|
|
340
340
|
Dictionary mapping learning types to lists of extracted learnings
|
|
341
341
|
"""
|
|
342
|
-
learnings = {learning_type: [] for learning_type in self.type_mapping
|
|
342
|
+
learnings = {learning_type: [] for learning_type in self.type_mapping}
|
|
343
343
|
seen_learnings = set() # Avoid duplicates
|
|
344
344
|
|
|
345
345
|
# Pattern to find memory blocks with multiple trigger phrases
|
|
@@ -394,7 +394,7 @@ class MemoryPostDelegationHook(PostDelegationHook):
|
|
|
394
394
|
f"Unsupported learning type: {learning_type}. Supported types: {list(self.type_mapping.keys())}"
|
|
395
395
|
)
|
|
396
396
|
else:
|
|
397
|
-
logger.debug(
|
|
397
|
+
logger.debug("Invalid memory block format - missing Type or Content")
|
|
398
398
|
|
|
399
399
|
# Log summary of extracted learnings
|
|
400
400
|
total_learnings = sum(len(items) for items in learnings.values())
|
|
@@ -6,7 +6,7 @@ from datetime import datetime
|
|
|
6
6
|
from typing import Any, Dict, List, Optional
|
|
7
7
|
|
|
8
8
|
from claude_mpm.core.logger import get_logger
|
|
9
|
-
from claude_mpm.hooks.base_hook import BaseHook, HookContext,
|
|
9
|
+
from claude_mpm.hooks.base_hook import BaseHook, HookContext, HookType
|
|
10
10
|
|
|
11
11
|
logger = get_logger(__name__)
|
|
12
12
|
|