claude-mpm 3.9.9__py3-none-any.whl → 4.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +155 -0
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +90 -49
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +21 -18
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +143 -762
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1150
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +36 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +571 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +40 -23
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +14 -21
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +97 -93
- claude_mpm/services/mcp_gateway/main.py +307 -127
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
- claude_mpm/services/mcp_gateway/server/{mcp_server.py → mcp_gateway.py} +149 -153
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +110 -121
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +20 -534
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +552 -0
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +51 -2
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/memory_guardian.py +0 -770
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.9.dist-info/RECORD +0 -293
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            from pathlib import Path
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            """
         | 
| 2 4 | 
             
            Claude Session Response Logger
         | 
| 3 5 |  | 
| @@ -9,11 +11,10 @@ Configuration via .claude-mpm/configuration.yaml. | |
| 9 11 | 
             
            """
         | 
| 10 12 |  | 
| 11 13 | 
             
            import json
         | 
| 14 | 
            +
            import logging
         | 
| 12 15 | 
             
            import os
         | 
| 13 16 | 
             
            from datetime import datetime
         | 
| 14 | 
            -
            from  | 
| 15 | 
            -
            from typing import Dict, Any, Optional
         | 
| 16 | 
            -
            import logging
         | 
| 17 | 
            +
            from typing import Any, Dict, Optional
         | 
| 17 18 |  | 
| 18 19 | 
             
            # Import configuration manager
         | 
| 19 20 | 
             
            from claude_mpm.core.config import Config
         | 
| @@ -21,10 +22,11 @@ from claude_mpm.core.config import Config | |
| 21 22 | 
             
            # Try to import async logger for performance optimization
         | 
| 22 23 | 
             
            try:
         | 
| 23 24 | 
             
                from claude_mpm.services.async_session_logger import (
         | 
| 25 | 
            +
                    AsyncSessionLogger,
         | 
| 24 26 | 
             
                    get_async_logger,
         | 
| 25 27 | 
             
                    log_response_async,
         | 
| 26 | 
            -
                    AsyncSessionLogger
         | 
| 27 28 | 
             
                )
         | 
| 29 | 
            +
             | 
| 28 30 | 
             
                ASYNC_AVAILABLE = True
         | 
| 29 31 | 
             
            except ImportError:
         | 
| 30 32 | 
             
                ASYNC_AVAILABLE = False
         | 
| @@ -34,11 +36,16 @@ logger = logging.getLogger(__name__) | |
| 34 36 |  | 
| 35 37 | 
             
            class ClaudeSessionLogger:
         | 
| 36 38 | 
             
                """Simplified response logger for Claude Code sessions."""
         | 
| 37 | 
            -
             | 
| 38 | 
            -
                def __init__( | 
| 39 | 
            +
             | 
| 40 | 
            +
                def __init__(
         | 
| 41 | 
            +
                    self,
         | 
| 42 | 
            +
                    base_dir: Optional[Path] = None,
         | 
| 43 | 
            +
                    use_async: Optional[bool] = None,
         | 
| 44 | 
            +
                    config: Optional[Config] = None,
         | 
| 45 | 
            +
                ):
         | 
| 39 46 | 
             
                    """
         | 
| 40 47 | 
             
                    Initialize the session logger.
         | 
| 41 | 
            -
             | 
| 48 | 
            +
             | 
| 42 49 | 
             
                    Args:
         | 
| 43 50 | 
             
                        base_dir: Base directory for responses. Overrides config.
         | 
| 44 51 | 
             
                        use_async: Use async logging if available. Overrides config.
         | 
| @@ -48,92 +55,98 @@ class ClaudeSessionLogger: | |
| 48 55 | 
             
                    if config is None:
         | 
| 49 56 | 
             
                        config = Config()
         | 
| 50 57 | 
             
                    self.config = config
         | 
| 51 | 
            -
             | 
| 58 | 
            +
             | 
| 52 59 | 
             
                    # Get response logging configuration
         | 
| 53 | 
            -
                    response_config = self.config.get( | 
| 54 | 
            -
             | 
| 60 | 
            +
                    response_config = self.config.get("response_logging", {})
         | 
| 61 | 
            +
             | 
| 55 62 | 
             
                    # Determine base directory
         | 
| 56 63 | 
             
                    if base_dir is None:
         | 
| 57 64 | 
             
                        # Check configuration first
         | 
| 58 | 
            -
                        base_dir = response_config.get( | 
| 65 | 
            +
                        base_dir = response_config.get("session_directory")
         | 
| 59 66 | 
             
                        if not base_dir:
         | 
| 60 67 | 
             
                            # Fall back to default response directory
         | 
| 61 | 
            -
                            base_dir =  | 
| 68 | 
            +
                            base_dir = ".claude-mpm/responses"
         | 
| 62 69 | 
             
                        base_dir = Path(base_dir)
         | 
| 63 | 
            -
             | 
| 70 | 
            +
             | 
| 64 71 | 
             
                    self.base_dir = Path(base_dir)
         | 
| 65 72 | 
             
                    self.base_dir.mkdir(parents=True, exist_ok=True)
         | 
| 66 | 
            -
             | 
| 73 | 
            +
             | 
| 67 74 | 
             
                    # Try to get session ID from environment
         | 
| 68 75 | 
             
                    self.session_id = self._get_claude_session_id()
         | 
| 69 76 | 
             
                    self.response_counter = {}  # Track response count per session
         | 
| 70 | 
            -
             | 
| 77 | 
            +
             | 
| 71 78 | 
             
                    # Determine if we should use async logging
         | 
| 72 79 | 
             
                    if use_async is None:
         | 
| 73 80 | 
             
                        # Check if response logging is enabled at all
         | 
| 74 | 
            -
                        if not response_config.get( | 
| 81 | 
            +
                        if not response_config.get("enabled", True):
         | 
| 75 82 | 
             
                            logger.info("Response logging disabled in configuration")
         | 
| 76 83 | 
             
                            use_async = False
         | 
| 77 84 | 
             
                        else:
         | 
| 78 85 | 
             
                            # Check configuration for async preference
         | 
| 79 | 
            -
                            use_async = response_config.get( | 
| 80 | 
            -
             | 
| 86 | 
            +
                            use_async = response_config.get("use_async", True)
         | 
| 87 | 
            +
             | 
| 81 88 | 
             
                            # Check environment for backward compatibility
         | 
| 82 | 
            -
                            if os.environ.get( | 
| 83 | 
            -
                                env_async =  | 
| 84 | 
            -
             | 
| 89 | 
            +
                            if os.environ.get("CLAUDE_USE_ASYNC_LOG"):
         | 
| 90 | 
            +
                                env_async = (
         | 
| 91 | 
            +
                                    os.environ.get("CLAUDE_USE_ASYNC_LOG", "true").lower() == "true"
         | 
| 92 | 
            +
                                )
         | 
| 93 | 
            +
                                logger.info(
         | 
| 94 | 
            +
                                    f"Using CLAUDE_USE_ASYNC_LOG environment variable (deprecated): {env_async}"
         | 
| 95 | 
            +
                                )
         | 
| 85 96 | 
             
                                use_async = env_async
         | 
| 86 | 
            -
             | 
| 97 | 
            +
             | 
| 87 98 | 
             
                    self.use_async = use_async and ASYNC_AVAILABLE
         | 
| 88 99 | 
             
                    self._async_logger = None
         | 
| 89 | 
            -
             | 
| 100 | 
            +
             | 
| 90 101 | 
             
                    if self.use_async:
         | 
| 91 102 | 
             
                        try:
         | 
| 92 103 | 
             
                            self._async_logger = get_async_logger(config=config)
         | 
| 93 104 | 
             
                            logger.info("Using async logger for improved performance")
         | 
| 94 105 | 
             
                        except Exception as e:
         | 
| 95 | 
            -
                            logger.warning( | 
| 106 | 
            +
                            logger.warning(
         | 
| 107 | 
            +
                                f"Failed to initialize async logger, falling back to sync: {e}"
         | 
| 108 | 
            +
                            )
         | 
| 96 109 | 
             
                            self.use_async = False
         | 
| 97 | 
            -
             | 
| 110 | 
            +
             | 
| 98 111 | 
             
                def _get_claude_session_id(self) -> Optional[str]:
         | 
| 99 112 | 
             
                    """
         | 
| 100 113 | 
             
                    Get the Claude Code session ID from environment.
         | 
| 101 | 
            -
             | 
| 114 | 
            +
             | 
| 102 115 | 
             
                    Returns:
         | 
| 103 116 | 
             
                        Session ID if available, None otherwise
         | 
| 104 117 | 
             
                    """
         | 
| 105 118 | 
             
                    # Claude Code may set various environment variables
         | 
| 106 119 | 
             
                    # Check common patterns
         | 
| 107 120 | 
             
                    session_id = None
         | 
| 108 | 
            -
             | 
| 121 | 
            +
             | 
| 109 122 | 
             
                    # Check for CLAUDE_SESSION_ID
         | 
| 110 | 
            -
                    session_id = os.environ.get( | 
| 111 | 
            -
             | 
| 123 | 
            +
                    session_id = os.environ.get("CLAUDE_SESSION_ID")
         | 
| 124 | 
            +
             | 
| 112 125 | 
             
                    # Check for ANTHROPIC_SESSION_ID
         | 
| 113 126 | 
             
                    if not session_id:
         | 
| 114 | 
            -
                        session_id = os.environ.get( | 
| 115 | 
            -
             | 
| 127 | 
            +
                        session_id = os.environ.get("ANTHROPIC_SESSION_ID")
         | 
| 128 | 
            +
             | 
| 116 129 | 
             
                    # Check for generic SESSION_ID
         | 
| 117 130 | 
             
                    if not session_id:
         | 
| 118 | 
            -
                        session_id = os.environ.get( | 
| 119 | 
            -
             | 
| 131 | 
            +
                        session_id = os.environ.get("SESSION_ID")
         | 
| 132 | 
            +
             | 
| 120 133 | 
             
                    # Generate a default based on timestamp if nothing found
         | 
| 121 134 | 
             
                    if not session_id:
         | 
| 122 135 | 
             
                        # Use a timestamp-based session ID as fallback
         | 
| 123 | 
            -
                        session_id = datetime.now().strftime( | 
| 136 | 
            +
                        session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
         | 
| 124 137 | 
             
                        logger.info(f"No Claude session ID found, using generated: {session_id}")
         | 
| 125 138 | 
             
                    else:
         | 
| 126 139 | 
             
                        logger.info(f"Using Claude session ID: {session_id}")
         | 
| 127 | 
            -
             | 
| 140 | 
            +
             | 
| 128 141 | 
             
                    return session_id
         | 
| 129 | 
            -
             | 
| 142 | 
            +
             | 
| 130 143 | 
             
                def _generate_filename(self, agent: Optional[str] = None) -> str:
         | 
| 131 144 | 
             
                    """
         | 
| 132 145 | 
             
                    Generate a flat filename with session ID, agent, and timestamp.
         | 
| 133 | 
            -
             | 
| 146 | 
            +
             | 
| 134 147 | 
             
                    Args:
         | 
| 135 148 | 
             
                        agent: Optional agent name
         | 
| 136 | 
            -
             | 
| 149 | 
            +
             | 
| 137 150 | 
             
                    Returns:
         | 
| 138 151 | 
             
                        Filename in format: [session_id]-[agent]-timestamp.json
         | 
| 139 152 | 
             
                    """
         | 
| @@ -141,116 +154,118 @@ class ClaudeSessionLogger: | |
| 141 154 | 
             
                    agent_name = agent or "unknown"
         | 
| 142 155 | 
             
                    # Sanitize agent name (replace spaces with underscores, lowercase)
         | 
| 143 156 | 
             
                    agent_name = agent_name.replace(" ", "_").lower()
         | 
| 144 | 
            -
             | 
| 157 | 
            +
             | 
| 145 158 | 
             
                    # Generate timestamp with microseconds for uniqueness
         | 
| 146 159 | 
             
                    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
         | 
| 147 | 
            -
             | 
| 160 | 
            +
             | 
| 148 161 | 
             
                    # Create filename: session_id-agent-timestamp.json
         | 
| 149 162 | 
             
                    filename = f"{self.session_id}-{agent_name}-{timestamp}.json"
         | 
| 150 163 | 
             
                    return filename
         | 
| 151 | 
            -
             | 
| 164 | 
            +
             | 
| 152 165 | 
             
                def log_response(
         | 
| 153 166 | 
             
                    self,
         | 
| 154 167 | 
             
                    request_summary: str,
         | 
| 155 168 | 
             
                    response_content: str,
         | 
| 156 169 | 
             
                    metadata: Optional[Dict[str, Any]] = None,
         | 
| 157 | 
            -
                    agent: Optional[str] = None
         | 
| 170 | 
            +
                    agent: Optional[str] = None,
         | 
| 158 171 | 
             
                ) -> Optional[Path]:
         | 
| 159 172 | 
             
                    """
         | 
| 160 173 | 
             
                    Log a response to the session directory.
         | 
| 161 | 
            -
             | 
| 174 | 
            +
             | 
| 162 175 | 
             
                    Args:
         | 
| 163 176 | 
             
                        request_summary: Brief summary of the request
         | 
| 164 177 | 
             
                        response_content: The full response content
         | 
| 165 178 | 
             
                        metadata: Optional metadata (agent name, model, etc.)
         | 
| 166 179 | 
             
                        agent: Optional agent name (overrides metadata)
         | 
| 167 | 
            -
             | 
| 180 | 
            +
             | 
| 168 181 | 
             
                    Returns:
         | 
| 169 182 | 
             
                        Path to the saved response file, or None if disabled
         | 
| 170 183 | 
             
                    """
         | 
| 171 184 | 
             
                    # Check if logging is actually enabled
         | 
| 172 | 
            -
                    response_config = self.config.get( | 
| 173 | 
            -
                    if not response_config.get( | 
| 185 | 
            +
                    response_config = self.config.get("response_logging", {})
         | 
| 186 | 
            +
                    if not response_config.get("enabled", True):
         | 
| 174 187 | 
             
                        logger.debug("Response logging is disabled in configuration")
         | 
| 175 188 | 
             
                        return None
         | 
| 176 | 
            -
             | 
| 189 | 
            +
             | 
| 177 190 | 
             
                    if not self.session_id:
         | 
| 178 191 | 
             
                        return None
         | 
| 179 | 
            -
             | 
| 192 | 
            +
             | 
| 180 193 | 
             
                    # Use async logger if available for better performance
         | 
| 181 194 | 
             
                    if self.use_async and self._async_logger:
         | 
| 182 195 | 
             
                        success = self._async_logger.log_response(
         | 
| 183 196 | 
             
                            request_summary=request_summary,
         | 
| 184 197 | 
             
                            response_content=response_content,
         | 
| 185 198 | 
             
                            metadata=metadata,
         | 
| 186 | 
            -
                            agent=agent
         | 
| 199 | 
            +
                            agent=agent,
         | 
| 187 200 | 
             
                        )
         | 
| 188 201 | 
             
                        if success:
         | 
| 189 202 | 
             
                            # Return expected path for compatibility
         | 
| 190 203 | 
             
                            # Async logger uses timestamp-based names, so we can't return exact path
         | 
| 191 204 | 
             
                            return self.base_dir / "async_response.json"
         | 
| 192 205 | 
             
                        return None
         | 
| 193 | 
            -
             | 
| 206 | 
            +
             | 
| 194 207 | 
             
                    # Fall back to synchronous logging
         | 
| 195 208 | 
             
                    # Ensure base directory exists (flat structure, no subdirs)
         | 
| 196 209 | 
             
                    self.base_dir.mkdir(parents=True, exist_ok=True)
         | 
| 197 | 
            -
             | 
| 210 | 
            +
             | 
| 198 211 | 
             
                    # Extract agent name from parameter or metadata
         | 
| 199 212 | 
             
                    agent_name = agent or (metadata.get("agent") if metadata else None) or "unknown"
         | 
| 200 | 
            -
             | 
| 213 | 
            +
             | 
| 201 214 | 
             
                    # Generate filename with flat structure
         | 
| 202 215 | 
             
                    filename = self._generate_filename(agent_name)
         | 
| 203 216 | 
             
                    file_path = self.base_dir / filename
         | 
| 204 | 
            -
             | 
| 217 | 
            +
             | 
| 205 218 | 
             
                    # Prepare response data with standardized field names
         | 
| 206 219 | 
             
                    response_data = {
         | 
| 207 220 | 
             
                        "timestamp": datetime.now().isoformat(),
         | 
| 208 221 | 
             
                        "session_id": self.session_id,
         | 
| 209 222 | 
             
                        "request": request_summary,  # Standardized field name
         | 
| 210 223 | 
             
                        "response": response_content,  # Already correct
         | 
| 211 | 
            -
                        "agent": agent | 
| 212 | 
            -
                         | 
| 224 | 
            +
                        "agent": agent
         | 
| 225 | 
            +
                        or (metadata.get("agent") if metadata else None)
         | 
| 226 | 
            +
                        or "unknown",  # Standardized field name
         | 
| 227 | 
            +
                        "metadata": metadata or {},
         | 
| 213 228 | 
             
                    }
         | 
| 214 | 
            -
             | 
| 229 | 
            +
             | 
| 215 230 | 
             
                    # Save response
         | 
| 216 231 | 
             
                    try:
         | 
| 217 | 
            -
                        with open(file_path,  | 
| 232 | 
            +
                        with open(file_path, "w", encoding="utf-8") as f:
         | 
| 218 233 | 
             
                            json.dump(response_data, f, indent=2, ensure_ascii=False)
         | 
| 219 | 
            -
             | 
| 234 | 
            +
             | 
| 220 235 | 
             
                        logger.debug(f"Logged response to {filename} for session {self.session_id}")
         | 
| 221 236 | 
             
                        return file_path
         | 
| 222 | 
            -
             | 
| 237 | 
            +
             | 
| 223 238 | 
             
                    except Exception as e:
         | 
| 224 239 | 
             
                        logger.error(f"Failed to log response: {e}")
         | 
| 225 240 | 
             
                        return None
         | 
| 226 | 
            -
             | 
| 241 | 
            +
             | 
| 227 242 | 
             
                def set_session_id(self, session_id: str) -> None:
         | 
| 228 243 | 
             
                    """
         | 
| 229 244 | 
             
                    Manually set the session ID.
         | 
| 230 | 
            -
             | 
| 245 | 
            +
             | 
| 231 246 | 
             
                    Args:
         | 
| 232 247 | 
             
                        session_id: The session ID to use
         | 
| 233 248 | 
             
                    """
         | 
| 234 249 | 
             
                    self.session_id = session_id
         | 
| 235 250 | 
             
                    logger.info(f"Session ID set to: {session_id}")
         | 
| 236 | 
            -
             | 
| 251 | 
            +
             | 
| 237 252 | 
             
                def get_session_path(self) -> Optional[Path]:
         | 
| 238 253 | 
             
                    """
         | 
| 239 254 | 
             
                    Get the path to the responses directory.
         | 
| 240 | 
            -
             | 
| 255 | 
            +
             | 
| 241 256 | 
             
                    Note: With flat structure, returns the base directory.
         | 
| 242 | 
            -
             | 
| 257 | 
            +
             | 
| 243 258 | 
             
                    Returns:
         | 
| 244 259 | 
             
                        Path to responses directory, or None if no session
         | 
| 245 260 | 
             
                    """
         | 
| 246 261 | 
             
                    if not self.session_id:
         | 
| 247 262 | 
             
                        return None
         | 
| 248 263 | 
             
                    return self.base_dir
         | 
| 249 | 
            -
             | 
| 264 | 
            +
             | 
| 250 265 | 
             
                def is_enabled(self) -> bool:
         | 
| 251 266 | 
             
                    """
         | 
| 252 267 | 
             
                    Check if logging is enabled.
         | 
| 253 | 
            -
             | 
| 268 | 
            +
             | 
| 254 269 | 
             
                    Returns:
         | 
| 255 270 | 
             
                        True if logging is enabled (session ID available)
         | 
| 256 271 | 
             
                    """
         | 
| @@ -264,10 +279,10 @@ _logger_instance = None | |
| 264 279 | 
             
            def get_session_logger(config: Optional[Config] = None) -> ClaudeSessionLogger:
         | 
| 265 280 | 
             
                """
         | 
| 266 281 | 
             
                Get the singleton session logger instance.
         | 
| 267 | 
            -
             | 
| 282 | 
            +
             | 
| 268 283 | 
             
                Args:
         | 
| 269 284 | 
             
                    config: Optional configuration instance to use
         | 
| 270 | 
            -
             | 
| 285 | 
            +
             | 
| 271 286 | 
             
                Returns:
         | 
| 272 287 | 
             
                    The shared ClaudeSessionLogger instance
         | 
| 273 288 | 
             
                """
         | 
| @@ -281,19 +296,19 @@ def log_response( | |
| 281 296 | 
             
                request_summary: str,
         | 
| 282 297 | 
             
                response_content: str,
         | 
| 283 298 | 
             
                metadata: Optional[Dict[str, Any]] = None,
         | 
| 284 | 
            -
                agent: Optional[str] = None
         | 
| 299 | 
            +
                agent: Optional[str] = None,
         | 
| 285 300 | 
             
            ) -> Optional[Path]:
         | 
| 286 301 | 
             
                """
         | 
| 287 302 | 
             
                Convenience function to log a response.
         | 
| 288 | 
            -
             | 
| 303 | 
            +
             | 
| 289 304 | 
             
                Args:
         | 
| 290 305 | 
             
                    request_summary: Brief summary of the request
         | 
| 291 306 | 
             
                    response_content: The full response content
         | 
| 292 307 | 
             
                    metadata: Optional metadata
         | 
| 293 308 | 
             
                    agent: Optional agent name
         | 
| 294 | 
            -
             | 
| 309 | 
            +
             | 
| 295 310 | 
             
                Returns:
         | 
| 296 311 | 
             
                    Path to the saved response file
         | 
| 297 312 | 
             
                """
         | 
| 298 313 | 
             
                logger = get_session_logger()
         | 
| 299 | 
            -
                return logger.log_response(request_summary, response_content, metadata, agent)
         | 
| 314 | 
            +
                return logger.log_response(request_summary, response_content, metadata, agent)
         | 
| @@ -0,0 +1,217 @@ | |
| 1 | 
            +
            """Command handler service for processing MPM commands.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            This service handles:
         | 
| 4 | 
            +
            1. MPM command parsing and validation
         | 
| 5 | 
            +
            2. Command execution and routing
         | 
| 6 | 
            +
            3. Command logging and error handling
         | 
| 7 | 
            +
            4. Integration with CLI modules
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Extracted from ClaudeRunner to follow Single Responsibility Principle.
         | 
| 10 | 
            +
            """
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            from typing import Any, Dict, List
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            from claude_mpm.core.base_service import BaseService
         | 
| 15 | 
            +
            from claude_mpm.services.core.interfaces import CommandHandlerInterface
         | 
| 16 | 
            +
             | 
| 17 | 
            +
             | 
| 18 | 
            +
            class CommandHandlerService(BaseService, CommandHandlerInterface):
         | 
| 19 | 
            +
                """Service for handling MPM commands."""
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def __init__(self, project_logger=None):
         | 
| 22 | 
            +
                    """Initialize the command handler service.
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    Args:
         | 
| 25 | 
            +
                        project_logger: Optional project logger for command logging
         | 
| 26 | 
            +
                    """
         | 
| 27 | 
            +
                    super().__init__(name="command_handler_service")
         | 
| 28 | 
            +
                    self.project_logger = project_logger
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                async def _initialize(self) -> None:
         | 
| 31 | 
            +
                    """Initialize the service. No special initialization needed."""
         | 
| 32 | 
            +
                    pass
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                async def _cleanup(self) -> None:
         | 
| 35 | 
            +
                    """Cleanup service resources. No cleanup needed."""
         | 
| 36 | 
            +
                    pass
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def handle_mpm_command(self, prompt: str) -> bool:
         | 
| 39 | 
            +
                    """Handle /mpm: commands directly without going to Claude.
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    Args:
         | 
| 42 | 
            +
                        prompt: The full prompt starting with "/mpm:"
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    Returns:
         | 
| 45 | 
            +
                        bool: True if command was handled successfully, False otherwise
         | 
| 46 | 
            +
                    """
         | 
| 47 | 
            +
                    try:
         | 
| 48 | 
            +
                        # Extract command and arguments
         | 
| 49 | 
            +
                        command_line = prompt[5:].strip()  # Remove "/mpm:"
         | 
| 50 | 
            +
                        parts = command_line.split()
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                        if not parts:
         | 
| 53 | 
            +
                            print("No command specified. Available commands: test, agents")
         | 
| 54 | 
            +
                            return True
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                        command = parts[0]
         | 
| 57 | 
            +
                        args = parts[1:]
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                        # Route to appropriate handler
         | 
| 60 | 
            +
                        if command == "test":
         | 
| 61 | 
            +
                            return self._handle_test_command(args)
         | 
| 62 | 
            +
                        elif command == "agents":
         | 
| 63 | 
            +
                            return self._handle_agents_command(args)
         | 
| 64 | 
            +
                        else:
         | 
| 65 | 
            +
                            print(f"Unknown command: {command}")
         | 
| 66 | 
            +
                            print("Available commands: test, agents")
         | 
| 67 | 
            +
                            return True
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    except KeyboardInterrupt:
         | 
| 70 | 
            +
                        print("\nCommand interrupted")
         | 
| 71 | 
            +
                        return False
         | 
| 72 | 
            +
                    except Exception as e:
         | 
| 73 | 
            +
                        print(f"Error executing command: {e}")
         | 
| 74 | 
            +
                        self._log_command_error(f"Failed to execute /mpm: command: {e}")
         | 
| 75 | 
            +
                        return False
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def _handle_test_command(self, args: list) -> bool:
         | 
| 78 | 
            +
                    """Handle the test command.
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    Args:
         | 
| 81 | 
            +
                        args: Command arguments
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    Returns:
         | 
| 84 | 
            +
                        bool: True if successful
         | 
| 85 | 
            +
                    """
         | 
| 86 | 
            +
                    print("Hello World")
         | 
| 87 | 
            +
                    self._log_command_success("Executed /mpm:test command")
         | 
| 88 | 
            +
                    return True
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def _handle_agents_command(self, args: list) -> bool:
         | 
| 91 | 
            +
                    """Handle the agents command - display deployed agent versions.
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    WHY: This provides users with a quick way to check deployed agent versions
         | 
| 94 | 
            +
                    directly from within Claude Code, maintaining consistency with CLI behavior.
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                    Args:
         | 
| 97 | 
            +
                        args: Command arguments
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                    Returns:
         | 
| 100 | 
            +
                        bool: True if successful, False if error occurred
         | 
| 101 | 
            +
                    """
         | 
| 102 | 
            +
                    try:
         | 
| 103 | 
            +
                        from claude_mpm.cli import _get_agent_versions_display
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                        agent_versions = _get_agent_versions_display()
         | 
| 106 | 
            +
                        if agent_versions:
         | 
| 107 | 
            +
                            print(agent_versions)
         | 
| 108 | 
            +
                        else:
         | 
| 109 | 
            +
                            print("No deployed agents found")
         | 
| 110 | 
            +
                            print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                        self._log_command_success("Executed /mpm:agents command")
         | 
| 113 | 
            +
                        return True
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                    except ImportError as e:
         | 
| 116 | 
            +
                        print(f"Error: CLI module not available: {e}")
         | 
| 117 | 
            +
                        return False
         | 
| 118 | 
            +
                    except Exception as e:
         | 
| 119 | 
            +
                        print(f"Error getting agent versions: {e}")
         | 
| 120 | 
            +
                        return False
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def _log_command_success(self, message: str):
         | 
| 123 | 
            +
                    """Log successful command execution.
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                    Args:
         | 
| 126 | 
            +
                        message: Success message to log
         | 
| 127 | 
            +
                    """
         | 
| 128 | 
            +
                    if self.project_logger:
         | 
| 129 | 
            +
                        self.project_logger.log_system(message, level="INFO", component="command")
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                def _log_command_error(self, message: str):
         | 
| 132 | 
            +
                    """Log command execution error.
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    Args:
         | 
| 135 | 
            +
                        message: Error message to log
         | 
| 136 | 
            +
                    """
         | 
| 137 | 
            +
                    if self.project_logger:
         | 
| 138 | 
            +
                        self.project_logger.log_system(message, level="ERROR", component="command")
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                def is_mpm_command(self, prompt: str) -> bool:
         | 
| 141 | 
            +
                    """Check if a prompt is an MPM command.
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    Args:
         | 
| 144 | 
            +
                        prompt: The prompt to check
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                    Returns:
         | 
| 147 | 
            +
                        bool: True if the prompt is an MPM command
         | 
| 148 | 
            +
                    """
         | 
| 149 | 
            +
                    return prompt.strip().startswith("/mpm:")
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                def get_available_commands(self) -> List[str]:
         | 
| 152 | 
            +
                    """Get list of available MPM commands.
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                    Returns:
         | 
| 155 | 
            +
                        List: List of available command names
         | 
| 156 | 
            +
                    """
         | 
| 157 | 
            +
                    return ["test", "agents"]
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                # Implementation of abstract methods from CommandHandlerInterface
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                def handle_command(self, command: str, args: List[str]) -> Dict[str, Any]:
         | 
| 162 | 
            +
                    """Handle an MPM command.
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                    Args:
         | 
| 165 | 
            +
                        command: Command name to execute
         | 
| 166 | 
            +
                        args: Command arguments
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                    Returns:
         | 
| 169 | 
            +
                        Dictionary with command execution results
         | 
| 170 | 
            +
                    """
         | 
| 171 | 
            +
                    try:
         | 
| 172 | 
            +
                        if command == "test":
         | 
| 173 | 
            +
                            success = self._handle_test_command(args)
         | 
| 174 | 
            +
                            return {
         | 
| 175 | 
            +
                                "success": success,
         | 
| 176 | 
            +
                                "command": command,
         | 
| 177 | 
            +
                                "args": args,
         | 
| 178 | 
            +
                                "message": "Test command executed"
         | 
| 179 | 
            +
                                if success
         | 
| 180 | 
            +
                                else "Test command failed",
         | 
| 181 | 
            +
                            }
         | 
| 182 | 
            +
                        elif command == "agents":
         | 
| 183 | 
            +
                            success = self._handle_agents_command(args)
         | 
| 184 | 
            +
                            return {
         | 
| 185 | 
            +
                                "success": success,
         | 
| 186 | 
            +
                                "command": command,
         | 
| 187 | 
            +
                                "args": args,
         | 
| 188 | 
            +
                                "message": "Agents command executed"
         | 
| 189 | 
            +
                                if success
         | 
| 190 | 
            +
                                else "Agents command failed",
         | 
| 191 | 
            +
                            }
         | 
| 192 | 
            +
                        else:
         | 
| 193 | 
            +
                            return {
         | 
| 194 | 
            +
                                "success": False,
         | 
| 195 | 
            +
                                "command": command,
         | 
| 196 | 
            +
                                "args": args,
         | 
| 197 | 
            +
                                "error": f"Unknown command: {command}",
         | 
| 198 | 
            +
                                "available_commands": self.get_available_commands(),
         | 
| 199 | 
            +
                            }
         | 
| 200 | 
            +
                    except Exception as e:
         | 
| 201 | 
            +
                        return {"success": False, "command": command, "args": args, "error": str(e)}
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                def get_command_help(self, command: str) -> str:
         | 
| 204 | 
            +
                    """Get help text for a specific command.
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                    Args:
         | 
| 207 | 
            +
                        command: Command name
         | 
| 208 | 
            +
             | 
| 209 | 
            +
                    Returns:
         | 
| 210 | 
            +
                        Help text for the command
         | 
| 211 | 
            +
                    """
         | 
| 212 | 
            +
                    help_text = {
         | 
| 213 | 
            +
                        "test": "Test command - prints 'Hello World' to verify MPM command functionality",
         | 
| 214 | 
            +
                        "agents": "Agents command - displays information about deployed agents and their versions",
         | 
| 215 | 
            +
                    }
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                    return help_text.get(command, f"No help available for command: {command}")
         |