claude-mpm 3.9.11__py3-none-any.whl → 4.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +1 -1
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +79 -51
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +20 -20
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +140 -905
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1156
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +71 -73
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +35 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +233 -199
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +30 -13
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
- claude_mpm/services/mcp_gateway/main.py +287 -137
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +19 -533
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +2 -2
- claude_mpm/storage/state_storage.py +177 -181
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/cli/commands/run_guarded.py +0 -511
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/config/memory_guardian_yaml.py +0 -335
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/core/memory_aware_runner.py +0 -353
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
- claude_mpm/services/infrastructure/health_monitor.py +0 -775
- claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
- claude_mpm/services/infrastructure/memory_guardian.py +0 -944
- claude_mpm/services/infrastructure/restart_protection.py +0 -642
- claude_mpm/services/infrastructure/state_manager.py +0 -774
- claude_mpm/services/mcp_gateway/manager.py +0 -334
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.11.dist-info/RECORD +0 -306
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -7,11 +7,9 @@ with proper separation of concerns and reduced complexity. | |
| 7 7 | 
             
            import os
         | 
| 8 8 | 
             
            import subprocess
         | 
| 9 9 | 
             
            import sys
         | 
| 10 | 
            -
            from pathlib import Path
         | 
| 11 | 
            -
            from typing import Optional, Dict, Any, Tuple
         | 
| 12 10 | 
             
            import uuid
         | 
| 13 11 | 
             
            from datetime import datetime
         | 
| 14 | 
            -
            import  | 
| 12 | 
            +
            from typing import Any, Dict, Optional, Tuple
         | 
| 15 13 |  | 
| 16 14 | 
             
            from claude_mpm.core.logger import get_logger
         | 
| 17 15 |  | 
| @@ -19,19 +17,19 @@ from claude_mpm.core.logger import get_logger | |
| 19 17 | 
             
            class InteractiveSession:
         | 
| 20 18 | 
             
                """
         | 
| 21 19 | 
             
                Handles interactive Claude sessions with proper separation of concerns.
         | 
| 22 | 
            -
             | 
| 20 | 
            +
             | 
| 23 21 | 
             
                WHY: The original run_interactive() method had complexity of 39 and 262 lines.
         | 
| 24 22 | 
             
                This class breaks down that functionality into smaller, focused methods with
         | 
| 25 23 | 
             
                complexity <10 and lines <80 each, improving maintainability and testability.
         | 
| 26 | 
            -
             | 
| 24 | 
            +
             | 
| 27 25 | 
             
                DESIGN DECISION: Uses composition over inheritance - takes ClaudeRunner as
         | 
| 28 26 | 
             
                dependency rather than inheriting from it. This maintains loose coupling
         | 
| 29 27 | 
             
                and makes testing easier while preserving all original functionality.
         | 
| 30 28 | 
             
                """
         | 
| 31 | 
            -
             | 
| 29 | 
            +
             | 
| 32 30 | 
             
                def __init__(self, runner):
         | 
| 33 31 | 
             
                    """Initialize interactive session handler.
         | 
| 34 | 
            -
             | 
| 32 | 
            +
             | 
| 35 33 | 
             
                    Args:
         | 
| 36 34 | 
             
                        runner: ClaudeRunner instance with all necessary services
         | 
| 37 35 | 
             
                    """
         | 
| @@ -39,86 +37,93 @@ class InteractiveSession: | |
| 39 37 | 
             
                    self.logger = get_logger("interactive_session")
         | 
| 40 38 | 
             
                    self.session_id = None
         | 
| 41 39 | 
             
                    self.original_cwd = os.getcwd()
         | 
| 42 | 
            -
             | 
| 40 | 
            +
             | 
| 43 41 | 
             
                    # Initialize response tracking for interactive sessions
         | 
| 44 42 | 
             
                    # WHY: Interactive sessions need response logging just like oneshot sessions.
         | 
| 45 43 | 
             
                    # The hook system captures events, but we need the ResponseTracker to be
         | 
| 46 44 | 
             
                    # initialized to actually store them.
         | 
| 47 45 | 
             
                    self.response_tracker = None
         | 
| 48 | 
            -
             | 
| 46 | 
            +
             | 
| 49 47 | 
             
                    # Check if response logging is enabled in configuration
         | 
| 50 48 | 
             
                    try:
         | 
| 51 | 
            -
                        response_config = self.runner.config.get( | 
| 52 | 
            -
                        response_logging_enabled = response_config.get( | 
| 49 | 
            +
                        response_config = self.runner.config.get("response_logging", {})
         | 
| 50 | 
            +
                        response_logging_enabled = response_config.get("enabled", False)
         | 
| 53 51 | 
             
                    except (AttributeError, TypeError):
         | 
| 54 52 | 
             
                        # Handle mock or missing config gracefully
         | 
| 55 53 | 
             
                        response_logging_enabled = False
         | 
| 56 | 
            -
             | 
| 54 | 
            +
             | 
| 57 55 | 
             
                    if response_logging_enabled:
         | 
| 58 56 | 
             
                        try:
         | 
| 59 57 | 
             
                            from claude_mpm.services.response_tracker import ResponseTracker
         | 
| 58 | 
            +
             | 
| 60 59 | 
             
                            self.response_tracker = ResponseTracker(self.runner.config)
         | 
| 61 | 
            -
                            self.logger.info( | 
| 60 | 
            +
                            self.logger.info(
         | 
| 61 | 
            +
                                "Response tracking initialized for interactive session"
         | 
| 62 | 
            +
                            )
         | 
| 62 63 | 
             
                        except Exception as e:
         | 
| 63 64 | 
             
                            self.logger.warning(f"Failed to initialize response tracker: {e}")
         | 
| 64 65 | 
             
                            # Continue without response tracking - not fatal
         | 
| 65 | 
            -
             | 
| 66 | 
            +
             | 
| 66 67 | 
             
                def initialize_interactive_session(self) -> Tuple[bool, Optional[str]]:
         | 
| 67 68 | 
             
                    """Initialize the interactive session environment.
         | 
| 68 | 
            -
             | 
| 69 | 
            +
             | 
| 69 70 | 
             
                    Sets up WebSocket connections, generates session IDs, and prepares
         | 
| 70 71 | 
             
                    the session for launch.
         | 
| 71 | 
            -
             | 
| 72 | 
            +
             | 
| 72 73 | 
             
                    Returns:
         | 
| 73 74 | 
             
                        Tuple of (success, error_message)
         | 
| 74 75 | 
             
                    """
         | 
| 75 76 | 
             
                    try:
         | 
| 76 77 | 
             
                        # Generate session ID
         | 
| 77 78 | 
             
                        self.session_id = str(uuid.uuid4())
         | 
| 78 | 
            -
             | 
| 79 | 
            +
             | 
| 79 80 | 
             
                        # Initialize WebSocket if enabled
         | 
| 80 81 | 
             
                        if self.runner.enable_websocket:
         | 
| 81 82 | 
             
                            success, error = self._initialize_websocket()
         | 
| 82 83 | 
             
                            if not success:
         | 
| 83 84 | 
             
                                self.logger.warning(f"WebSocket initialization failed: {error}")
         | 
| 84 85 | 
             
                                # Continue without WebSocket - not a fatal error
         | 
| 85 | 
            -
             | 
| 86 | 
            +
             | 
| 86 87 | 
             
                        # Display welcome message
         | 
| 87 88 | 
             
                        self._display_welcome_message()
         | 
| 88 | 
            -
             | 
| 89 | 
            +
             | 
| 89 90 | 
             
                        # Log session start
         | 
| 90 91 | 
             
                        if self.runner.project_logger:
         | 
| 91 92 | 
             
                            self.runner.project_logger.log_system(
         | 
| 92 | 
            -
                                "Starting interactive session",
         | 
| 93 | 
            -
                                level="INFO",
         | 
| 94 | 
            -
                                component="session"
         | 
| 93 | 
            +
                                "Starting interactive session", level="INFO", component="session"
         | 
| 95 94 | 
             
                            )
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                        # Initialize response tracking session if enabled
         | 
| 98 | 
            -
                        # WHY: The ResponseTracker needs to know about the session to properly
         | 
| 99 | 
            -
                        # correlate prompts and responses during the interactive session.
         | 
| 95 | 
            +
             | 
| 100 96 | 
             
                        if self.response_tracker and self.response_tracker.enabled:
         | 
| 101 97 | 
             
                            try:
         | 
| 102 98 | 
             
                                # Set the session ID in the tracker for correlation
         | 
| 103 | 
            -
                                if  | 
| 104 | 
            -
                                    self.response_tracker | 
| 105 | 
            -
                                    self. | 
| 99 | 
            +
                                if (
         | 
| 100 | 
            +
                                    hasattr(self.response_tracker, "session_logger")
         | 
| 101 | 
            +
                                    and self.response_tracker.session_logger
         | 
| 102 | 
            +
                                ):
         | 
| 103 | 
            +
                                    self.response_tracker.session_logger.set_session_id(
         | 
| 104 | 
            +
                                        self.session_id
         | 
| 105 | 
            +
                                    )
         | 
| 106 | 
            +
                                    self.logger.debug(
         | 
| 107 | 
            +
                                        f"Response tracker session ID set to: {self.session_id}"
         | 
| 108 | 
            +
                                    )
         | 
| 106 109 | 
             
                            except Exception as e:
         | 
| 107 | 
            -
                                self.logger.debug( | 
| 108 | 
            -
             | 
| 110 | 
            +
                                self.logger.debug(
         | 
| 111 | 
            +
                                    f"Could not set session ID in response tracker: {e}"
         | 
| 112 | 
            +
                                )
         | 
| 113 | 
            +
             | 
| 109 114 | 
             
                        return True, None
         | 
| 110 | 
            -
             | 
| 115 | 
            +
             | 
| 111 116 | 
             
                    except Exception as e:
         | 
| 112 117 | 
             
                        error_msg = f"Failed to initialize session: {e}"
         | 
| 113 118 | 
             
                        self.logger.error(error_msg)
         | 
| 114 119 | 
             
                        return False, error_msg
         | 
| 115 | 
            -
             | 
| 120 | 
            +
             | 
| 116 121 | 
             
                def setup_interactive_environment(self) -> Tuple[bool, Dict[str, Any]]:
         | 
| 117 122 | 
             
                    """Set up the interactive environment including agents and commands.
         | 
| 118 | 
            -
             | 
| 123 | 
            +
             | 
| 119 124 | 
             
                    Deploys system and project agents, prepares the command line,
         | 
| 120 125 | 
             
                    and sets up the execution environment.
         | 
| 121 | 
            -
             | 
| 126 | 
            +
             | 
| 122 127 | 
             
                    Returns:
         | 
| 123 128 | 
             
                        Tuple of (success, environment_dict)
         | 
| 124 129 | 
             
                    """
         | 
| @@ -126,64 +131,63 @@ class InteractiveSession: | |
| 126 131 | 
             
                        # Deploy system agents
         | 
| 127 132 | 
             
                        if not self.runner.setup_agents():
         | 
| 128 133 | 
             
                            print("Continuing without native agents...")
         | 
| 129 | 
            -
             | 
| 134 | 
            +
             | 
| 130 135 | 
             
                        # Deploy project-specific agents
         | 
| 131 136 | 
             
                        self.runner.deploy_project_agents_to_claude()
         | 
| 132 | 
            -
             | 
| 137 | 
            +
             | 
| 133 138 | 
             
                        # Build command
         | 
| 134 139 | 
             
                        cmd = self._build_claude_command()
         | 
| 135 | 
            -
             | 
| 140 | 
            +
             | 
| 136 141 | 
             
                        # Prepare environment
         | 
| 137 142 | 
             
                        env = self._prepare_environment()
         | 
| 138 | 
            -
             | 
| 143 | 
            +
             | 
| 139 144 | 
             
                        # Change to user directory if needed
         | 
| 140 145 | 
             
                        self._change_to_user_directory(env)
         | 
| 141 | 
            -
             | 
| 146 | 
            +
             | 
| 142 147 | 
             
                        return True, {
         | 
| 143 | 
            -
                             | 
| 144 | 
            -
                             | 
| 145 | 
            -
                             | 
| 148 | 
            +
                            "command": cmd,
         | 
| 149 | 
            +
                            "environment": env,
         | 
| 150 | 
            +
                            "session_id": self.session_id,
         | 
| 146 151 | 
             
                        }
         | 
| 147 | 
            -
             | 
| 152 | 
            +
             | 
| 148 153 | 
             
                    except Exception as e:
         | 
| 149 154 | 
             
                        error_msg = f"Failed to setup environment: {e}"
         | 
| 150 155 | 
             
                        self.logger.error(error_msg)
         | 
| 151 156 | 
             
                        return False, {}
         | 
| 152 | 
            -
             | 
| 157 | 
            +
             | 
| 153 158 | 
             
                def handle_interactive_input(self, environment: Dict[str, Any]) -> bool:
         | 
| 154 159 | 
             
                    """Handle the interactive input/output loop.
         | 
| 155 | 
            -
             | 
| 160 | 
            +
             | 
| 156 161 | 
             
                    Launches Claude and manages the interactive session using either
         | 
| 157 162 | 
             
                    exec or subprocess method based on configuration.
         | 
| 158 | 
            -
             | 
| 163 | 
            +
             | 
| 159 164 | 
             
                    Args:
         | 
| 160 165 | 
             
                        environment: Dictionary with command, env vars, and session info
         | 
| 161 | 
            -
             | 
| 166 | 
            +
             | 
| 162 167 | 
             
                    Returns:
         | 
| 163 168 | 
             
                        bool: True if successful, False otherwise
         | 
| 164 169 | 
             
                    """
         | 
| 165 170 | 
             
                    try:
         | 
| 166 | 
            -
                        cmd = environment[ | 
| 167 | 
            -
                        env = environment[ | 
| 168 | 
            -
             | 
| 171 | 
            +
                        cmd = environment["command"]
         | 
| 172 | 
            +
                        env = environment["environment"]
         | 
| 173 | 
            +
             | 
| 169 174 | 
             
                        print("Launching Claude...")
         | 
| 170 | 
            -
             | 
| 175 | 
            +
             | 
| 171 176 | 
             
                        # Log launch attempt
         | 
| 172 177 | 
             
                        self._log_launch_attempt(cmd)
         | 
| 173 | 
            -
             | 
| 178 | 
            +
             | 
| 174 179 | 
             
                        # Notify WebSocket if connected
         | 
| 175 180 | 
             
                        if self.runner.websocket_server:
         | 
| 176 181 | 
             
                            self.runner.websocket_server.claude_status_changed(
         | 
| 177 | 
            -
                                status="starting",
         | 
| 178 | 
            -
                                message="Launching Claude interactive session"
         | 
| 182 | 
            +
                                status="starting", message="Launching Claude interactive session"
         | 
| 179 183 | 
             
                            )
         | 
| 180 | 
            -
             | 
| 184 | 
            +
             | 
| 181 185 | 
             
                        # Launch using selected method
         | 
| 182 186 | 
             
                        if self.runner.launch_method == "subprocess":
         | 
| 183 187 | 
             
                            return self._launch_subprocess_mode(cmd, env)
         | 
| 184 188 | 
             
                        else:
         | 
| 185 189 | 
             
                            return self._launch_exec_mode(cmd, env)
         | 
| 186 | 
            -
             | 
| 190 | 
            +
             | 
| 187 191 | 
             
                    except FileNotFoundError as e:
         | 
| 188 192 | 
             
                        self._handle_launch_error("FileNotFoundError", e)
         | 
| 189 193 | 
             
                        return False
         | 
| @@ -199,26 +203,26 @@ class InteractiveSession: | |
| 199 203 | 
             
                    except Exception as e:
         | 
| 200 204 | 
             
                        self._handle_launch_error("Exception", e)
         | 
| 201 205 | 
             
                        return self._attempt_fallback_launch(environment)
         | 
| 202 | 
            -
             | 
| 206 | 
            +
             | 
| 203 207 | 
             
                def process_interactive_command(self, prompt: str) -> Optional[bool]:
         | 
| 204 208 | 
             
                    """Process special interactive commands like /agents.
         | 
| 205 | 
            -
             | 
| 209 | 
            +
             | 
| 206 210 | 
             
                    Args:
         | 
| 207 211 | 
             
                        prompt: User input command
         | 
| 208 | 
            -
             | 
| 212 | 
            +
             | 
| 209 213 | 
             
                    Returns:
         | 
| 210 214 | 
             
                        Optional[bool]: True if handled, False if error, None if not a special command
         | 
| 211 215 | 
             
                    """
         | 
| 212 216 | 
             
                    # Check for special commands
         | 
| 213 217 | 
             
                    if prompt.strip() == "/agents":
         | 
| 214 218 | 
             
                        return self._show_available_agents()
         | 
| 215 | 
            -
             | 
| 219 | 
            +
             | 
| 216 220 | 
             
                    # Not a special command
         | 
| 217 221 | 
             
                    return None
         | 
| 218 | 
            -
             | 
| 222 | 
            +
             | 
| 219 223 | 
             
                def cleanup_interactive_session(self) -> None:
         | 
| 220 224 | 
             
                    """Clean up resources after interactive session ends.
         | 
| 221 | 
            -
             | 
| 225 | 
            +
             | 
| 222 226 | 
             
                    Restores original directory, closes connections, and logs session end.
         | 
| 223 227 | 
             
                    """
         | 
| 224 228 | 
             
                    try:
         | 
| @@ -228,222 +232,227 @@ class InteractiveSession: | |
| 228 232 | 
             
                                os.chdir(self.original_cwd)
         | 
| 229 233 | 
             
                            except OSError:
         | 
| 230 234 | 
             
                                pass
         | 
| 231 | 
            -
             | 
| 235 | 
            +
             | 
| 232 236 | 
             
                        # Close WebSocket if connected
         | 
| 233 237 | 
             
                        if self.runner.websocket_server:
         | 
| 234 238 | 
             
                            self.runner.websocket_server.session_ended()
         | 
| 235 239 | 
             
                            self.runner.websocket_server = None
         | 
| 236 | 
            -
             | 
| 240 | 
            +
             | 
| 237 241 | 
             
                        # Log session end
         | 
| 238 242 | 
             
                        if self.runner.project_logger:
         | 
| 239 243 | 
             
                            self.runner.project_logger.log_system(
         | 
| 240 | 
            -
                                "Interactive session ended",
         | 
| 241 | 
            -
                                level="INFO",
         | 
| 242 | 
            -
                                component="session"
         | 
| 244 | 
            +
                                "Interactive session ended", level="INFO", component="session"
         | 
| 243 245 | 
             
                            )
         | 
| 244 | 
            -
             | 
| 246 | 
            +
             | 
| 245 247 | 
             
                        # Log session event
         | 
| 246 248 | 
             
                        if self.runner.session_log_file:
         | 
| 247 | 
            -
                            self.runner._log_session_event( | 
| 248 | 
            -
                                "event": "session_end",
         | 
| 249 | 
            -
             | 
| 250 | 
            -
             | 
| 251 | 
            -
                        
         | 
| 252 | 
            -
                        # Clean up response tracker if initialized
         | 
| 253 | 
            -
                        # WHY: Ensure proper cleanup of response tracking resources and
         | 
| 254 | 
            -
                        # finalize any pending response logs.
         | 
| 249 | 
            +
                            self.runner._log_session_event(
         | 
| 250 | 
            +
                                {"event": "session_end", "session_id": self.session_id}
         | 
| 251 | 
            +
                            )
         | 
| 252 | 
            +
             | 
| 255 253 | 
             
                        if self.response_tracker:
         | 
| 256 254 | 
             
                            try:
         | 
| 257 255 | 
             
                                # Clear the session ID to stop tracking this session
         | 
| 258 | 
            -
                                if  | 
| 256 | 
            +
                                if (
         | 
| 257 | 
            +
                                    hasattr(self.response_tracker, "session_logger")
         | 
| 258 | 
            +
                                    and self.response_tracker.session_logger
         | 
| 259 | 
            +
                                ):
         | 
| 259 260 | 
             
                                    self.response_tracker.session_logger.set_session_id(None)
         | 
| 260 261 | 
             
                                    self.logger.debug("Response tracker session cleared")
         | 
| 261 262 | 
             
                            except Exception as e:
         | 
| 262 263 | 
             
                                self.logger.debug(f"Error clearing response tracker session: {e}")
         | 
| 263 | 
            -
             | 
| 264 | 
            +
             | 
| 264 265 | 
             
                    except Exception as e:
         | 
| 265 266 | 
             
                        self.logger.debug(f"Error during cleanup: {e}")
         | 
| 266 | 
            -
             | 
| 267 | 
            +
             | 
| 267 268 | 
             
                # Private helper methods (each <80 lines, complexity <10)
         | 
| 268 | 
            -
             | 
| 269 | 
            +
             | 
| 269 270 | 
             
                def _initialize_websocket(self) -> Tuple[bool, Optional[str]]:
         | 
| 270 271 | 
             
                    """Initialize WebSocket connection for monitoring."""
         | 
| 271 272 | 
             
                    try:
         | 
| 272 273 | 
             
                        from claude_mpm.services.socketio_server import SocketIOClientProxy
         | 
| 273 | 
            -
             | 
| 274 | 
            +
             | 
| 275 | 
            +
                        self.runner.websocket_server = SocketIOClientProxy(
         | 
| 276 | 
            +
                            port=self.runner.websocket_port
         | 
| 277 | 
            +
                        )
         | 
| 274 278 | 
             
                        self.runner.websocket_server.start()
         | 
| 275 279 | 
             
                        self.logger.info("Connected to Socket.IO monitoring server")
         | 
| 276 | 
            -
             | 
| 280 | 
            +
             | 
| 277 281 | 
             
                        # Notify session start
         | 
| 278 282 | 
             
                        self.runner.websocket_server.session_started(
         | 
| 279 283 | 
             
                            session_id=self.session_id,
         | 
| 280 284 | 
             
                            launch_method=self.runner.launch_method,
         | 
| 281 | 
            -
                            working_dir=os.getcwd()
         | 
| 285 | 
            +
                            working_dir=os.getcwd(),
         | 
| 282 286 | 
             
                        )
         | 
| 283 287 | 
             
                        return True, None
         | 
| 284 | 
            -
             | 
| 288 | 
            +
             | 
| 285 289 | 
             
                    except ImportError as e:
         | 
| 286 290 | 
             
                        return False, f"Socket.IO module not available: {e}"
         | 
| 287 291 | 
             
                    except ConnectionError as e:
         | 
| 288 292 | 
             
                        return False, f"Cannot connect to Socket.IO server: {e}"
         | 
| 289 293 | 
             
                    except Exception as e:
         | 
| 290 294 | 
             
                        return False, f"Unexpected error with Socket.IO: {e}"
         | 
| 291 | 
            -
             | 
| 295 | 
            +
             | 
| 292 296 | 
             
                def _display_welcome_message(self) -> None:
         | 
| 293 297 | 
             
                    """Display the interactive session welcome message."""
         | 
| 294 298 | 
             
                    version_str = self.runner._get_version()
         | 
| 295 | 
            -
             | 
| 299 | 
            +
             | 
| 296 300 | 
             
                    print("\033[32m╭───────────────────────────────────────────────────╮\033[0m")
         | 
| 297 | 
            -
                    print( | 
| 301 | 
            +
                    print(
         | 
| 302 | 
            +
                        "\033[32m│\033[0m ✻ Claude MPM - Interactive Session                \033[32m│\033[0m"
         | 
| 303 | 
            +
                    )
         | 
| 298 304 | 
             
                    print(f"\033[32m│\033[0m   Version {version_str:<40}\033[32m│\033[0m")
         | 
| 299 305 | 
             
                    print("\033[32m│                                                   │\033[0m")
         | 
| 300 | 
            -
                    print( | 
| 306 | 
            +
                    print(
         | 
| 307 | 
            +
                        "\033[32m│\033[0m   Type '/agents' to see available agents          \033[32m│\033[0m"
         | 
| 308 | 
            +
                    )
         | 
| 301 309 | 
             
                    print("\033[32m╰───────────────────────────────────────────────────╯\033[0m")
         | 
| 302 310 | 
             
                    print("")  # Add blank line after box
         | 
| 303 | 
            -
             | 
| 311 | 
            +
             | 
| 304 312 | 
             
                def _build_claude_command(self) -> list:
         | 
| 305 313 | 
             
                    """Build the Claude command with all necessary arguments."""
         | 
| 306 | 
            -
                    cmd = [
         | 
| 307 | 
            -
             | 
| 308 | 
            -
                        "--model", "opus",
         | 
| 309 | 
            -
                        "--dangerously-skip-permissions"
         | 
| 310 | 
            -
                    ]
         | 
| 311 | 
            -
                    
         | 
| 314 | 
            +
                    cmd = ["claude", "--model", "opus", "--dangerously-skip-permissions"]
         | 
| 315 | 
            +
             | 
| 312 316 | 
             
                    # Add custom arguments
         | 
| 313 317 | 
             
                    if self.runner.claude_args:
         | 
| 314 318 | 
             
                        cmd.extend(self.runner.claude_args)
         | 
| 315 | 
            -
             | 
| 319 | 
            +
             | 
| 316 320 | 
             
                    # Add system instructions
         | 
| 317 321 | 
             
                    from claude_mpm.core.claude_runner import create_simple_context
         | 
| 322 | 
            +
             | 
| 318 323 | 
             
                    system_prompt = self.runner._create_system_prompt()
         | 
| 319 324 | 
             
                    if system_prompt and system_prompt != create_simple_context():
         | 
| 320 325 | 
             
                        cmd.extend(["--append-system-prompt", system_prompt])
         | 
| 321 | 
            -
             | 
| 326 | 
            +
             | 
| 322 327 | 
             
                    return cmd
         | 
| 323 | 
            -
             | 
| 328 | 
            +
             | 
| 324 329 | 
             
                def _prepare_environment(self) -> dict:
         | 
| 325 330 | 
             
                    """Prepare clean environment variables for Claude."""
         | 
| 326 331 | 
             
                    clean_env = os.environ.copy()
         | 
| 327 | 
            -
             | 
| 332 | 
            +
             | 
| 328 333 | 
             
                    # Remove Claude-specific variables that might interfere
         | 
| 329 334 | 
             
                    claude_vars_to_remove = [
         | 
| 330 | 
            -
                         | 
| 331 | 
            -
                         | 
| 335 | 
            +
                        "CLAUDE_CODE_ENTRYPOINT",
         | 
| 336 | 
            +
                        "CLAUDECODE",
         | 
| 337 | 
            +
                        "CLAUDE_CONFIG_DIR",
         | 
| 338 | 
            +
                        "CLAUDE_MAX_PARALLEL_SUBAGENTS",
         | 
| 339 | 
            +
                        "CLAUDE_TIMEOUT",
         | 
| 332 340 | 
             
                    ]
         | 
| 333 341 | 
             
                    for var in claude_vars_to_remove:
         | 
| 334 342 | 
             
                        clean_env.pop(var, None)
         | 
| 335 | 
            -
             | 
| 343 | 
            +
             | 
| 336 344 | 
             
                    return clean_env
         | 
| 337 | 
            -
             | 
| 345 | 
            +
             | 
| 338 346 | 
             
                def _change_to_user_directory(self, env: dict) -> None:
         | 
| 339 347 | 
             
                    """Change to user's working directory if specified."""
         | 
| 340 | 
            -
                    if  | 
| 341 | 
            -
                        user_pwd = env[ | 
| 342 | 
            -
                        env[ | 
| 343 | 
            -
             | 
| 348 | 
            +
                    if "CLAUDE_MPM_USER_PWD" in env:
         | 
| 349 | 
            +
                        user_pwd = env["CLAUDE_MPM_USER_PWD"]
         | 
| 350 | 
            +
                        env["CLAUDE_WORKSPACE"] = user_pwd
         | 
| 351 | 
            +
             | 
| 344 352 | 
             
                        try:
         | 
| 345 353 | 
             
                            os.chdir(user_pwd)
         | 
| 346 354 | 
             
                            self.logger.info(f"Changed working directory to: {user_pwd}")
         | 
| 347 355 | 
             
                        except (PermissionError, FileNotFoundError, OSError) as e:
         | 
| 348 356 | 
             
                            self.logger.warning(f"Could not change to directory {user_pwd}: {e}")
         | 
| 349 | 
            -
             | 
| 357 | 
            +
             | 
| 350 358 | 
             
                def _log_launch_attempt(self, cmd: list) -> None:
         | 
| 351 359 | 
             
                    """Log the Claude launch attempt."""
         | 
| 352 360 | 
             
                    if self.runner.project_logger:
         | 
| 353 361 | 
             
                        self.runner.project_logger.log_system(
         | 
| 354 362 | 
             
                            f"Launching Claude interactive mode with {self.runner.launch_method}",
         | 
| 355 363 | 
             
                            level="INFO",
         | 
| 356 | 
            -
                            component="session"
         | 
| 364 | 
            +
                            component="session",
         | 
| 357 365 | 
             
                        )
         | 
| 358 | 
            -
                        self.runner._log_session_event( | 
| 359 | 
            -
                             | 
| 360 | 
            -
             | 
| 361 | 
            -
             | 
| 362 | 
            -
             | 
| 363 | 
            -
             | 
| 366 | 
            +
                        self.runner._log_session_event(
         | 
| 367 | 
            +
                            {
         | 
| 368 | 
            +
                                "event": "launching_claude_interactive",
         | 
| 369 | 
            +
                                "command": " ".join(cmd),
         | 
| 370 | 
            +
                                "method": self.runner.launch_method,
         | 
| 371 | 
            +
                            }
         | 
| 372 | 
            +
                        )
         | 
| 373 | 
            +
             | 
| 364 374 | 
             
                def _launch_exec_mode(self, cmd: list, env: dict) -> bool:
         | 
| 365 375 | 
             
                    """Launch Claude using exec mode (replaces current process)."""
         | 
| 366 376 | 
             
                    # Notify WebSocket before exec
         | 
| 367 377 | 
             
                    if self.runner.websocket_server:
         | 
| 368 378 | 
             
                        self.runner.websocket_server.claude_status_changed(
         | 
| 369 | 
            -
                            status="running",
         | 
| 370 | 
            -
                            message="Claude process started (exec mode)"
         | 
| 379 | 
            +
                            status="running", message="Claude process started (exec mode)"
         | 
| 371 380 | 
             
                        )
         | 
| 372 | 
            -
             | 
| 381 | 
            +
             | 
| 373 382 | 
             
                    # This will not return if successful
         | 
| 374 383 | 
             
                    os.execvpe(cmd[0], cmd, env)
         | 
| 375 384 | 
             
                    return False  # Only reached on failure
         | 
| 376 | 
            -
             | 
| 385 | 
            +
             | 
| 377 386 | 
             
                def _launch_subprocess_mode(self, cmd: list, env: dict) -> bool:
         | 
| 378 387 | 
             
                    """Launch Claude as subprocess with PTY."""
         | 
| 379 388 | 
             
                    # Delegate to runner's existing method
         | 
| 380 389 | 
             
                    self.runner._launch_subprocess_interactive(cmd, env)
         | 
| 381 390 | 
             
                    return True
         | 
| 382 | 
            -
             | 
| 391 | 
            +
             | 
| 383 392 | 
             
                def _handle_launch_error(self, error_type: str, error: Exception) -> None:
         | 
| 384 393 | 
             
                    """Handle errors during Claude launch."""
         | 
| 385 394 | 
             
                    error_messages = {
         | 
| 386 395 | 
             
                        "FileNotFoundError": "Claude CLI not found. Please ensure 'claude' is installed and in your PATH",
         | 
| 387 396 | 
             
                        "PermissionError": "Permission denied executing Claude CLI",
         | 
| 388 397 | 
             
                        "OSError": "OS error launching Claude",
         | 
| 389 | 
            -
                        "Exception": "Unexpected error launching Claude"
         | 
| 398 | 
            +
                        "Exception": "Unexpected error launching Claude",
         | 
| 390 399 | 
             
                    }
         | 
| 391 | 
            -
             | 
| 400 | 
            +
             | 
| 392 401 | 
             
                    error_msg = f"{error_messages.get(error_type, 'Error')}: {error}"
         | 
| 393 402 | 
             
                    print(f"❌ {error_msg}")
         | 
| 394 | 
            -
             | 
| 403 | 
            +
             | 
| 395 404 | 
             
                    if self.runner.project_logger:
         | 
| 396 | 
            -
                        self.runner.project_logger.log_system( | 
| 397 | 
            -
             | 
| 398 | 
            -
             | 
| 399 | 
            -
             | 
| 400 | 
            -
                             | 
| 401 | 
            -
             | 
| 402 | 
            -
             | 
| 405 | 
            +
                        self.runner.project_logger.log_system(
         | 
| 406 | 
            +
                            error_msg, level="ERROR", component="session"
         | 
| 407 | 
            +
                        )
         | 
| 408 | 
            +
                        self.runner._log_session_event(
         | 
| 409 | 
            +
                            {
         | 
| 410 | 
            +
                                "event": "interactive_launch_failed",
         | 
| 411 | 
            +
                                "error": str(error),
         | 
| 412 | 
            +
                                "exception_type": error_type,
         | 
| 413 | 
            +
                            }
         | 
| 414 | 
            +
                        )
         | 
| 415 | 
            +
             | 
| 403 416 | 
             
                    # Notify WebSocket of error
         | 
| 404 417 | 
             
                    if self.runner.websocket_server:
         | 
| 405 418 | 
             
                        self.runner.websocket_server.claude_status_changed(
         | 
| 406 | 
            -
                            status="error",
         | 
| 407 | 
            -
                            message=f"Failed to launch Claude: {error}"
         | 
| 419 | 
            +
                            status="error", message=f"Failed to launch Claude: {error}"
         | 
| 408 420 | 
             
                        )
         | 
| 409 | 
            -
             | 
| 421 | 
            +
             | 
| 410 422 | 
             
                def _handle_keyboard_interrupt(self) -> None:
         | 
| 411 423 | 
             
                    """Handle keyboard interrupt during session."""
         | 
| 412 424 | 
             
                    print("\n⚠️  Session interrupted by user")
         | 
| 413 | 
            -
             | 
| 425 | 
            +
             | 
| 414 426 | 
             
                    if self.runner.project_logger:
         | 
| 415 427 | 
             
                        self.runner.project_logger.log_system(
         | 
| 416 | 
            -
                            "Session interrupted by user",
         | 
| 417 | 
            -
             | 
| 418 | 
            -
             | 
| 428 | 
            +
                            "Session interrupted by user", level="INFO", component="session"
         | 
| 429 | 
            +
                        )
         | 
| 430 | 
            +
                        self.runner._log_session_event(
         | 
| 431 | 
            +
                            {"event": "session_interrupted", "reason": "user_interrupt"}
         | 
| 419 432 | 
             
                        )
         | 
| 420 | 
            -
             | 
| 421 | 
            -
                            "event": "session_interrupted",
         | 
| 422 | 
            -
                            "reason": "user_interrupt"
         | 
| 423 | 
            -
                        })
         | 
| 424 | 
            -
                
         | 
| 433 | 
            +
             | 
| 425 434 | 
             
                def _attempt_fallback_launch(self, environment: Dict[str, Any]) -> bool:
         | 
| 426 435 | 
             
                    """Attempt fallback launch using subprocess."""
         | 
| 427 436 | 
             
                    print("\n🔄 Attempting fallback launch method...")
         | 
| 428 | 
            -
             | 
| 437 | 
            +
             | 
| 429 438 | 
             
                    try:
         | 
| 430 | 
            -
                        cmd = environment[ | 
| 431 | 
            -
                        env = environment[ | 
| 432 | 
            -
             | 
| 439 | 
            +
                        cmd = environment["command"]
         | 
| 440 | 
            +
                        env = environment["environment"]
         | 
| 441 | 
            +
             | 
| 433 442 | 
             
                        result = subprocess.run(cmd, stdin=None, stdout=None, stderr=None, env=env)
         | 
| 434 | 
            -
             | 
| 443 | 
            +
             | 
| 435 444 | 
             
                        if result.returncode == 0:
         | 
| 436 445 | 
             
                            if self.runner.project_logger:
         | 
| 437 446 | 
             
                                self.runner.project_logger.log_system(
         | 
| 438 447 | 
             
                                    "Interactive session completed (subprocess fallback)",
         | 
| 439 448 | 
             
                                    level="INFO",
         | 
| 440 | 
            -
                                    component="session"
         | 
| 449 | 
            +
                                    component="session",
         | 
| 441 450 | 
             
                                )
         | 
| 442 451 | 
             
                            return True
         | 
| 443 452 | 
             
                        else:
         | 
| 444 453 | 
             
                            print(f"⚠️  Claude exited with code {result.returncode}")
         | 
| 445 454 | 
             
                            return False
         | 
| 446 | 
            -
             | 
| 455 | 
            +
             | 
| 447 456 | 
             
                    except FileNotFoundError:
         | 
| 448 457 | 
             
                        print("❌ Fallback failed: Claude CLI not found in PATH")
         | 
| 449 458 | 
             
                        print("\n💡 To fix this issue:")
         | 
| @@ -456,24 +465,25 @@ class InteractiveSession: | |
| 456 465 | 
             
                    except Exception as e:
         | 
| 457 466 | 
             
                        print(f"❌ Fallback failed with unexpected error: {e}")
         | 
| 458 467 | 
             
                        return False
         | 
| 459 | 
            -
             | 
| 468 | 
            +
             | 
| 460 469 | 
             
                def _show_available_agents(self) -> bool:
         | 
| 461 470 | 
             
                    """Show available agents in the system."""
         | 
| 462 471 | 
             
                    try:
         | 
| 463 472 | 
             
                        from claude_mpm.cli import _get_agent_versions_display
         | 
| 473 | 
            +
             | 
| 464 474 | 
             
                        agent_versions = _get_agent_versions_display()
         | 
| 465 | 
            -
             | 
| 475 | 
            +
             | 
| 466 476 | 
             
                        if agent_versions:
         | 
| 467 477 | 
             
                            print(agent_versions)
         | 
| 468 478 | 
             
                        else:
         | 
| 469 479 | 
             
                            print("No deployed agents found")
         | 
| 470 480 | 
             
                            print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
         | 
| 471 | 
            -
             | 
| 481 | 
            +
             | 
| 472 482 | 
             
                        return True
         | 
| 473 | 
            -
             | 
| 483 | 
            +
             | 
| 474 484 | 
             
                    except ImportError:
         | 
| 475 485 | 
             
                        print("Error: CLI module not available")
         | 
| 476 486 | 
             
                        return False
         | 
| 477 487 | 
             
                    except Exception as e:
         | 
| 478 488 | 
             
                        print(f"Error getting agent versions: {e}")
         | 
| 479 | 
            -
                        return False
         | 
| 489 | 
            +
                        return False
         |