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
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            from pathlib import Path
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            """
         | 
| 2 4 | 
             
            Dynamic agent dependency loader for runtime dependency management.
         | 
| 3 5 |  | 
| @@ -6,15 +8,15 @@ at runtime, rather than requiring all possible agent dependencies to be | |
| 6 8 | 
             
            installed upfront.
         | 
| 7 9 | 
             
            """
         | 
| 8 10 |  | 
| 11 | 
            +
            import hashlib
         | 
| 9 12 | 
             
            import json
         | 
| 13 | 
            +
            import logging
         | 
| 10 14 | 
             
            import subprocess
         | 
| 11 15 | 
             
            import sys
         | 
| 12 | 
            -
            import hashlib
         | 
| 13 16 | 
             
            import time
         | 
| 14 | 
            -
            from  | 
| 15 | 
            -
             | 
| 16 | 
            -
            import  | 
| 17 | 
            -
            from packaging.requirements import Requirement, InvalidRequirement
         | 
| 17 | 
            +
            from typing import Dict, List, Optional, Set, Tuple
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            from packaging.requirements import InvalidRequirement, Requirement
         | 
| 18 20 |  | 
| 19 21 | 
             
            from ..core.logger import get_logger
         | 
| 20 22 |  | 
| @@ -24,15 +26,15 @@ logger = get_logger(__name__) | |
| 24 26 | 
             
            class AgentDependencyLoader:
         | 
| 25 27 | 
             
                """
         | 
| 26 28 | 
             
                Dynamically loads and manages dependencies for deployed agents.
         | 
| 27 | 
            -
             | 
| 29 | 
            +
             | 
| 28 30 | 
             
                Only checks/installs dependencies for agents that are actually deployed
         | 
| 29 31 | 
             
                and being used, rather than all possible agents.
         | 
| 30 32 | 
             
                """
         | 
| 31 | 
            -
             | 
| 33 | 
            +
             | 
| 32 34 | 
             
                def __init__(self, auto_install: bool = False):
         | 
| 33 35 | 
             
                    """
         | 
| 34 36 | 
             
                    Initialize the agent dependency loader.
         | 
| 35 | 
            -
             | 
| 37 | 
            +
             | 
| 36 38 | 
             
                    Args:
         | 
| 37 39 | 
             
                        auto_install: If True, automatically install missing dependencies.
         | 
| 38 40 | 
             
                                     If False, only check and report missing dependencies.
         | 
| @@ -42,665 +44,873 @@ class AgentDependencyLoader: | |
| 42 44 | 
             
                    self.agent_dependencies: Dict[str, Dict] = {}
         | 
| 43 45 | 
             
                    self.missing_dependencies: Dict[str, List[str]] = {}
         | 
| 44 46 | 
             
                    self.checked_packages: Set[str] = set()
         | 
| 45 | 
            -
                    self.deployment_state_file =  | 
| 46 | 
            -
             | 
| 47 | 
            +
                    self.deployment_state_file = (
         | 
| 48 | 
            +
                        Path.cwd() / ".claude" / "agents" / ".mpm_deployment_state"
         | 
| 49 | 
            +
                    )
         | 
| 50 | 
            +
             | 
| 47 51 | 
             
                def discover_deployed_agents(self) -> Dict[str, Path]:
         | 
| 48 52 | 
             
                    """
         | 
| 49 53 | 
             
                    Discover which agents are currently deployed in .claude/agents/
         | 
| 50 | 
            -
             | 
| 54 | 
            +
             | 
| 51 55 | 
             
                    Returns:
         | 
| 52 56 | 
             
                        Dictionary mapping agent IDs to their file paths
         | 
| 53 57 | 
             
                    """
         | 
| 54 58 | 
             
                    deployed_agents = {}
         | 
| 55 59 | 
             
                    claude_agents_dir = Path.cwd() / ".claude" / "agents"
         | 
| 56 | 
            -
             | 
| 60 | 
            +
             | 
| 57 61 | 
             
                    if not claude_agents_dir.exists():
         | 
| 58 62 | 
             
                        logger.debug("No .claude/agents directory found")
         | 
| 59 63 | 
             
                        return deployed_agents
         | 
| 60 | 
            -
             | 
| 64 | 
            +
             | 
| 61 65 | 
             
                    # Scan for deployed agent markdown files
         | 
| 62 66 | 
             
                    for agent_file in claude_agents_dir.glob("*.md"):
         | 
| 63 67 | 
             
                        agent_id = agent_file.stem
         | 
| 64 68 | 
             
                        deployed_agents[agent_id] = agent_file
         | 
| 65 69 | 
             
                        logger.debug(f"Found deployed agent: {agent_id}")
         | 
| 66 | 
            -
             | 
| 70 | 
            +
             | 
| 67 71 | 
             
                    logger.info(f"Discovered {len(deployed_agents)} deployed agents")
         | 
| 68 72 | 
             
                    self.deployed_agents = deployed_agents
         | 
| 69 73 | 
             
                    return deployed_agents
         | 
| 70 | 
            -
             | 
| 74 | 
            +
             | 
| 71 75 | 
             
                def load_agent_dependencies(self) -> Dict[str, Dict]:
         | 
| 72 76 | 
             
                    """
         | 
| 73 77 | 
             
                    Load dependency information for deployed agents from their source configs.
         | 
| 74 | 
            -
             | 
| 78 | 
            +
             | 
| 75 79 | 
             
                    Returns:
         | 
| 76 80 | 
             
                        Dictionary mapping agent IDs to their dependency requirements
         | 
| 77 81 | 
             
                    """
         | 
| 78 82 | 
             
                    agent_dependencies = {}
         | 
| 79 | 
            -
             | 
| 83 | 
            +
             | 
| 80 84 | 
             
                    # Define paths to check for agent configs (in precedence order)
         | 
| 81 85 | 
             
                    config_paths = [
         | 
| 82 86 | 
             
                        Path.cwd() / ".claude-mpm" / "agents",  # PROJECT
         | 
| 83 87 | 
             
                        Path.home() / ".claude-mpm" / "agents",  # USER
         | 
| 84 | 
            -
                        Path.cwd() / "src" / "claude_mpm" / "agents" / "templates"  # SYSTEM
         | 
| 88 | 
            +
                        Path.cwd() / "src" / "claude_mpm" / "agents" / "templates",  # SYSTEM
         | 
| 85 89 | 
             
                    ]
         | 
| 86 | 
            -
             | 
| 90 | 
            +
             | 
| 87 91 | 
             
                    for agent_id in self.deployed_agents:
         | 
| 88 92 | 
             
                        # Try to find the agent's JSON config
         | 
| 89 93 | 
             
                        for config_dir in config_paths:
         | 
| 90 94 | 
             
                            config_file = config_dir / f"{agent_id}.json"
         | 
| 91 95 | 
             
                            if config_file.exists():
         | 
| 92 96 | 
             
                                try:
         | 
| 93 | 
            -
                                    with open(config_file,  | 
| 97 | 
            +
                                    with open(config_file, "r") as f:
         | 
| 94 98 | 
             
                                        config = json.load(f)
         | 
| 95 | 
            -
                                        if  | 
| 96 | 
            -
                                            agent_dependencies[agent_id] = config[ | 
| 99 | 
            +
                                        if "dependencies" in config:
         | 
| 100 | 
            +
                                            agent_dependencies[agent_id] = config["dependencies"]
         | 
| 97 101 | 
             
                                            logger.debug(f"Loaded dependencies for {agent_id}")
         | 
| 98 102 | 
             
                                            break
         | 
| 99 103 | 
             
                                except Exception as e:
         | 
| 100 104 | 
             
                                    logger.warning(f"Failed to load config for {agent_id}: {e}")
         | 
| 101 | 
            -
             | 
| 105 | 
            +
             | 
| 102 106 | 
             
                    self.agent_dependencies = agent_dependencies
         | 
| 103 107 | 
             
                    logger.info(f"Loaded dependencies for {len(agent_dependencies)} agents")
         | 
| 104 108 | 
             
                    return agent_dependencies
         | 
| 105 | 
            -
             | 
| 109 | 
            +
             | 
| 106 110 | 
             
                def check_python_dependency(self, package_spec: str) -> Tuple[bool, Optional[str]]:
         | 
| 107 111 | 
             
                    """
         | 
| 108 112 | 
             
                    Check if a Python package dependency is satisfied.
         | 
| 109 | 
            -
             | 
| 113 | 
            +
             | 
| 110 114 | 
             
                    Args:
         | 
| 111 115 | 
             
                        package_spec: Package specification (e.g., "pandas>=2.0.0")
         | 
| 112 | 
            -
             | 
| 116 | 
            +
             | 
| 113 117 | 
             
                    Returns:
         | 
| 114 118 | 
             
                        Tuple of (is_satisfied, installed_version)
         | 
| 115 119 | 
             
                    """
         | 
| 116 120 | 
             
                    try:
         | 
| 117 121 | 
             
                        req = Requirement(package_spec)
         | 
| 118 122 | 
             
                        package_name = req.name
         | 
| 119 | 
            -
             | 
| 123 | 
            +
             | 
| 120 124 | 
             
                        # Skip if already checked
         | 
| 121 125 | 
             
                        if package_name in self.checked_packages:
         | 
| 122 126 | 
             
                            return True, None
         | 
| 123 | 
            -
             | 
| 127 | 
            +
             | 
| 128 | 
            +
                        # Check if it's a built-in module first
         | 
| 129 | 
            +
                        if self._is_builtin_module(package_name):
         | 
| 130 | 
            +
                            self.checked_packages.add(package_name)
         | 
| 131 | 
            +
                            return True, "built-in"
         | 
| 132 | 
            +
             | 
| 124 133 | 
             
                        # Try to import and check version
         | 
| 125 134 | 
             
                        try:
         | 
| 126 135 | 
             
                            import importlib.metadata
         | 
| 136 | 
            +
             | 
| 127 137 | 
             
                            try:
         | 
| 128 138 | 
             
                                version = importlib.metadata.version(package_name)
         | 
| 129 139 | 
             
                                self.checked_packages.add(package_name)
         | 
| 130 | 
            -
             | 
| 140 | 
            +
             | 
| 131 141 | 
             
                                # Check if version satisfies requirement
         | 
| 132 142 | 
             
                                if req.specifier.contains(version):
         | 
| 133 143 | 
             
                                    return True, version
         | 
| 134 144 | 
             
                                else:
         | 
| 135 | 
            -
                                    logger.debug( | 
| 145 | 
            +
                                    logger.debug(
         | 
| 146 | 
            +
                                        f"{package_name} {version} does not satisfy {req.specifier}"
         | 
| 147 | 
            +
                                    )
         | 
| 136 148 | 
             
                                    return False, version
         | 
| 137 | 
            -
             | 
| 149 | 
            +
             | 
| 138 150 | 
             
                            except importlib.metadata.PackageNotFoundError:
         | 
| 139 151 | 
             
                                return False, None
         | 
| 140 | 
            -
             | 
| 152 | 
            +
             | 
| 141 153 | 
             
                        except ImportError:
         | 
| 142 154 | 
             
                            # Fallback for older Python versions
         | 
| 143 155 | 
             
                            try:
         | 
| 144 156 | 
             
                                import pkg_resources
         | 
| 157 | 
            +
             | 
| 145 158 | 
             
                                version = pkg_resources.get_distribution(package_name).version
         | 
| 146 159 | 
             
                                self.checked_packages.add(package_name)
         | 
| 147 | 
            -
             | 
| 160 | 
            +
             | 
| 148 161 | 
             
                                if req.specifier.contains(version):
         | 
| 149 162 | 
             
                                    return True, version
         | 
| 150 163 | 
             
                                else:
         | 
| 151 164 | 
             
                                    return False, version
         | 
| 152 | 
            -
             | 
| 165 | 
            +
             | 
| 153 166 | 
             
                            except pkg_resources.DistributionNotFound:
         | 
| 154 167 | 
             
                                return False, None
         | 
| 155 | 
            -
             | 
| 168 | 
            +
             | 
| 156 169 | 
             
                    except InvalidRequirement as e:
         | 
| 157 170 | 
             
                        logger.warning(f"Invalid requirement specification: {package_spec}: {e}")
         | 
| 158 171 | 
             
                        return False, None
         | 
| 159 | 
            -
             | 
| 172 | 
            +
             | 
| 173 | 
            +
                def _is_builtin_module(self, module_name: str) -> bool:
         | 
| 174 | 
            +
                    """
         | 
| 175 | 
            +
                    Check if a module is a built-in Python module.
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                    Args:
         | 
| 178 | 
            +
                        module_name: Name of the module to check
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                    Returns:
         | 
| 181 | 
            +
                        True if the module is built-in, False otherwise
         | 
| 182 | 
            +
                    """
         | 
| 183 | 
            +
                    # List of common built-in modules that don't have distribution metadata
         | 
| 184 | 
            +
                    builtin_modules = {
         | 
| 185 | 
            +
                        "json",
         | 
| 186 | 
            +
                        "pathlib",
         | 
| 187 | 
            +
                        "os",
         | 
| 188 | 
            +
                        "sys",
         | 
| 189 | 
            +
                        "datetime",
         | 
| 190 | 
            +
                        "time",
         | 
| 191 | 
            +
                        "math",
         | 
| 192 | 
            +
                        "random",
         | 
| 193 | 
            +
                        "collections",
         | 
| 194 | 
            +
                        "itertools",
         | 
| 195 | 
            +
                        "functools",
         | 
| 196 | 
            +
                        "operator",
         | 
| 197 | 
            +
                        "copy",
         | 
| 198 | 
            +
                        "pickle",
         | 
| 199 | 
            +
                        "sqlite3",
         | 
| 200 | 
            +
                        "urllib",
         | 
| 201 | 
            +
                        "http",
         | 
| 202 | 
            +
                        "email",
         | 
| 203 | 
            +
                        "html",
         | 
| 204 | 
            +
                        "xml",
         | 
| 205 | 
            +
                        "csv",
         | 
| 206 | 
            +
                        "configparser",
         | 
| 207 | 
            +
                        "logging",
         | 
| 208 | 
            +
                        "unittest",
         | 
| 209 | 
            +
                        "doctest",
         | 
| 210 | 
            +
                        "pdb",
         | 
| 211 | 
            +
                        "profile",
         | 
| 212 | 
            +
                        "timeit",
         | 
| 213 | 
            +
                        "trace",
         | 
| 214 | 
            +
                        "gc",
         | 
| 215 | 
            +
                        "weakref",
         | 
| 216 | 
            +
                        "types",
         | 
| 217 | 
            +
                        "inspect",
         | 
| 218 | 
            +
                        "importlib",
         | 
| 219 | 
            +
                        "pkgutil",
         | 
| 220 | 
            +
                        "modulefinder",
         | 
| 221 | 
            +
                        "runpy",
         | 
| 222 | 
            +
                        "ast",
         | 
| 223 | 
            +
                        "symtable",
         | 
| 224 | 
            +
                        "keyword",
         | 
| 225 | 
            +
                        "token",
         | 
| 226 | 
            +
                        "tokenize",
         | 
| 227 | 
            +
                        "tabnanny",
         | 
| 228 | 
            +
                        "pyclbr",
         | 
| 229 | 
            +
                        "py_compile",
         | 
| 230 | 
            +
                        "compileall",
         | 
| 231 | 
            +
                        "dis",
         | 
| 232 | 
            +
                        "pickletools",
         | 
| 233 | 
            +
                        "platform",
         | 
| 234 | 
            +
                        "ctypes",
         | 
| 235 | 
            +
                        "struct",
         | 
| 236 | 
            +
                        "codecs",
         | 
| 237 | 
            +
                        "unicodedata",
         | 
| 238 | 
            +
                        "stringprep",
         | 
| 239 | 
            +
                        "readline",
         | 
| 240 | 
            +
                        "rlcompleter",
         | 
| 241 | 
            +
                        "subprocess",
         | 
| 242 | 
            +
                        "sched",
         | 
| 243 | 
            +
                        "queue",
         | 
| 244 | 
            +
                        "threading",
         | 
| 245 | 
            +
                        "multiprocessing",
         | 
| 246 | 
            +
                        "concurrent",
         | 
| 247 | 
            +
                        "asyncio",
         | 
| 248 | 
            +
                        "socket",
         | 
| 249 | 
            +
                        "ssl",
         | 
| 250 | 
            +
                        "select",
         | 
| 251 | 
            +
                        "selectors",
         | 
| 252 | 
            +
                        "signal",
         | 
| 253 | 
            +
                        "mmap",
         | 
| 254 | 
            +
                        "errno",
         | 
| 255 | 
            +
                        "io",
         | 
| 256 | 
            +
                        "tempfile",
         | 
| 257 | 
            +
                        "glob",
         | 
| 258 | 
            +
                        "fnmatch",
         | 
| 259 | 
            +
                        "linecache",
         | 
| 260 | 
            +
                        "shutil",
         | 
| 261 | 
            +
                        "stat",
         | 
| 262 | 
            +
                        "filecmp",
         | 
| 263 | 
            +
                        "tarfile",
         | 
| 264 | 
            +
                        "zipfile",
         | 
| 265 | 
            +
                        "gzip",
         | 
| 266 | 
            +
                        "bz2",
         | 
| 267 | 
            +
                        "lzma",
         | 
| 268 | 
            +
                        "zlib",
         | 
| 269 | 
            +
                        "hashlib",
         | 
| 270 | 
            +
                        "hmac",
         | 
| 271 | 
            +
                        "secrets",
         | 
| 272 | 
            +
                        "base64",
         | 
| 273 | 
            +
                        "binascii",
         | 
| 274 | 
            +
                        "quopri",
         | 
| 275 | 
            +
                        "uu",
         | 
| 276 | 
            +
                        "string",
         | 
| 277 | 
            +
                        "re",
         | 
| 278 | 
            +
                        "difflib",
         | 
| 279 | 
            +
                        "textwrap",
         | 
| 280 | 
            +
                        "unicodedata",
         | 
| 281 | 
            +
                        "stringprep",
         | 
| 282 | 
            +
                        "calendar",
         | 
| 283 | 
            +
                        "locale",
         | 
| 284 | 
            +
                        "gettext",
         | 
| 285 | 
            +
                        "argparse",
         | 
| 286 | 
            +
                        "optparse",
         | 
| 287 | 
            +
                        "getopt",
         | 
| 288 | 
            +
                        "shlex",
         | 
| 289 | 
            +
                        "cmd",
         | 
| 290 | 
            +
                        "pprint",
         | 
| 291 | 
            +
                        "reprlib",
         | 
| 292 | 
            +
                        "enum",
         | 
| 293 | 
            +
                        "numbers",
         | 
| 294 | 
            +
                        "decimal",
         | 
| 295 | 
            +
                        "fractions",
         | 
| 296 | 
            +
                        "statistics",
         | 
| 297 | 
            +
                        "array",
         | 
| 298 | 
            +
                        "bisect",
         | 
| 299 | 
            +
                        "heapq",
         | 
| 300 | 
            +
                        "contextlib",
         | 
| 301 | 
            +
                        "abc",
         | 
| 302 | 
            +
                        "atexit",
         | 
| 303 | 
            +
                        "traceback",
         | 
| 304 | 
            +
                        "warnings",
         | 
| 305 | 
            +
                        "dataclasses",
         | 
| 306 | 
            +
                        "graphlib",
         | 
| 307 | 
            +
                    }
         | 
| 308 | 
            +
             | 
| 309 | 
            +
                    # Check if it's in our known built-in modules
         | 
| 310 | 
            +
                    if module_name in builtin_modules:
         | 
| 311 | 
            +
                        return True
         | 
| 312 | 
            +
             | 
| 313 | 
            +
                    # Try to import it and check if it's a built-in module
         | 
| 314 | 
            +
                    try:
         | 
| 315 | 
            +
                        import importlib.util
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                        spec = importlib.util.find_spec(module_name)
         | 
| 318 | 
            +
                        if spec is not None and spec.origin is None:
         | 
| 319 | 
            +
                            # Built-in modules have spec.origin as None
         | 
| 320 | 
            +
                            return True
         | 
| 321 | 
            +
                    except (ImportError, ModuleNotFoundError, ValueError):
         | 
| 322 | 
            +
                        pass
         | 
| 323 | 
            +
             | 
| 324 | 
            +
                    return False
         | 
| 325 | 
            +
             | 
| 160 326 | 
             
                def check_system_dependency(self, command: str) -> bool:
         | 
| 161 327 | 
             
                    """
         | 
| 162 328 | 
             
                    Check if a system command is available in PATH.
         | 
| 163 | 
            -
             | 
| 329 | 
            +
             | 
| 164 330 | 
             
                    Args:
         | 
| 165 331 | 
             
                        command: System command to check (e.g., "git")
         | 
| 166 | 
            -
             | 
| 332 | 
            +
             | 
| 167 333 | 
             
                    Returns:
         | 
| 168 334 | 
             
                        True if command is available, False otherwise
         | 
| 169 335 | 
             
                    """
         | 
| 170 336 | 
             
                    try:
         | 
| 171 337 | 
             
                        result = subprocess.run(
         | 
| 172 | 
            -
                            ["which", command],
         | 
| 173 | 
            -
                            capture_output=True,
         | 
| 174 | 
            -
                            text=True,
         | 
| 175 | 
            -
                            timeout=5
         | 
| 338 | 
            +
                            ["which", command], capture_output=True, text=True, timeout=5
         | 
| 176 339 | 
             
                        )
         | 
| 177 340 | 
             
                        return result.returncode == 0
         | 
| 178 341 | 
             
                    except Exception:
         | 
| 179 342 | 
             
                        return False
         | 
| 180 | 
            -
             | 
| 343 | 
            +
             | 
| 181 344 | 
             
                def analyze_dependencies(self) -> Dict[str, Dict]:
         | 
| 182 345 | 
             
                    """
         | 
| 183 346 | 
             
                    Analyze dependencies for all deployed agents.
         | 
| 184 | 
            -
             | 
| 347 | 
            +
             | 
| 185 348 | 
             
                    Returns:
         | 
| 186 349 | 
             
                        Analysis results including missing and satisfied dependencies
         | 
| 187 350 | 
             
                    """
         | 
| 188 351 | 
             
                    results = {
         | 
| 189 | 
            -
                         | 
| 190 | 
            -
                         | 
| 191 | 
            -
                             | 
| 192 | 
            -
                             | 
| 193 | 
            -
                             | 
| 194 | 
            -
                             | 
| 195 | 
            -
                             | 
| 196 | 
            -
                             | 
| 197 | 
            -
                        }
         | 
| 352 | 
            +
                        "agents": {},
         | 
| 353 | 
            +
                        "summary": {
         | 
| 354 | 
            +
                            "total_agents": len(self.deployed_agents),
         | 
| 355 | 
            +
                            "agents_with_deps": 0,
         | 
| 356 | 
            +
                            "missing_python": [],
         | 
| 357 | 
            +
                            "missing_system": [],
         | 
| 358 | 
            +
                            "satisfied_python": [],
         | 
| 359 | 
            +
                            "satisfied_system": [],
         | 
| 360 | 
            +
                        },
         | 
| 198 361 | 
             
                    }
         | 
| 199 | 
            -
             | 
| 362 | 
            +
             | 
| 200 363 | 
             
                    for agent_id, deps in self.agent_dependencies.items():
         | 
| 201 364 | 
             
                        agent_result = {
         | 
| 202 | 
            -
                             | 
| 203 | 
            -
                             | 
| 365 | 
            +
                            "python": {"satisfied": [], "missing": [], "outdated": []},
         | 
| 366 | 
            +
                            "system": {"satisfied": [], "missing": []},
         | 
| 204 367 | 
             
                        }
         | 
| 205 | 
            -
             | 
| 368 | 
            +
             | 
| 206 369 | 
             
                        # Check Python dependencies
         | 
| 207 | 
            -
                        if  | 
| 208 | 
            -
                            for dep_spec in deps[ | 
| 370 | 
            +
                        if "python" in deps:
         | 
| 371 | 
            +
                            for dep_spec in deps["python"]:
         | 
| 209 372 | 
             
                                is_satisfied, version = self.check_python_dependency(dep_spec)
         | 
| 210 373 | 
             
                                if is_satisfied:
         | 
| 211 | 
            -
                                    agent_result[ | 
| 212 | 
            -
                                    if dep_spec not in results[ | 
| 213 | 
            -
                                        results[ | 
| 374 | 
            +
                                    agent_result["python"]["satisfied"].append(dep_spec)
         | 
| 375 | 
            +
                                    if dep_spec not in results["summary"]["satisfied_python"]:
         | 
| 376 | 
            +
                                        results["summary"]["satisfied_python"].append(dep_spec)
         | 
| 214 377 | 
             
                                else:
         | 
| 215 378 | 
             
                                    if version:  # Installed but wrong version
         | 
| 216 | 
            -
                                        agent_result[ | 
| 379 | 
            +
                                        agent_result["python"]["outdated"].append(
         | 
| 380 | 
            +
                                            f"{dep_spec} (have {version})"
         | 
| 381 | 
            +
                                        )
         | 
| 217 382 | 
             
                                    else:  # Not installed
         | 
| 218 | 
            -
                                        agent_result[ | 
| 219 | 
            -
                                        if dep_spec not in results[ | 
| 220 | 
            -
                                            results[ | 
| 221 | 
            -
             | 
| 383 | 
            +
                                        agent_result["python"]["missing"].append(dep_spec)
         | 
| 384 | 
            +
                                        if dep_spec not in results["summary"]["missing_python"]:
         | 
| 385 | 
            +
                                            results["summary"]["missing_python"].append(dep_spec)
         | 
| 386 | 
            +
             | 
| 222 387 | 
             
                        # Check system dependencies
         | 
| 223 | 
            -
                        if  | 
| 224 | 
            -
                            for command in deps[ | 
| 388 | 
            +
                        if "system" in deps:
         | 
| 389 | 
            +
                            for command in deps["system"]:
         | 
| 225 390 | 
             
                                if self.check_system_dependency(command):
         | 
| 226 | 
            -
                                    agent_result[ | 
| 227 | 
            -
                                    if command not in results[ | 
| 228 | 
            -
                                        results[ | 
| 391 | 
            +
                                    agent_result["system"]["satisfied"].append(command)
         | 
| 392 | 
            +
                                    if command not in results["summary"]["satisfied_system"]:
         | 
| 393 | 
            +
                                        results["summary"]["satisfied_system"].append(command)
         | 
| 229 394 | 
             
                                else:
         | 
| 230 | 
            -
                                    agent_result[ | 
| 231 | 
            -
                                    if command not in results[ | 
| 232 | 
            -
                                        results[ | 
| 233 | 
            -
             | 
| 234 | 
            -
                        results[ | 
| 235 | 
            -
                        if  | 
| 236 | 
            -
                            results[ | 
| 237 | 
            -
             | 
| 395 | 
            +
                                    agent_result["system"]["missing"].append(command)
         | 
| 396 | 
            +
                                    if command not in results["summary"]["missing_system"]:
         | 
| 397 | 
            +
                                        results["summary"]["missing_system"].append(command)
         | 
| 398 | 
            +
             | 
| 399 | 
            +
                        results["agents"][agent_id] = agent_result
         | 
| 400 | 
            +
                        if "python" in deps or "system" in deps:
         | 
| 401 | 
            +
                            results["summary"]["agents_with_deps"] += 1
         | 
| 402 | 
            +
             | 
| 238 403 | 
             
                    return results
         | 
| 239 | 
            -
             | 
| 240 | 
            -
                def check_python_compatibility( | 
| 404 | 
            +
             | 
| 405 | 
            +
                def check_python_compatibility(
         | 
| 406 | 
            +
                    self, dependencies: List[str]
         | 
| 407 | 
            +
                ) -> Tuple[List[str], List[str]]:
         | 
| 241 408 | 
             
                    """
         | 
| 242 409 | 
             
                    Check which dependencies are compatible with current Python version.
         | 
| 243 | 
            -
             | 
| 410 | 
            +
             | 
| 244 411 | 
             
                    Args:
         | 
| 245 412 | 
             
                        dependencies: List of package specifications to check
         | 
| 246 | 
            -
             | 
| 413 | 
            +
             | 
| 247 414 | 
             
                    Returns:
         | 
| 248 415 | 
             
                        Tuple of (compatible_deps, incompatible_deps)
         | 
| 249 416 | 
             
                    """
         | 
| 250 417 | 
             
                    import sys
         | 
| 418 | 
            +
             | 
| 251 419 | 
             
                    current_version = f"{sys.version_info.major}.{sys.version_info.minor}"
         | 
| 252 420 | 
             
                    compatible = []
         | 
| 253 421 | 
             
                    incompatible = []
         | 
| 254 | 
            -
             | 
| 422 | 
            +
             | 
| 255 423 | 
             
                    for dep in dependencies:
         | 
| 256 424 | 
             
                        try:
         | 
| 257 425 | 
             
                            # For known problematic packages in Python 3.13
         | 
| 258 426 | 
             
                            req = Requirement(dep)
         | 
| 259 427 | 
             
                            package_name = req.name.lower()
         | 
| 260 | 
            -
             | 
| 428 | 
            +
             | 
| 261 429 | 
             
                            # Known Python 3.13 incompatibilities
         | 
| 262 430 | 
             
                            if sys.version_info >= (3, 13):
         | 
| 263 | 
            -
                                if package_name in [ | 
| 431 | 
            +
                                if package_name in ["ydata-profiling", "pandas-profiling"]:
         | 
| 264 432 | 
             
                                    incompatible.append(f"{dep} (requires Python <3.13)")
         | 
| 265 433 | 
             
                                    continue
         | 
| 266 | 
            -
                                elif package_name ==  | 
| 434 | 
            +
                                elif package_name == "apache-airflow":
         | 
| 267 435 | 
             
                                    incompatible.append(f"{dep} (requires Python <3.13)")
         | 
| 268 436 | 
             
                                    continue
         | 
| 269 | 
            -
             | 
| 437 | 
            +
             | 
| 270 438 | 
             
                            # Default to compatible if we don't know
         | 
| 271 439 | 
             
                            compatible.append(dep)
         | 
| 272 | 
            -
             | 
| 440 | 
            +
             | 
| 273 441 | 
             
                        except Exception as e:
         | 
| 274 442 | 
             
                            logger.warning(f"Could not check compatibility for {dep}: {e}")
         | 
| 275 443 | 
             
                            compatible.append(dep)  # Assume compatible if we can't check
         | 
| 276 | 
            -
             | 
| 444 | 
            +
             | 
| 277 445 | 
             
                    return compatible, incompatible
         | 
| 278 | 
            -
             | 
| 446 | 
            +
             | 
| 279 447 | 
             
                def install_missing_dependencies(self, dependencies: List[str]) -> Tuple[bool, str]:
         | 
| 280 448 | 
             
                    """
         | 
| 281 449 | 
             
                    Install missing Python dependencies using robust retry logic.
         | 
| 282 | 
            -
             | 
| 450 | 
            +
             | 
| 283 451 | 
             
                    WHY: Network issues and temporary package unavailability can cause
         | 
| 284 452 | 
             
                    installation failures. Using the robust installer with retries
         | 
| 285 453 | 
             
                    significantly improves success rate.
         | 
| 286 | 
            -
             | 
| 454 | 
            +
             | 
| 287 455 | 
             
                    Args:
         | 
| 288 456 | 
             
                        dependencies: List of package specifications to install
         | 
| 289 | 
            -
             | 
| 457 | 
            +
             | 
| 290 458 | 
             
                    Returns:
         | 
| 291 459 | 
             
                        Tuple of (success, error_message)
         | 
| 292 460 | 
             
                    """
         | 
| 293 461 | 
             
                    if not dependencies:
         | 
| 294 462 | 
             
                        return True, ""
         | 
| 295 | 
            -
             | 
| 463 | 
            +
             | 
| 296 464 | 
             
                    # Check Python version compatibility first
         | 
| 297 465 | 
             
                    compatible, incompatible = self.check_python_compatibility(dependencies)
         | 
| 298 | 
            -
             | 
| 466 | 
            +
             | 
| 299 467 | 
             
                    if incompatible:
         | 
| 300 468 | 
             
                        logger.warning(f"Skipping {len(incompatible)} incompatible packages:")
         | 
| 301 469 | 
             
                        for dep in incompatible:
         | 
| 302 470 | 
             
                            logger.warning(f"  - {dep}")
         | 
| 303 | 
            -
             | 
| 471 | 
            +
             | 
| 304 472 | 
             
                    if not compatible:
         | 
| 305 473 | 
             
                        return True, "No compatible packages to install"
         | 
| 306 | 
            -
             | 
| 474 | 
            +
             | 
| 307 475 | 
             
                    # Use robust installer with retry logic
         | 
| 308 476 | 
             
                    try:
         | 
| 309 477 | 
             
                        from .robust_installer import RobustPackageInstaller
         | 
| 310 | 
            -
             | 
| 311 | 
            -
                        logger.info( | 
| 478 | 
            +
             | 
| 479 | 
            +
                        logger.info(
         | 
| 480 | 
            +
                            f"Installing {len(compatible)} compatible dependencies with retry logic..."
         | 
| 481 | 
            +
                        )
         | 
| 312 482 | 
             
                        if incompatible:
         | 
| 313 | 
            -
                            logger.info( | 
| 314 | 
            -
             | 
| 483 | 
            +
                            logger.info(
         | 
| 484 | 
            +
                                f"(Skipping {len(incompatible)} incompatible with Python {sys.version_info.major}.{sys.version_info.minor})"
         | 
| 485 | 
            +
                            )
         | 
| 486 | 
            +
             | 
| 315 487 | 
             
                        # Create installer with sensible defaults
         | 
| 316 488 | 
             
                        installer = RobustPackageInstaller(
         | 
| 317 | 
            -
                            max_retries=3,
         | 
| 318 | 
            -
                            retry_delay=2.0,
         | 
| 319 | 
            -
                            timeout=300
         | 
| 489 | 
            +
                            max_retries=3, retry_delay=2.0, timeout=300
         | 
| 320 490 | 
             
                        )
         | 
| 321 | 
            -
             | 
| 491 | 
            +
             | 
| 322 492 | 
             
                        # Install packages
         | 
| 323 493 | 
             
                        successful, failed, errors = installer.install_packages(compatible)
         | 
| 324 | 
            -
             | 
| 494 | 
            +
             | 
| 325 495 | 
             
                        if failed:
         | 
| 326 496 | 
             
                            # Provide detailed error information
         | 
| 327 497 | 
             
                            error_details = []
         | 
| 328 498 | 
             
                            for pkg in failed:
         | 
| 329 499 | 
             
                                error_details.append(f"{pkg}: {errors.get(pkg, 'Unknown error')}")
         | 
| 330 | 
            -
             | 
| 331 | 
            -
                            error_msg = f"Failed to install {len(failed)} packages:\n" + "\n".join( | 
| 500 | 
            +
             | 
| 501 | 
            +
                            error_msg = f"Failed to install {len(failed)} packages:\n" + "\n".join(
         | 
| 502 | 
            +
                                error_details
         | 
| 503 | 
            +
                            )
         | 
| 332 504 | 
             
                            logger.error(error_msg)
         | 
| 333 | 
            -
             | 
| 505 | 
            +
             | 
| 334 506 | 
             
                            # Partial success handling
         | 
| 335 507 | 
             
                            if successful:
         | 
| 336 508 | 
             
                                partial_msg = f"Partially successful: installed {len(successful)} of {len(compatible)} packages"
         | 
| 337 509 | 
             
                                logger.info(partial_msg)
         | 
| 338 510 | 
             
                                if incompatible:
         | 
| 339 | 
            -
                                    return  | 
| 511 | 
            +
                                    return (
         | 
| 512 | 
            +
                                        True,
         | 
| 513 | 
            +
                                        f"{partial_msg}. Also skipped {len(incompatible)} incompatible",
         | 
| 514 | 
            +
                                    )
         | 
| 340 515 | 
             
                                return True, partial_msg
         | 
| 341 | 
            -
             | 
| 516 | 
            +
             | 
| 342 517 | 
             
                            return False, error_msg
         | 
| 343 | 
            -
             | 
| 344 | 
            -
                        logger.info( | 
| 518 | 
            +
             | 
| 519 | 
            +
                        logger.info(
         | 
| 520 | 
            +
                            f"Successfully installed all {len(successful)} compatible dependencies"
         | 
| 521 | 
            +
                        )
         | 
| 345 522 | 
             
                        if incompatible:
         | 
| 346 | 
            -
                            return  | 
| 523 | 
            +
                            return (
         | 
| 524 | 
            +
                                True,
         | 
| 525 | 
            +
                                f"Installed {len(compatible)} packages, skipped {len(incompatible)} incompatible",
         | 
| 526 | 
            +
                            )
         | 
| 347 527 | 
             
                        return True, ""
         | 
| 348 | 
            -
             | 
| 528 | 
            +
             | 
| 349 529 | 
             
                    except ImportError:
         | 
| 350 530 | 
             
                        # Fallback to simple installation if robust installer not available
         | 
| 351 | 
            -
                        logger.warning( | 
| 531 | 
            +
                        logger.warning(
         | 
| 532 | 
            +
                            "Robust installer not available, falling back to simple installation"
         | 
| 533 | 
            +
                        )
         | 
| 352 534 | 
             
                        try:
         | 
| 353 535 | 
             
                            cmd = [sys.executable, "-m", "pip", "install"] + compatible
         | 
| 354 | 
            -
             | 
| 536 | 
            +
             | 
| 355 537 | 
             
                            result = subprocess.run(
         | 
| 356 | 
            -
                                cmd,
         | 
| 357 | 
            -
                                capture_output=True,
         | 
| 358 | 
            -
                                text=True,
         | 
| 359 | 
            -
                                timeout=300
         | 
| 538 | 
            +
                                cmd, capture_output=True, text=True, timeout=300
         | 
| 360 539 | 
             
                            )
         | 
| 361 | 
            -
             | 
| 540 | 
            +
             | 
| 362 541 | 
             
                            if result.returncode == 0:
         | 
| 363 542 | 
             
                                logger.info("Successfully installed compatible dependencies")
         | 
| 364 543 | 
             
                                if incompatible:
         | 
| 365 | 
            -
                                    return  | 
| 544 | 
            +
                                    return (
         | 
| 545 | 
            +
                                        True,
         | 
| 546 | 
            +
                                        f"Installed {len(compatible)} packages, skipped {len(incompatible)} incompatible",
         | 
| 547 | 
            +
                                    )
         | 
| 366 548 | 
             
                                return True, ""
         | 
| 367 549 | 
             
                            else:
         | 
| 368 550 | 
             
                                error_msg = f"Installation failed: {result.stderr}"
         | 
| 369 551 | 
             
                                logger.error(error_msg)
         | 
| 370 552 | 
             
                                return False, error_msg
         | 
| 371 | 
            -
             | 
| 553 | 
            +
             | 
| 372 554 | 
             
                        except Exception as e:
         | 
| 373 555 | 
             
                            error_msg = f"Failed to install dependencies: {e}"
         | 
| 374 556 | 
             
                            logger.error(error_msg)
         | 
| 375 557 | 
             
                            return False, error_msg
         | 
| 376 | 
            -
             | 
| 558 | 
            +
             | 
| 377 559 | 
             
                    except Exception as e:
         | 
| 378 560 | 
             
                        error_msg = f"Failed to install dependencies: {e}"
         | 
| 379 561 | 
             
                        logger.error(error_msg)
         | 
| 380 562 | 
             
                        return False, error_msg
         | 
| 381 | 
            -
             | 
| 563 | 
            +
             | 
| 382 564 | 
             
                def load_and_check(self) -> Dict[str, Dict]:
         | 
| 383 565 | 
             
                    """
         | 
| 384 566 | 
             
                    Complete workflow: discover agents, load dependencies, and check them.
         | 
| 385 | 
            -
             | 
| 567 | 
            +
             | 
| 386 568 | 
             
                    Returns:
         | 
| 387 569 | 
             
                        Complete analysis results
         | 
| 388 570 | 
             
                    """
         | 
| 389 571 | 
             
                    # Discover deployed agents
         | 
| 390 572 | 
             
                    self.discover_deployed_agents()
         | 
| 391 | 
            -
             | 
| 573 | 
            +
             | 
| 392 574 | 
             
                    if not self.deployed_agents:
         | 
| 393 575 | 
             
                        logger.info("No deployed agents found")
         | 
| 394 | 
            -
                        return { | 
| 395 | 
            -
             | 
| 576 | 
            +
                        return {"agents": {}, "summary": {"total_agents": 0}}
         | 
| 577 | 
            +
             | 
| 396 578 | 
             
                    # Load their dependencies
         | 
| 397 579 | 
             
                    self.load_agent_dependencies()
         | 
| 398 | 
            -
             | 
| 580 | 
            +
             | 
| 399 581 | 
             
                    # Analyze what's missing
         | 
| 400 582 | 
             
                    results = self.analyze_dependencies()
         | 
| 401 | 
            -
             | 
| 583 | 
            +
             | 
| 402 584 | 
             
                    # Optionally auto-install missing dependencies
         | 
| 403 | 
            -
                    if self.auto_install and results[ | 
| 404 | 
            -
                        logger.info( | 
| 405 | 
            -
             | 
| 585 | 
            +
                    if self.auto_install and results["summary"]["missing_python"]:
         | 
| 586 | 
            +
                        logger.info(
         | 
| 587 | 
            +
                            f"Auto-installing {len(results['summary']['missing_python'])} missing dependencies..."
         | 
| 588 | 
            +
                        )
         | 
| 589 | 
            +
                        success, error = self.install_missing_dependencies(
         | 
| 590 | 
            +
                            results["summary"]["missing_python"]
         | 
| 591 | 
            +
                        )
         | 
| 406 592 | 
             
                        if success:
         | 
| 407 593 | 
             
                            # Re-analyze after installation
         | 
| 408 594 | 
             
                            self.checked_packages.clear()
         | 
| 409 595 | 
             
                            results = self.analyze_dependencies()
         | 
| 410 | 
            -
             | 
| 596 | 
            +
             | 
| 411 597 | 
             
                    return results
         | 
| 412 | 
            -
             | 
| 598 | 
            +
             | 
| 413 599 | 
             
                def format_report(self, results: Dict[str, Dict]) -> str:
         | 
| 414 600 | 
             
                    """
         | 
| 415 601 | 
             
                    Format a human-readable dependency report.
         | 
| 416 | 
            -
             | 
| 602 | 
            +
             | 
| 417 603 | 
             
                    Args:
         | 
| 418 604 | 
             
                        results: Analysis results from analyze_dependencies()
         | 
| 419 | 
            -
             | 
| 605 | 
            +
             | 
| 420 606 | 
             
                    Returns:
         | 
| 421 607 | 
             
                        Formatted report string
         | 
| 422 608 | 
             
                    """
         | 
| 423 609 | 
             
                    import sys
         | 
| 610 | 
            +
             | 
| 424 611 | 
             
                    lines = []
         | 
| 425 612 | 
             
                    lines.append("=" * 80)
         | 
| 426 613 | 
             
                    lines.append("AGENT DEPENDENCY ANALYSIS REPORT")
         | 
| 427 614 | 
             
                    lines.append("=" * 80)
         | 
| 428 615 | 
             
                    lines.append("")
         | 
| 429 | 
            -
             | 
| 616 | 
            +
             | 
| 430 617 | 
             
                    # Python version info
         | 
| 431 | 
            -
                    lines.append( | 
| 618 | 
            +
                    lines.append(
         | 
| 619 | 
            +
                        f"Python Version: {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
         | 
| 620 | 
            +
                    )
         | 
| 432 621 | 
             
                    lines.append("")
         | 
| 433 | 
            -
             | 
| 622 | 
            +
             | 
| 434 623 | 
             
                    # Summary
         | 
| 435 | 
            -
                    summary = results[ | 
| 624 | 
            +
                    summary = results["summary"]
         | 
| 436 625 | 
             
                    lines.append(f"Deployed Agents: {summary['total_agents']}")
         | 
| 437 626 | 
             
                    lines.append(f"Agents with Dependencies: {summary['agents_with_deps']}")
         | 
| 438 627 | 
             
                    lines.append("")
         | 
| 439 | 
            -
             | 
| 628 | 
            +
             | 
| 440 629 | 
             
                    # Missing dependencies summary
         | 
| 441 | 
            -
                    if summary[ | 
| 630 | 
            +
                    if summary["missing_python"] or summary["missing_system"]:
         | 
| 442 631 | 
             
                        lines.append("⚠️  MISSING DEPENDENCIES:")
         | 
| 443 | 
            -
                        if summary[ | 
| 632 | 
            +
                        if summary["missing_python"]:
         | 
| 444 633 | 
             
                            lines.append(f"  Python packages: {len(summary['missing_python'])}")
         | 
| 445 | 
            -
                            for dep in summary[ | 
| 634 | 
            +
                            for dep in summary["missing_python"][:5]:  # Show first 5
         | 
| 446 635 | 
             
                                lines.append(f"    - {dep}")
         | 
| 447 | 
            -
                            if len(summary[ | 
| 448 | 
            -
                                lines.append( | 
| 449 | 
            -
             | 
| 450 | 
            -
             | 
| 636 | 
            +
                            if len(summary["missing_python"]) > 5:
         | 
| 637 | 
            +
                                lines.append(
         | 
| 638 | 
            +
                                    f"    ... and {len(summary['missing_python']) - 5} more"
         | 
| 639 | 
            +
                                )
         | 
| 640 | 
            +
             | 
| 641 | 
            +
                        if summary["missing_system"]:
         | 
| 451 642 | 
             
                            lines.append(f"  System commands: {len(summary['missing_system'])}")
         | 
| 452 | 
            -
                            for cmd in summary[ | 
| 643 | 
            +
                            for cmd in summary["missing_system"]:
         | 
| 453 644 | 
             
                                lines.append(f"    - {cmd}")
         | 
| 454 645 | 
             
                        lines.append("")
         | 
| 455 | 
            -
             | 
| 646 | 
            +
             | 
| 456 647 | 
             
                    # Per-agent details (only for agents with issues)
         | 
| 457 648 | 
             
                    agents_with_issues = {
         | 
| 458 | 
            -
                        agent_id: info | 
| 459 | 
            -
                         | 
| 649 | 
            +
                        agent_id: info
         | 
| 650 | 
            +
                        for agent_id, info in results["agents"].items()
         | 
| 651 | 
            +
                        if info["python"]["missing"]
         | 
| 652 | 
            +
                        or info["python"]["outdated"]
         | 
| 653 | 
            +
                        or info["system"]["missing"]
         | 
| 460 654 | 
             
                    }
         | 
| 461 | 
            -
             | 
| 655 | 
            +
             | 
| 462 656 | 
             
                    if agents_with_issues:
         | 
| 463 657 | 
             
                        lines.append("AGENT-SPECIFIC ISSUES:")
         | 
| 464 658 | 
             
                        lines.append("-" * 40)
         | 
| 465 659 | 
             
                        for agent_id, info in agents_with_issues.items():
         | 
| 466 660 | 
             
                            lines.append(f"\n📦 {agent_id}:")
         | 
| 467 | 
            -
             | 
| 468 | 
            -
                            if info[ | 
| 469 | 
            -
                                lines.append( | 
| 470 | 
            -
             | 
| 471 | 
            -
                                 | 
| 472 | 
            -
                            if info[ | 
| 473 | 
            -
                                lines.append( | 
| 474 | 
            -
             | 
| 661 | 
            +
             | 
| 662 | 
            +
                            if info["python"]["missing"]:
         | 
| 663 | 
            +
                                lines.append(
         | 
| 664 | 
            +
                                    f"  Missing Python: {', '.join(info['python']['missing'])}"
         | 
| 665 | 
            +
                                )
         | 
| 666 | 
            +
                            if info["python"]["outdated"]:
         | 
| 667 | 
            +
                                lines.append(
         | 
| 668 | 
            +
                                    f"  Outdated Python: {', '.join(info['python']['outdated'])}"
         | 
| 669 | 
            +
                                )
         | 
| 670 | 
            +
                            if info["system"]["missing"]:
         | 
| 671 | 
            +
                                lines.append(
         | 
| 672 | 
            +
                                    f"  Missing System: {', '.join(info['system']['missing'])}"
         | 
| 673 | 
            +
                                )
         | 
| 674 | 
            +
             | 
| 475 675 | 
             
                    else:
         | 
| 476 676 | 
             
                        lines.append("✅ All agent dependencies are satisfied!")
         | 
| 477 | 
            -
             | 
| 677 | 
            +
             | 
| 478 678 | 
             
                    # Installation instructions
         | 
| 479 | 
            -
                    if summary[ | 
| 679 | 
            +
                    if summary["missing_python"]:
         | 
| 480 680 | 
             
                        lines.append("")
         | 
| 481 681 | 
             
                        lines.append("TO INSTALL MISSING PYTHON DEPENDENCIES:")
         | 
| 482 682 | 
             
                        lines.append("-" * 40)
         | 
| 483 | 
            -
             | 
| 683 | 
            +
             | 
| 484 684 | 
             
                        # Check for Python 3.13 compatibility issues
         | 
| 485 685 | 
             
                        import sys
         | 
| 686 | 
            +
             | 
| 486 687 | 
             
                        if sys.version_info >= (3, 13):
         | 
| 487 | 
            -
                            compatible, incompatible = self.check_python_compatibility( | 
| 688 | 
            +
                            compatible, incompatible = self.check_python_compatibility(
         | 
| 689 | 
            +
                                summary["missing_python"]
         | 
| 690 | 
            +
                            )
         | 
| 488 691 | 
             
                            if incompatible:
         | 
| 489 692 | 
             
                                lines.append("⚠️  Python 3.13 Compatibility Warning:")
         | 
| 490 | 
            -
                                lines.append( | 
| 693 | 
            +
                                lines.append(
         | 
| 694 | 
            +
                                    f"  {len(incompatible)} packages are not yet compatible with Python 3.13:"
         | 
| 695 | 
            +
                                )
         | 
| 491 696 | 
             
                                for dep in incompatible[:3]:
         | 
| 492 697 | 
             
                                    lines.append(f"    - {dep}")
         | 
| 493 698 | 
             
                                if len(incompatible) > 3:
         | 
| 494 699 | 
             
                                    lines.append(f"    ... and {len(incompatible) - 3} more")
         | 
| 495 700 | 
             
                                lines.append("")
         | 
| 496 | 
            -
                                lines.append( | 
| 701 | 
            +
                                lines.append(
         | 
| 702 | 
            +
                                    "  Consider using Python 3.12 or earlier for full compatibility."
         | 
| 703 | 
            +
                                )
         | 
| 497 704 | 
             
                                lines.append("")
         | 
| 498 | 
            -
             | 
| 705 | 
            +
             | 
| 499 706 | 
             
                        lines.append("Option 1: Install all agent dependencies:")
         | 
| 500 707 | 
             
                        lines.append('  pip install "claude-mpm[agents]"')
         | 
| 501 708 | 
             
                        lines.append("")
         | 
| 502 709 | 
             
                        lines.append("Option 2: Install only what's needed:")
         | 
| 503 | 
            -
                        deps_str =  | 
| 710 | 
            +
                        deps_str = " ".join(f'"{dep}"' for dep in summary["missing_python"][:3])
         | 
| 504 711 | 
             
                        lines.append(f"  pip install {deps_str}")
         | 
| 505 | 
            -
                        if len(summary[ | 
| 712 | 
            +
                        if len(summary["missing_python"]) > 3:
         | 
| 506 713 | 
             
                            lines.append(f"  # ... and {len(summary['missing_python']) - 3} more")
         | 
| 507 | 
            -
             | 
| 508 | 
            -
                    if summary[ | 
| 714 | 
            +
             | 
| 715 | 
            +
                    if summary["missing_system"]:
         | 
| 509 716 | 
             
                        lines.append("")
         | 
| 510 717 | 
             
                        lines.append("TO INSTALL MISSING SYSTEM DEPENDENCIES:")
         | 
| 511 718 | 
             
                        lines.append("-" * 40)
         | 
| 512 719 | 
             
                        lines.append("Use your system package manager:")
         | 
| 513 | 
            -
                        lines.append( | 
| 514 | 
            -
             | 
| 515 | 
            -
                        
         | 
| 720 | 
            +
                        lines.append(
         | 
| 721 | 
            +
                            "  # macOS: brew install " + " ".join(summary["missing_system"])
         | 
| 722 | 
            +
                        )
         | 
| 723 | 
            +
                        lines.append(
         | 
| 724 | 
            +
                            "  # Ubuntu: apt-get install " + " ".join(summary["missing_system"])
         | 
| 725 | 
            +
                        )
         | 
| 726 | 
            +
             | 
| 516 727 | 
             
                    lines.append("")
         | 
| 517 728 | 
             
                    lines.append("=" * 80)
         | 
| 518 | 
            -
             | 
| 519 | 
            -
                    return  | 
| 520 | 
            -
             | 
| 729 | 
            +
             | 
| 730 | 
            +
                    return "\n".join(lines)
         | 
| 731 | 
            +
             | 
| 521 732 | 
             
                def calculate_deployment_hash(self) -> str:
         | 
| 522 733 | 
             
                    """
         | 
| 523 734 | 
             
                    Calculate a hash of the current agent deployment state.
         | 
| 524 | 
            -
             | 
| 735 | 
            +
             | 
| 525 736 | 
             
                    WHY: We use SHA256 hash of agent files to detect when agents have changed.
         | 
| 526 737 | 
             
                    This allows us to skip dependency checks when nothing has changed,
         | 
| 527 738 | 
             
                    improving startup performance.
         | 
| 528 | 
            -
             | 
| 739 | 
            +
             | 
| 529 740 | 
             
                    Returns:
         | 
| 530 741 | 
             
                        SHA256 hash of all deployed agent files and their content.
         | 
| 531 742 | 
             
                    """
         | 
| 532 743 | 
             
                    hash_obj = hashlib.sha256()
         | 
| 533 | 
            -
             | 
| 744 | 
            +
             | 
| 534 745 | 
             
                    # Discover current agents if not already done
         | 
| 535 746 | 
             
                    if not self.deployed_agents:
         | 
| 536 747 | 
             
                        self.discover_deployed_agents()
         | 
| 537 | 
            -
             | 
| 748 | 
            +
             | 
| 538 749 | 
             
                    # Sort agent IDs for consistent hashing
         | 
| 539 750 | 
             
                    for agent_id in sorted(self.deployed_agents.keys()):
         | 
| 540 751 | 
             
                        agent_path = self.deployed_agents[agent_id]
         | 
| 541 | 
            -
             | 
| 752 | 
            +
             | 
| 542 753 | 
             
                        # Include agent ID in hash
         | 
| 543 | 
            -
                        hash_obj.update(agent_id.encode( | 
| 544 | 
            -
             | 
| 754 | 
            +
                        hash_obj.update(agent_id.encode("utf-8"))
         | 
| 755 | 
            +
             | 
| 545 756 | 
             
                        # Include file modification time and size for quick change detection
         | 
| 546 757 | 
             
                        try:
         | 
| 547 758 | 
             
                            stat = agent_path.stat()
         | 
| 548 | 
            -
                            hash_obj.update(str(stat.st_mtime).encode( | 
| 549 | 
            -
                            hash_obj.update(str(stat.st_size).encode( | 
| 550 | 
            -
             | 
| 759 | 
            +
                            hash_obj.update(str(stat.st_mtime).encode("utf-8"))
         | 
| 760 | 
            +
                            hash_obj.update(str(stat.st_size).encode("utf-8"))
         | 
| 761 | 
            +
             | 
| 551 762 | 
             
                            # Include file content for comprehensive change detection
         | 
| 552 | 
            -
                            with open(agent_path,  | 
| 763 | 
            +
                            with open(agent_path, "rb") as f:
         | 
| 553 764 | 
             
                                hash_obj.update(f.read())
         | 
| 554 765 | 
             
                        except Exception as e:
         | 
| 555 766 | 
             
                            logger.debug(f"Could not hash agent file {agent_path}: {e}")
         | 
| 556 767 | 
             
                            # Include error in hash to force recheck on next run
         | 
| 557 | 
            -
                            hash_obj.update(f"error:{agent_id}:{e}".encode( | 
| 558 | 
            -
             | 
| 768 | 
            +
                            hash_obj.update(f"error:{agent_id}:{e}".encode("utf-8"))
         | 
| 769 | 
            +
             | 
| 559 770 | 
             
                    return hash_obj.hexdigest()
         | 
| 560 | 
            -
             | 
| 771 | 
            +
             | 
| 561 772 | 
             
                def load_deployment_state(self) -> Dict:
         | 
| 562 773 | 
             
                    """
         | 
| 563 774 | 
             
                    Load the saved deployment state.
         | 
| 564 | 
            -
             | 
| 775 | 
            +
             | 
| 565 776 | 
             
                    Returns:
         | 
| 566 777 | 
             
                        Dictionary with deployment state or empty dict if not found.
         | 
| 567 778 | 
             
                    """
         | 
| 568 779 | 
             
                    if not self.deployment_state_file.exists():
         | 
| 569 780 | 
             
                        return {}
         | 
| 570 | 
            -
             | 
| 781 | 
            +
             | 
| 571 782 | 
             
                    try:
         | 
| 572 | 
            -
                        with open(self.deployment_state_file,  | 
| 783 | 
            +
                        with open(self.deployment_state_file, "r") as f:
         | 
| 573 784 | 
             
                            return json.load(f)
         | 
| 574 785 | 
             
                    except Exception as e:
         | 
| 575 786 | 
             
                        logger.debug(f"Could not load deployment state: {e}")
         | 
| 576 787 | 
             
                        return {}
         | 
| 577 | 
            -
             | 
| 788 | 
            +
             | 
| 578 789 | 
             
                def save_deployment_state(self, state: Dict) -> None:
         | 
| 579 790 | 
             
                    """
         | 
| 580 791 | 
             
                    Save the deployment state to disk.
         | 
| 581 | 
            -
             | 
| 792 | 
            +
             | 
| 582 793 | 
             
                    Args:
         | 
| 583 794 | 
             
                        state: Deployment state dictionary to save.
         | 
| 584 795 | 
             
                    """
         | 
| 585 796 | 
             
                    try:
         | 
| 586 797 | 
             
                        # Ensure directory exists
         | 
| 587 798 | 
             
                        self.deployment_state_file.parent.mkdir(parents=True, exist_ok=True)
         | 
| 588 | 
            -
             | 
| 589 | 
            -
                        with open(self.deployment_state_file,  | 
| 799 | 
            +
             | 
| 800 | 
            +
                        with open(self.deployment_state_file, "w") as f:
         | 
| 590 801 | 
             
                            json.dump(state, f, indent=2)
         | 
| 591 802 | 
             
                    except Exception as e:
         | 
| 592 803 | 
             
                        logger.debug(f"Could not save deployment state: {e}")
         | 
| 593 | 
            -
             | 
| 804 | 
            +
             | 
| 594 805 | 
             
                def has_agents_changed(self) -> Tuple[bool, str]:
         | 
| 595 806 | 
             
                    """
         | 
| 596 807 | 
             
                    Check if agents have changed since last dependency check.
         | 
| 597 | 
            -
             | 
| 808 | 
            +
             | 
| 598 809 | 
             
                    WHY: This is the core of our smart checking system. We only want to
         | 
| 599 810 | 
             
                    check dependencies when agents have actually changed, not on every run.
         | 
| 600 | 
            -
             | 
| 811 | 
            +
             | 
| 601 812 | 
             
                    Returns:
         | 
| 602 813 | 
             
                        Tuple of (has_changed, current_hash)
         | 
| 603 814 | 
             
                    """
         | 
| 604 815 | 
             
                    current_hash = self.calculate_deployment_hash()
         | 
| 605 816 | 
             
                    state = self.load_deployment_state()
         | 
| 606 | 
            -
             | 
| 607 | 
            -
                    last_hash = state.get( | 
| 608 | 
            -
                    last_check_time = state.get( | 
| 609 | 
            -
             | 
| 817 | 
            +
             | 
| 818 | 
            +
                    last_hash = state.get("deployment_hash")
         | 
| 819 | 
            +
                    last_check_time = state.get("last_check_time", 0)
         | 
| 820 | 
            +
             | 
| 610 821 | 
             
                    # Check if hash has changed
         | 
| 611 822 | 
             
                    if last_hash != current_hash:
         | 
| 612 823 | 
             
                        logger.info("Agent deployment has changed since last check")
         | 
| 613 824 | 
             
                        return True, current_hash
         | 
| 614 | 
            -
             | 
| 825 | 
            +
             | 
| 615 826 | 
             
                    # Also check if it's been more than 24 hours (optional staleness check)
         | 
| 616 827 | 
             
                    current_time = time.time()
         | 
| 617 828 | 
             
                    if current_time - last_check_time > 86400:  # 24 hours
         | 
| 618 829 | 
             
                        logger.debug("Over 24 hours since last dependency check")
         | 
| 619 830 | 
             
                        return True, current_hash
         | 
| 620 | 
            -
             | 
| 831 | 
            +
             | 
| 621 832 | 
             
                    logger.debug("No agent changes detected, skipping dependency check")
         | 
| 622 833 | 
             
                    return False, current_hash
         | 
| 623 | 
            -
             | 
| 624 | 
            -
                def mark_deployment_checked( | 
| 834 | 
            +
             | 
| 835 | 
            +
                def mark_deployment_checked(
         | 
| 836 | 
            +
                    self, deployment_hash: str, check_results: Dict
         | 
| 837 | 
            +
                ) -> None:
         | 
| 625 838 | 
             
                    """
         | 
| 626 839 | 
             
                    Mark the current deployment as checked.
         | 
| 627 | 
            -
             | 
| 840 | 
            +
             | 
| 628 841 | 
             
                    Args:
         | 
| 629 842 | 
             
                        deployment_hash: Hash of the current deployment
         | 
| 630 843 | 
             
                        check_results: Results of the dependency check
         | 
| 631 844 | 
             
                    """
         | 
| 632 845 | 
             
                    state = {
         | 
| 633 | 
            -
                         | 
| 634 | 
            -
                         | 
| 635 | 
            -
                         | 
| 636 | 
            -
                         | 
| 846 | 
            +
                        "deployment_hash": deployment_hash,
         | 
| 847 | 
            +
                        "last_check_time": time.time(),
         | 
| 848 | 
            +
                        "last_check_results": check_results,
         | 
| 849 | 
            +
                        "agent_count": len(self.deployed_agents),
         | 
| 637 850 | 
             
                    }
         | 
| 638 851 | 
             
                    self.save_deployment_state(state)
         | 
| 639 | 
            -
             | 
| 852 | 
            +
             | 
| 640 853 | 
             
                def get_cached_check_results(self) -> Optional[Dict]:
         | 
| 641 854 | 
             
                    """
         | 
| 642 855 | 
             
                    Get cached dependency check results if still valid.
         | 
| 643 | 
            -
             | 
| 856 | 
            +
             | 
| 644 857 | 
             
                    Returns:
         | 
| 645 858 | 
             
                        Cached results or None if not available/valid.
         | 
| 646 859 | 
             
                    """
         | 
| 647 860 | 
             
                    has_changed, current_hash = self.has_agents_changed()
         | 
| 648 | 
            -
             | 
| 861 | 
            +
             | 
| 649 862 | 
             
                    if not has_changed:
         | 
| 650 863 | 
             
                        state = self.load_deployment_state()
         | 
| 651 | 
            -
                        cached_results = state.get( | 
| 864 | 
            +
                        cached_results = state.get("last_check_results")
         | 
| 652 865 | 
             
                        if cached_results:
         | 
| 653 866 | 
             
                            logger.debug("Using cached dependency check results")
         | 
| 654 867 | 
             
                            return cached_results
         | 
| 655 | 
            -
             | 
| 868 | 
            +
             | 
| 656 869 | 
             
                    return None
         | 
| 657 870 |  | 
| 658 871 |  | 
| 659 | 
            -
            def check_deployed_agent_dependencies( | 
| 872 | 
            +
            def check_deployed_agent_dependencies(
         | 
| 873 | 
            +
                auto_install: bool = False, verbose: bool = False
         | 
| 874 | 
            +
            ) -> None:
         | 
| 660 875 | 
             
                """
         | 
| 661 876 | 
             
                Check dependencies for currently deployed agents.
         | 
| 662 | 
            -
             | 
| 877 | 
            +
             | 
| 663 878 | 
             
                Args:
         | 
| 664 879 | 
             
                    auto_install: If True, automatically install missing Python dependencies
         | 
| 665 880 | 
             
                    verbose: If True, enable verbose logging
         | 
| 666 881 | 
             
                """
         | 
| 667 882 | 
             
                if verbose:
         | 
| 668 883 | 
             
                    logging.getLogger().setLevel(logging.DEBUG)
         | 
| 669 | 
            -
             | 
| 884 | 
            +
             | 
| 670 885 | 
             
                loader = AgentDependencyLoader(auto_install=auto_install)
         | 
| 671 886 | 
             
                results = loader.load_and_check()
         | 
| 672 | 
            -
             | 
| 887 | 
            +
             | 
| 673 888 | 
             
                # Print report
         | 
| 674 889 | 
             
                report = loader.format_report(results)
         | 
| 675 890 | 
             
                print(report)
         | 
| 676 | 
            -
             | 
| 891 | 
            +
             | 
| 677 892 | 
             
                # Return status code based on missing dependencies
         | 
| 678 | 
            -
                if results[ | 
| 893 | 
            +
                if results["summary"]["missing_python"] or results["summary"]["missing_system"]:
         | 
| 679 894 | 
             
                    return 1  # Missing dependencies
         | 
| 680 895 | 
             
                return 0  # All satisfied
         | 
| 681 896 |  | 
| 682 897 |  | 
| 683 898 | 
             
            if __name__ == "__main__":
         | 
| 684 899 | 
             
                import argparse
         | 
| 685 | 
            -
             | 
| 900 | 
            +
             | 
| 686 901 | 
             
                parser = argparse.ArgumentParser(
         | 
| 687 902 | 
             
                    description="Check and manage dependencies for deployed agents"
         | 
| 688 903 | 
             
                )
         | 
| 689 904 | 
             
                parser.add_argument(
         | 
| 690 905 | 
             
                    "--auto-install",
         | 
| 691 906 | 
             
                    action="store_true",
         | 
| 692 | 
            -
                    help="Automatically install missing Python dependencies"
         | 
| 907 | 
            +
                    help="Automatically install missing Python dependencies",
         | 
| 693 908 | 
             
                )
         | 
| 694 | 
            -
                parser.add_argument(
         | 
| 695 | 
            -
             | 
| 696 | 
            -
                    action="store_true",
         | 
| 697 | 
            -
                    help="Enable verbose logging"
         | 
| 698 | 
            -
                )
         | 
| 699 | 
            -
                
         | 
| 909 | 
            +
                parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
         | 
| 910 | 
            +
             | 
| 700 911 | 
             
                args = parser.parse_args()
         | 
| 701 | 
            -
             | 
| 912 | 
            +
             | 
| 702 913 | 
             
                exit_code = check_deployed_agent_dependencies(
         | 
| 703 | 
            -
                    auto_install=args.auto_install,
         | 
| 704 | 
            -
                    verbose=args.verbose
         | 
| 914 | 
            +
                    auto_install=args.auto_install, verbose=args.verbose
         | 
| 705 915 | 
             
                )
         | 
| 706 | 
            -
                sys.exit(exit_code)
         | 
| 916 | 
            +
                sys.exit(exit_code)
         |