claude-mpm 4.1.1__py3-none-any.whl → 4.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/__main__.py +1 -1
- claude_mpm/agents/BASE_PM.md +74 -46
- claude_mpm/agents/INSTRUCTIONS.md +11 -153
- claude_mpm/agents/WORKFLOW.md +61 -321
- claude_mpm/agents/__init__.py +11 -11
- claude_mpm/agents/agent_loader.py +23 -20
- claude_mpm/agents/agent_loader_integration.py +1 -1
- claude_mpm/agents/agents_metadata.py +27 -0
- claude_mpm/agents/async_agent_loader.py +5 -8
- claude_mpm/agents/base_agent_loader.py +36 -25
- claude_mpm/agents/frontmatter_validator.py +6 -6
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +9 -9
- claude_mpm/agents/templates/api_qa.json +47 -2
- claude_mpm/agents/templates/engineer.json +33 -11
- claude_mpm/agents/templates/imagemagick.json +256 -0
- claude_mpm/agents/templates/qa.json +41 -2
- claude_mpm/agents/templates/ticketing.json +5 -5
- claude_mpm/agents/templates/web_qa.json +50 -2
- claude_mpm/cli/__init__.py +51 -46
- claude_mpm/cli/__main__.py +1 -1
- claude_mpm/cli/commands/__init__.py +10 -12
- claude_mpm/cli/commands/agent_manager.py +186 -181
- claude_mpm/cli/commands/agents.py +648 -1098
- claude_mpm/cli/commands/aggregate.py +30 -29
- claude_mpm/cli/commands/cleanup.py +50 -44
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
- claude_mpm/cli/commands/config.py +162 -127
- claude_mpm/cli/commands/doctor.py +52 -62
- claude_mpm/cli/commands/info.py +37 -25
- claude_mpm/cli/commands/mcp.py +3 -7
- claude_mpm/cli/commands/mcp_command_router.py +14 -18
- claude_mpm/cli/commands/mcp_install_commands.py +28 -23
- claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
- claude_mpm/cli/commands/mcp_server_commands.py +23 -17
- claude_mpm/cli/commands/memory.py +339 -967
- claude_mpm/cli/commands/monitor.py +117 -88
- claude_mpm/cli/commands/run.py +233 -542
- claude_mpm/cli/commands/socketio_monitor.py +17 -19
- claude_mpm/cli/commands/tickets.py +92 -92
- claude_mpm/cli/parser.py +1 -5
- claude_mpm/cli/parsers/__init__.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
- claude_mpm/cli/parsers/agents_parser.py +2 -3
- claude_mpm/cli/parsers/base_parser.py +7 -5
- claude_mpm/cli/parsers/mcp_parser.py +4 -2
- claude_mpm/cli/parsers/monitor_parser.py +26 -18
- claude_mpm/cli/shared/__init__.py +10 -10
- claude_mpm/cli/shared/argument_patterns.py +57 -71
- claude_mpm/cli/shared/base_command.py +61 -53
- claude_mpm/cli/shared/error_handling.py +62 -58
- claude_mpm/cli/shared/output_formatters.py +78 -77
- claude_mpm/cli/startup_logging.py +280 -172
- claude_mpm/cli/utils.py +10 -11
- claude_mpm/cli_module/__init__.py +1 -1
- claude_mpm/cli_module/args.py +1 -1
- claude_mpm/cli_module/migration_example.py +5 -5
- claude_mpm/config/__init__.py +9 -9
- claude_mpm/config/agent_config.py +15 -14
- claude_mpm/config/experimental_features.py +4 -4
- claude_mpm/config/paths.py +0 -1
- claude_mpm/config/socketio_config.py +5 -6
- claude_mpm/constants.py +1 -2
- claude_mpm/core/__init__.py +8 -8
- claude_mpm/core/agent_name_normalizer.py +1 -1
- claude_mpm/core/agent_registry.py +22 -29
- claude_mpm/core/agent_session_manager.py +3 -3
- claude_mpm/core/base_service.py +7 -15
- claude_mpm/core/cache.py +4 -6
- claude_mpm/core/claude_runner.py +85 -113
- claude_mpm/core/config.py +43 -28
- claude_mpm/core/config_aliases.py +0 -9
- claude_mpm/core/config_constants.py +52 -30
- claude_mpm/core/constants.py +0 -1
- claude_mpm/core/container.py +18 -27
- claude_mpm/core/exceptions.py +2 -2
- claude_mpm/core/factories.py +10 -12
- claude_mpm/core/framework_loader.py +500 -680
- claude_mpm/core/hook_manager.py +26 -22
- claude_mpm/core/hook_performance_config.py +58 -47
- claude_mpm/core/injectable_service.py +1 -1
- claude_mpm/core/interactive_session.py +61 -152
- claude_mpm/core/interfaces.py +1 -100
- claude_mpm/core/lazy.py +5 -5
- claude_mpm/core/log_manager.py +587 -0
- claude_mpm/core/logger.py +125 -8
- claude_mpm/core/logging_config.py +15 -17
- claude_mpm/core/minimal_framework_loader.py +5 -8
- claude_mpm/core/oneshot_session.py +15 -33
- claude_mpm/core/optimized_agent_loader.py +4 -6
- claude_mpm/core/optimized_startup.py +2 -1
- claude_mpm/core/output_style_manager.py +147 -106
- claude_mpm/core/pm_hook_interceptor.py +0 -1
- claude_mpm/core/service_registry.py +11 -8
- claude_mpm/core/session_manager.py +1 -2
- claude_mpm/core/shared/__init__.py +1 -1
- claude_mpm/core/shared/config_loader.py +101 -97
- claude_mpm/core/shared/path_resolver.py +72 -68
- claude_mpm/core/shared/singleton_manager.py +56 -50
- claude_mpm/core/socketio_pool.py +26 -6
- claude_mpm/core/tool_access_control.py +4 -5
- claude_mpm/core/typing_utils.py +50 -59
- claude_mpm/core/unified_agent_registry.py +14 -19
- claude_mpm/core/unified_config.py +4 -6
- claude_mpm/core/unified_paths.py +197 -109
- claude_mpm/dashboard/open_dashboard.py +2 -4
- claude_mpm/experimental/cli_enhancements.py +51 -36
- claude_mpm/generators/agent_profile_generator.py +2 -4
- claude_mpm/hooks/base_hook.py +1 -2
- claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
- claude_mpm/hooks/claude_hooks/event_handlers.py +99 -154
- claude_mpm/hooks/claude_hooks/hook_handler.py +110 -720
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
- claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
- claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
- claude_mpm/hooks/memory_integration_hook.py +5 -5
- claude_mpm/hooks/tool_call_interceptor.py +1 -1
- claude_mpm/hooks/validation_hooks.py +4 -4
- claude_mpm/init.py +4 -9
- claude_mpm/models/__init__.py +2 -2
- claude_mpm/models/agent_session.py +11 -14
- claude_mpm/scripts/mcp_server.py +20 -11
- claude_mpm/scripts/mcp_wrapper.py +5 -5
- claude_mpm/scripts/mpm_doctor.py +321 -0
- claude_mpm/scripts/socketio_daemon.py +28 -25
- claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
- claude_mpm/scripts/socketio_server_manager.py +116 -95
- claude_mpm/services/__init__.py +49 -49
- claude_mpm/services/agent_capabilities_service.py +12 -18
- claude_mpm/services/agents/__init__.py +22 -22
- claude_mpm/services/agents/agent_builder.py +140 -119
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
- claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
- claude_mpm/services/agents/deployment/agent_deployment.py +129 -511
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
- claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
- claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
- claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
- claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
- claude_mpm/services/agents/deployment/agent_validator.py +0 -1
- claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
- claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
- claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
- claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
- claude_mpm/services/agents/deployment/config/__init__.py +1 -1
- claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
- claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
- claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
- claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
- claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
- claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
- claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
- claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
- claude_mpm/services/agents/deployment/results/__init__.py +1 -1
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
- claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
- claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
- claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
- claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
- claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
- claude_mpm/services/agents/loading/__init__.py +1 -1
- claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
- claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
- claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
- claude_mpm/services/agents/management/__init__.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
- claude_mpm/services/agents/management/agent_management_service.py +5 -9
- claude_mpm/services/agents/memory/__init__.py +4 -4
- claude_mpm/services/agents/memory/agent_memory_manager.py +157 -503
- claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
- claude_mpm/services/agents/memory/content_manager.py +44 -38
- claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
- claude_mpm/services/agents/memory/memory_file_service.py +103 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
- claude_mpm/services/agents/memory/template_generator.py +4 -6
- claude_mpm/services/agents/registry/__init__.py +11 -7
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
- claude_mpm/services/agents/registry/modification_tracker.py +3 -6
- claude_mpm/services/async_session_logger.py +1 -2
- claude_mpm/services/claude_session_logger.py +1 -2
- claude_mpm/services/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +407 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +589 -0
- claude_mpm/services/cli/dashboard_launcher.py +424 -0
- claude_mpm/services/cli/memory_crud_service.py +617 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/session_manager.py +513 -0
- claude_mpm/services/cli/socketio_manager.py +498 -0
- claude_mpm/services/cli/startup_checker.py +370 -0
- claude_mpm/services/command_deployment_service.py +173 -0
- claude_mpm/services/command_handler_service.py +20 -22
- claude_mpm/services/core/__init__.py +25 -25
- claude_mpm/services/core/base.py +0 -5
- claude_mpm/services/core/cache_manager.py +311 -0
- claude_mpm/services/core/interfaces/__init__.py +32 -32
- claude_mpm/services/core/interfaces/agent.py +0 -21
- claude_mpm/services/core/interfaces/communication.py +0 -27
- claude_mpm/services/core/interfaces/infrastructure.py +0 -56
- claude_mpm/services/core/interfaces/service.py +0 -29
- claude_mpm/services/core/memory_manager.py +637 -0
- claude_mpm/services/core/path_resolver.py +498 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- claude_mpm/services/diagnostics/__init__.py +1 -1
- claude_mpm/services/diagnostics/checks/__init__.py +6 -6
- claude_mpm/services/diagnostics/checks/agent_check.py +152 -97
- claude_mpm/services/diagnostics/checks/base_check.py +12 -16
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
- claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
- claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
- claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
- claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
- claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
- claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
- claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
- claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
- claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
- claude_mpm/services/diagnostics/models.py +21 -19
- claude_mpm/services/event_aggregator.py +10 -17
- claude_mpm/services/event_bus/__init__.py +1 -1
- claude_mpm/services/event_bus/config.py +54 -35
- claude_mpm/services/event_bus/event_bus.py +76 -71
- claude_mpm/services/event_bus/relay.py +74 -64
- claude_mpm/services/events/__init__.py +11 -11
- claude_mpm/services/events/consumers/__init__.py +3 -3
- claude_mpm/services/events/consumers/dead_letter.py +71 -63
- claude_mpm/services/events/consumers/logging.py +39 -37
- claude_mpm/services/events/consumers/metrics.py +56 -57
- claude_mpm/services/events/consumers/socketio.py +82 -81
- claude_mpm/services/events/core.py +110 -99
- claude_mpm/services/events/interfaces.py +56 -72
- claude_mpm/services/events/producers/__init__.py +1 -1
- claude_mpm/services/events/producers/hook.py +38 -38
- claude_mpm/services/events/producers/system.py +46 -44
- claude_mpm/services/exceptions.py +81 -80
- claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
- claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
- claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
- claude_mpm/services/hook_service.py +6 -9
- claude_mpm/services/infrastructure/__init__.py +1 -1
- claude_mpm/services/infrastructure/context_preservation.py +8 -12
- claude_mpm/services/infrastructure/monitoring.py +21 -23
- claude_mpm/services/mcp_gateway/__init__.py +37 -37
- claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
- claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
- claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
- claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
- claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
- claude_mpm/services/mcp_gateway/core/base.py +0 -3
- claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
- claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
- claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
- claude_mpm/services/mcp_gateway/main.py +2 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
- claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
- claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
- claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
- claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
- claude_mpm/services/memory/__init__.py +3 -3
- claude_mpm/services/memory/builder.py +3 -6
- claude_mpm/services/memory/cache/__init__.py +1 -1
- claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/memory/indexed_memory.py +5 -7
- claude_mpm/services/memory/optimizer.py +7 -10
- claude_mpm/services/memory/router.py +8 -9
- claude_mpm/services/memory_hook_service.py +48 -34
- claude_mpm/services/monitor_build_service.py +77 -73
- claude_mpm/services/port_manager.py +130 -108
- claude_mpm/services/project/analyzer.py +12 -10
- claude_mpm/services/project/registry.py +11 -11
- claude_mpm/services/recovery_manager.py +10 -19
- claude_mpm/services/response_tracker.py +0 -1
- claude_mpm/services/runner_configuration_service.py +19 -20
- claude_mpm/services/session_management_service.py +7 -11
- claude_mpm/services/shared/__init__.py +1 -1
- claude_mpm/services/shared/async_service_base.py +58 -50
- claude_mpm/services/shared/config_service_base.py +73 -67
- claude_mpm/services/shared/lifecycle_service_base.py +82 -78
- claude_mpm/services/shared/manager_base.py +94 -82
- claude_mpm/services/shared/service_factory.py +96 -98
- claude_mpm/services/socketio/__init__.py +3 -3
- claude_mpm/services/socketio/client_proxy.py +5 -5
- claude_mpm/services/socketio/event_normalizer.py +199 -181
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +5 -4
- claude_mpm/services/socketio/handlers/connection.py +163 -136
- claude_mpm/services/socketio/handlers/file.py +13 -14
- claude_mpm/services/socketio/handlers/git.py +12 -7
- claude_mpm/services/socketio/handlers/hook.py +49 -44
- claude_mpm/services/socketio/handlers/memory.py +0 -1
- claude_mpm/services/socketio/handlers/project.py +0 -1
- claude_mpm/services/socketio/handlers/registry.py +37 -19
- claude_mpm/services/socketio/migration_utils.py +98 -84
- claude_mpm/services/socketio/server/__init__.py +1 -1
- claude_mpm/services/socketio/server/broadcaster.py +81 -87
- claude_mpm/services/socketio/server/core.py +65 -54
- claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
- claude_mpm/services/socketio/server/main.py +64 -38
- claude_mpm/services/socketio_client_manager.py +10 -12
- claude_mpm/services/subprocess_launcher_service.py +4 -7
- claude_mpm/services/system_instructions_service.py +13 -14
- claude_mpm/services/ticket_manager.py +2 -2
- claude_mpm/services/utility_service.py +5 -13
- claude_mpm/services/version_control/__init__.py +16 -16
- claude_mpm/services/version_control/branch_strategy.py +5 -8
- claude_mpm/services/version_control/conflict_resolution.py +9 -23
- claude_mpm/services/version_control/git_operations.py +5 -7
- claude_mpm/services/version_control/semantic_versioning.py +16 -17
- claude_mpm/services/version_control/version_parser.py +13 -18
- claude_mpm/services/version_service.py +10 -11
- claude_mpm/storage/__init__.py +1 -1
- claude_mpm/storage/state_storage.py +22 -28
- claude_mpm/utils/__init__.py +6 -6
- claude_mpm/utils/agent_dependency_loader.py +47 -33
- claude_mpm/utils/config_manager.py +11 -14
- claude_mpm/utils/dependency_cache.py +1 -1
- claude_mpm/utils/dependency_manager.py +13 -17
- claude_mpm/utils/dependency_strategies.py +8 -10
- claude_mpm/utils/environment_context.py +3 -9
- claude_mpm/utils/error_handler.py +3 -13
- claude_mpm/utils/file_utils.py +1 -1
- claude_mpm/utils/path_operations.py +8 -12
- claude_mpm/utils/robust_installer.py +110 -33
- claude_mpm/utils/subprocess_utils.py +5 -6
- claude_mpm/validation/agent_validator.py +3 -6
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
- claude_mpm-4.1.3.dist-info/RECORD +528 -0
- claude_mpm/cli/commands/run_config_checker.py +0 -160
- claude_mpm-4.1.1.dist-info/RECORD +0 -494
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
|
@@ -12,18 +12,17 @@ DESIGN DECISIONS:
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
import json
|
|
15
|
-
import os
|
|
16
|
-
from pathlib import Path
|
|
17
|
-
from typing import Any, Dict, Optional
|
|
18
15
|
|
|
19
|
-
import yaml
|
|
20
|
-
|
|
21
|
-
from ...agents.frontmatter_validator import FrontmatterValidator
|
|
22
16
|
from ...constants import AgentCommands
|
|
23
|
-
from ...
|
|
24
|
-
from ...
|
|
25
|
-
from ...
|
|
26
|
-
from
|
|
17
|
+
from ...services.cli.agent_cleanup_service import AgentCleanupService
|
|
18
|
+
from ...services.cli.agent_dependency_service import AgentDependencyService
|
|
19
|
+
from ...services.cli.agent_listing_service import AgentListingService
|
|
20
|
+
from ...services.cli.agent_output_formatter import AgentOutputFormatter
|
|
21
|
+
from ...services.cli.agent_validation_service import AgentValidationService
|
|
22
|
+
from ..shared import (
|
|
23
|
+
AgentCommand,
|
|
24
|
+
CommandResult,
|
|
25
|
+
)
|
|
27
26
|
from ..utils import get_agent_versions_display
|
|
28
27
|
|
|
29
28
|
|
|
@@ -33,6 +32,11 @@ class AgentsCommand(AgentCommand):
|
|
|
33
32
|
def __init__(self):
|
|
34
33
|
super().__init__("agents")
|
|
35
34
|
self._deployment_service = None
|
|
35
|
+
self._listing_service = None
|
|
36
|
+
self._validation_service = None
|
|
37
|
+
self._dependency_service = None
|
|
38
|
+
self._cleanup_service = None
|
|
39
|
+
self._formatter = AgentOutputFormatter()
|
|
36
40
|
|
|
37
41
|
@property
|
|
38
42
|
def deployment_service(self):
|
|
@@ -40,13 +44,48 @@ class AgentsCommand(AgentCommand):
|
|
|
40
44
|
if self._deployment_service is None:
|
|
41
45
|
try:
|
|
42
46
|
from ...services import AgentDeploymentService
|
|
43
|
-
from ...services.agents.deployment.deployment_wrapper import
|
|
47
|
+
from ...services.agents.deployment.deployment_wrapper import (
|
|
48
|
+
DeploymentServiceWrapper,
|
|
49
|
+
)
|
|
50
|
+
|
|
44
51
|
base_service = AgentDeploymentService()
|
|
45
52
|
self._deployment_service = DeploymentServiceWrapper(base_service)
|
|
46
53
|
except ImportError:
|
|
47
54
|
raise ImportError("Agent deployment service not available")
|
|
48
55
|
return self._deployment_service
|
|
49
56
|
|
|
57
|
+
@property
|
|
58
|
+
def listing_service(self):
|
|
59
|
+
"""Get listing service instance (lazy loaded)."""
|
|
60
|
+
if self._listing_service is None:
|
|
61
|
+
self._listing_service = AgentListingService(
|
|
62
|
+
deployment_service=self.deployment_service
|
|
63
|
+
)
|
|
64
|
+
return self._listing_service
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def validation_service(self):
|
|
68
|
+
"""Get validation service instance (lazy loaded)."""
|
|
69
|
+
if self._validation_service is None:
|
|
70
|
+
self._validation_service = AgentValidationService()
|
|
71
|
+
return self._validation_service
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def dependency_service(self):
|
|
75
|
+
"""Get dependency service instance (lazy loaded)."""
|
|
76
|
+
if self._dependency_service is None:
|
|
77
|
+
self._dependency_service = AgentDependencyService()
|
|
78
|
+
return self._dependency_service
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def cleanup_service(self):
|
|
82
|
+
"""Get cleanup service instance (lazy loaded)."""
|
|
83
|
+
if self._cleanup_service is None:
|
|
84
|
+
self._cleanup_service = AgentCleanupService(
|
|
85
|
+
deployment_service=self.deployment_service
|
|
86
|
+
)
|
|
87
|
+
return self._cleanup_service
|
|
88
|
+
|
|
50
89
|
def validate_args(self, args) -> str:
|
|
51
90
|
"""Validate command arguments."""
|
|
52
91
|
# Most agent commands are optional, so basic validation
|
|
@@ -56,14 +95,18 @@ class AgentsCommand(AgentCommand):
|
|
|
56
95
|
"""Execute the agent command."""
|
|
57
96
|
try:
|
|
58
97
|
# Handle default case (no subcommand)
|
|
59
|
-
if not hasattr(args,
|
|
98
|
+
if not hasattr(args, "agents_command") or not args.agents_command:
|
|
60
99
|
return self._show_agent_versions(args)
|
|
61
100
|
|
|
62
101
|
# Route to appropriate subcommand
|
|
63
102
|
command_map = {
|
|
64
103
|
AgentCommands.LIST.value: self._list_agents,
|
|
65
|
-
AgentCommands.DEPLOY.value: lambda a: self._deploy_agents(
|
|
66
|
-
|
|
104
|
+
AgentCommands.DEPLOY.value: lambda a: self._deploy_agents(
|
|
105
|
+
a, force=False
|
|
106
|
+
),
|
|
107
|
+
AgentCommands.FORCE_DEPLOY.value: lambda a: self._deploy_agents(
|
|
108
|
+
a, force=True
|
|
109
|
+
),
|
|
67
110
|
AgentCommands.CLEAN.value: self._clean_agents,
|
|
68
111
|
AgentCommands.VIEW.value: self._view_agent,
|
|
69
112
|
AgentCommands.FIX.value: self._fix_agents,
|
|
@@ -76,10 +119,11 @@ class AgentsCommand(AgentCommand):
|
|
|
76
119
|
|
|
77
120
|
if args.agents_command in command_map:
|
|
78
121
|
return command_map[args.agents_command](args)
|
|
79
|
-
|
|
80
|
-
|
|
122
|
+
return CommandResult.error_result(
|
|
123
|
+
f"Unknown agent command: {args.agents_command}"
|
|
124
|
+
)
|
|
81
125
|
|
|
82
|
-
except ImportError
|
|
126
|
+
except ImportError:
|
|
83
127
|
self.logger.error("Agent deployment service not available")
|
|
84
128
|
return CommandResult.error_result("Agent deployment service not available")
|
|
85
129
|
except Exception as e:
|
|
@@ -91,53 +135,70 @@ class AgentsCommand(AgentCommand):
|
|
|
91
135
|
try:
|
|
92
136
|
agent_versions = get_agent_versions_display()
|
|
93
137
|
|
|
94
|
-
output_format = getattr(args,
|
|
95
|
-
if output_format in [
|
|
138
|
+
output_format = getattr(args, "format", "text")
|
|
139
|
+
if output_format in ["json", "yaml"]:
|
|
96
140
|
# Parse the agent versions display into structured data
|
|
97
141
|
if agent_versions:
|
|
98
142
|
data = {"agent_versions": agent_versions, "has_agents": True}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
143
|
+
formatted = (
|
|
144
|
+
self._formatter.format_as_json(data)
|
|
145
|
+
if output_format == "json"
|
|
146
|
+
else self._formatter.format_as_yaml(data)
|
|
147
|
+
)
|
|
148
|
+
print(formatted)
|
|
149
|
+
return CommandResult.success_result(
|
|
150
|
+
"Agent versions retrieved", data=data
|
|
151
|
+
)
|
|
152
|
+
data = {
|
|
153
|
+
"agent_versions": None,
|
|
154
|
+
"has_agents": False,
|
|
155
|
+
"suggestion": "To deploy agents, run: claude-mpm --mpm:agents deploy",
|
|
156
|
+
}
|
|
157
|
+
formatted = (
|
|
158
|
+
self._formatter.format_as_json(data)
|
|
159
|
+
if output_format == "json"
|
|
160
|
+
else self._formatter.format_as_yaml(data)
|
|
161
|
+
)
|
|
162
|
+
print(formatted)
|
|
163
|
+
return CommandResult.success_result(
|
|
164
|
+
"No deployed agents found", data=data
|
|
165
|
+
)
|
|
166
|
+
# Text output
|
|
167
|
+
if agent_versions:
|
|
168
|
+
print(agent_versions)
|
|
169
|
+
return CommandResult.success_result("Agent versions displayed")
|
|
170
|
+
print("No deployed agents found")
|
|
171
|
+
print("\nTo deploy agents, run: claude-mpm --mpm:agents deploy")
|
|
172
|
+
return CommandResult.success_result("No deployed agents found")
|
|
112
173
|
|
|
113
174
|
except Exception as e:
|
|
114
175
|
self.logger.error(f"Error getting agent versions: {e}", exc_info=True)
|
|
115
176
|
return CommandResult.error_result(f"Error getting agent versions: {e}")
|
|
116
177
|
|
|
117
|
-
|
|
118
178
|
def _list_agents(self, args) -> CommandResult:
|
|
119
179
|
"""List available or deployed agents."""
|
|
120
180
|
try:
|
|
121
|
-
output_format = getattr(args,
|
|
181
|
+
output_format = getattr(args, "format", "text")
|
|
122
182
|
|
|
123
183
|
if hasattr(args, "by_tier") and args.by_tier:
|
|
124
184
|
return self._list_agents_by_tier(args)
|
|
125
|
-
|
|
185
|
+
if getattr(args, "system", False):
|
|
126
186
|
return self._list_system_agents(args)
|
|
127
|
-
|
|
187
|
+
if getattr(args, "deployed", False):
|
|
128
188
|
return self._list_deployed_agents(args)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
usage_msg = "Use --system to list system agents, --deployed to list deployed agents, or --by-tier to group by precedence"
|
|
189
|
+
# Default: show usage
|
|
190
|
+
usage_msg = "Use --system to list system agents, --deployed to list deployed agents, or --by-tier to group by precedence"
|
|
132
191
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
192
|
+
if output_format in ["json", "yaml"]:
|
|
193
|
+
return CommandResult.error_result(
|
|
194
|
+
"No list option specified",
|
|
195
|
+
data={
|
|
196
|
+
"usage": usage_msg,
|
|
197
|
+
"available_options": ["--system", "--deployed", "--by-tier"],
|
|
198
|
+
},
|
|
199
|
+
)
|
|
200
|
+
print(usage_msg)
|
|
201
|
+
return CommandResult.error_result("No list option specified")
|
|
141
202
|
|
|
142
203
|
except Exception as e:
|
|
143
204
|
self.logger.error(f"Error listing agents: {e}", exc_info=True)
|
|
@@ -146,32 +207,34 @@ class AgentsCommand(AgentCommand):
|
|
|
146
207
|
def _list_system_agents(self, args) -> CommandResult:
|
|
147
208
|
"""List available agent templates."""
|
|
148
209
|
try:
|
|
149
|
-
|
|
150
|
-
|
|
210
|
+
verbose = getattr(args, "verbose", False)
|
|
211
|
+
agents = self.listing_service.list_system_agents(verbose=verbose)
|
|
212
|
+
|
|
213
|
+
output_format = getattr(args, "format", "text")
|
|
214
|
+
quiet = getattr(args, "quiet", False)
|
|
215
|
+
|
|
216
|
+
# Convert AgentInfo objects to dicts for formatter
|
|
217
|
+
agents_data = [
|
|
218
|
+
{
|
|
219
|
+
"name": agent.name,
|
|
220
|
+
"type": agent.type,
|
|
221
|
+
"path": agent.path,
|
|
222
|
+
"description": agent.description,
|
|
223
|
+
"specializations": agent.specializations,
|
|
224
|
+
"version": agent.version,
|
|
225
|
+
}
|
|
226
|
+
for agent in agents
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
formatted = self._formatter.format_agent_list(
|
|
230
|
+
agents_data, output_format=output_format, verbose=verbose, quiet=quiet
|
|
231
|
+
)
|
|
232
|
+
print(formatted)
|
|
151
233
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
)
|
|
157
|
-
else:
|
|
158
|
-
# Text output
|
|
159
|
-
print("Available Agent Templates:")
|
|
160
|
-
print("-" * 80)
|
|
161
|
-
if not agents:
|
|
162
|
-
print("No agent templates found")
|
|
163
|
-
else:
|
|
164
|
-
for agent in agents:
|
|
165
|
-
print(f"📄 {agent['file']}")
|
|
166
|
-
if "name" in agent:
|
|
167
|
-
print(f" Name: {agent['name']}")
|
|
168
|
-
if "description" in agent:
|
|
169
|
-
print(f" Description: {agent['description']}")
|
|
170
|
-
if "version" in agent:
|
|
171
|
-
print(f" Version: {agent['version']}")
|
|
172
|
-
print()
|
|
173
|
-
|
|
174
|
-
return CommandResult.success_result(f"Listed {len(agents)} agent templates")
|
|
234
|
+
return CommandResult.success_result(
|
|
235
|
+
f"Listed {len(agents)} agent templates",
|
|
236
|
+
data={"agents": agents_data, "count": len(agents)},
|
|
237
|
+
)
|
|
175
238
|
|
|
176
239
|
except Exception as e:
|
|
177
240
|
self.logger.error(f"Error listing system agents: {e}", exc_info=True)
|
|
@@ -180,39 +243,48 @@ class AgentsCommand(AgentCommand):
|
|
|
180
243
|
def _list_deployed_agents(self, args) -> CommandResult:
|
|
181
244
|
"""List deployed agents."""
|
|
182
245
|
try:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return CommandResult.success_result(
|
|
188
|
-
f"Found {len(verification['agents_found'])} deployed agents",
|
|
189
|
-
data={
|
|
190
|
-
"agents": verification["agents_found"],
|
|
191
|
-
"warnings": verification.get("warnings", []),
|
|
192
|
-
"count": len(verification["agents_found"])
|
|
193
|
-
}
|
|
194
|
-
)
|
|
195
|
-
else:
|
|
196
|
-
# Text output
|
|
197
|
-
print("Deployed Agents:")
|
|
198
|
-
print("-" * 80)
|
|
199
|
-
if not verification["agents_found"]:
|
|
200
|
-
print("No deployed agents found")
|
|
201
|
-
else:
|
|
202
|
-
for agent in verification["agents_found"]:
|
|
203
|
-
print(f"📄 {agent['file']}")
|
|
204
|
-
if "name" in agent:
|
|
205
|
-
print(f" Name: {agent['name']}")
|
|
206
|
-
if "path" in agent:
|
|
207
|
-
print(f" Path: {agent['path']}")
|
|
208
|
-
print()
|
|
209
|
-
|
|
210
|
-
if verification["warnings"]:
|
|
211
|
-
print("\nWarnings:")
|
|
212
|
-
for warning in verification["warnings"]:
|
|
213
|
-
print(f" ⚠️ {warning}")
|
|
246
|
+
verbose = getattr(args, "verbose", False)
|
|
247
|
+
agents, warnings = self.listing_service.list_deployed_agents(
|
|
248
|
+
verbose=verbose
|
|
249
|
+
)
|
|
214
250
|
|
|
215
|
-
|
|
251
|
+
output_format = getattr(args, "format", "text")
|
|
252
|
+
quiet = getattr(args, "quiet", False)
|
|
253
|
+
|
|
254
|
+
# Convert AgentInfo objects to dicts for formatter
|
|
255
|
+
agents_data = [
|
|
256
|
+
{
|
|
257
|
+
"name": agent.name,
|
|
258
|
+
"type": agent.type,
|
|
259
|
+
"tier": agent.tier,
|
|
260
|
+
"path": agent.path,
|
|
261
|
+
"description": agent.description,
|
|
262
|
+
"specializations": agent.specializations,
|
|
263
|
+
"version": agent.version,
|
|
264
|
+
}
|
|
265
|
+
for agent in agents
|
|
266
|
+
]
|
|
267
|
+
|
|
268
|
+
# Format the agent list
|
|
269
|
+
formatted = self._formatter.format_agent_list(
|
|
270
|
+
agents_data, output_format=output_format, verbose=verbose, quiet=quiet
|
|
271
|
+
)
|
|
272
|
+
print(formatted)
|
|
273
|
+
|
|
274
|
+
# Add warnings for text output
|
|
275
|
+
if output_format == "text" and warnings:
|
|
276
|
+
print("\nWarnings:")
|
|
277
|
+
for warning in warnings:
|
|
278
|
+
print(f" ⚠️ {warning}")
|
|
279
|
+
|
|
280
|
+
return CommandResult.success_result(
|
|
281
|
+
f"Listed {len(agents)} deployed agents",
|
|
282
|
+
data={
|
|
283
|
+
"agents": agents_data,
|
|
284
|
+
"warnings": warnings,
|
|
285
|
+
"count": len(agents),
|
|
286
|
+
},
|
|
287
|
+
)
|
|
216
288
|
|
|
217
289
|
except Exception as e:
|
|
218
290
|
self.logger.error(f"Error listing deployed agents: {e}", exc_info=True)
|
|
@@ -221,29 +293,58 @@ class AgentsCommand(AgentCommand):
|
|
|
221
293
|
def _list_agents_by_tier(self, args) -> CommandResult:
|
|
222
294
|
"""List agents grouped by tier/precedence."""
|
|
223
295
|
try:
|
|
224
|
-
|
|
225
|
-
output_format = getattr(args,
|
|
296
|
+
tier_info = self.listing_service.list_agents_by_tier()
|
|
297
|
+
output_format = getattr(args, "format", "text")
|
|
298
|
+
|
|
299
|
+
# Convert to format expected by formatter
|
|
300
|
+
agents_by_tier = {
|
|
301
|
+
"project": [
|
|
302
|
+
{
|
|
303
|
+
"name": agent.name,
|
|
304
|
+
"type": agent.type,
|
|
305
|
+
"path": agent.path,
|
|
306
|
+
"active": agent.active,
|
|
307
|
+
"overridden_by": agent.overridden_by,
|
|
308
|
+
}
|
|
309
|
+
for agent in tier_info.project
|
|
310
|
+
],
|
|
311
|
+
"user": [
|
|
312
|
+
{
|
|
313
|
+
"name": agent.name,
|
|
314
|
+
"type": agent.type,
|
|
315
|
+
"path": agent.path,
|
|
316
|
+
"active": agent.active,
|
|
317
|
+
"overridden_by": agent.overridden_by,
|
|
318
|
+
}
|
|
319
|
+
for agent in tier_info.user
|
|
320
|
+
],
|
|
321
|
+
"system": [
|
|
322
|
+
{
|
|
323
|
+
"name": agent.name,
|
|
324
|
+
"type": agent.type,
|
|
325
|
+
"path": agent.path,
|
|
326
|
+
"active": agent.active,
|
|
327
|
+
"overridden_by": agent.overridden_by,
|
|
328
|
+
}
|
|
329
|
+
for agent in tier_info.system
|
|
330
|
+
],
|
|
331
|
+
"summary": {
|
|
332
|
+
"total_count": tier_info.total_count,
|
|
333
|
+
"active_count": tier_info.active_count,
|
|
334
|
+
"project_count": len(tier_info.project),
|
|
335
|
+
"user_count": len(tier_info.user),
|
|
336
|
+
"system_count": len(tier_info.system),
|
|
337
|
+
},
|
|
338
|
+
}
|
|
226
339
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)
|
|
232
|
-
else:
|
|
233
|
-
# Text output
|
|
234
|
-
print("Agents by Tier/Precedence:")
|
|
235
|
-
print("=" * 50)
|
|
236
|
-
|
|
237
|
-
for tier, agents in agents_by_tier.items():
|
|
238
|
-
print(f"\n{tier.upper()}:")
|
|
239
|
-
print("-" * 20)
|
|
240
|
-
if agents:
|
|
241
|
-
for agent in agents:
|
|
242
|
-
print(f" • {agent}")
|
|
243
|
-
else:
|
|
244
|
-
print(" (none)")
|
|
340
|
+
formatted = self._formatter.format_agents_by_tier(
|
|
341
|
+
agents_by_tier, output_format=output_format
|
|
342
|
+
)
|
|
343
|
+
print(formatted)
|
|
245
344
|
|
|
246
|
-
|
|
345
|
+
return CommandResult.success_result(
|
|
346
|
+
"Agents listed by tier", data=agents_by_tier
|
|
347
|
+
)
|
|
247
348
|
|
|
248
349
|
except Exception as e:
|
|
249
350
|
self.logger.error(f"Error listing agents by tier: {e}", exc_info=True)
|
|
@@ -259,29 +360,39 @@ class AgentsCommand(AgentCommand):
|
|
|
259
360
|
project_result = self.deployment_service.deploy_project_agents(force=force)
|
|
260
361
|
|
|
261
362
|
# Combine results
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if project_result.get('deployed_count', 0) > 0:
|
|
279
|
-
print(f"✓ Deployed {project_result['deployed_count']} project agents")
|
|
363
|
+
combined_result = {
|
|
364
|
+
"deployed_count": system_result.get("deployed_count", 0)
|
|
365
|
+
+ project_result.get("deployed_count", 0),
|
|
366
|
+
"deployed": system_result.get("deployed", [])
|
|
367
|
+
+ project_result.get("deployed", []),
|
|
368
|
+
"updated_count": system_result.get("updated_count", 0)
|
|
369
|
+
+ project_result.get("updated_count", 0),
|
|
370
|
+
"updated": system_result.get("updated", [])
|
|
371
|
+
+ project_result.get("updated", []),
|
|
372
|
+
"skipped": system_result.get("skipped", [])
|
|
373
|
+
+ project_result.get("skipped", []),
|
|
374
|
+
"errors": system_result.get("errors", [])
|
|
375
|
+
+ project_result.get("errors", []),
|
|
376
|
+
"target_dir": system_result.get("target_dir")
|
|
377
|
+
or project_result.get("target_dir"),
|
|
378
|
+
}
|
|
280
379
|
|
|
281
|
-
|
|
282
|
-
|
|
380
|
+
output_format = getattr(args, "format", "text")
|
|
381
|
+
verbose = getattr(args, "verbose", False)
|
|
283
382
|
|
|
284
|
-
|
|
383
|
+
formatted = self._formatter.format_deployment_result(
|
|
384
|
+
combined_result, output_format=output_format, verbose=verbose
|
|
385
|
+
)
|
|
386
|
+
print(formatted)
|
|
387
|
+
|
|
388
|
+
return CommandResult.success_result(
|
|
389
|
+
f"Deployed {combined_result['deployed_count']} agents",
|
|
390
|
+
data={
|
|
391
|
+
"system_agents": system_result,
|
|
392
|
+
"project_agents": project_result,
|
|
393
|
+
"total_deployed": combined_result["deployed_count"],
|
|
394
|
+
},
|
|
395
|
+
)
|
|
285
396
|
|
|
286
397
|
except Exception as e:
|
|
287
398
|
self.logger.error(f"Error deploying agents: {e}", exc_info=True)
|
|
@@ -290,23 +401,20 @@ class AgentsCommand(AgentCommand):
|
|
|
290
401
|
def _clean_agents(self, args) -> CommandResult:
|
|
291
402
|
"""Clean deployed agents."""
|
|
292
403
|
try:
|
|
293
|
-
result = self.
|
|
404
|
+
result = self.cleanup_service.clean_deployed_agents()
|
|
294
405
|
|
|
295
|
-
output_format = getattr(args,
|
|
296
|
-
|
|
297
|
-
return CommandResult.success_result(
|
|
298
|
-
f"Cleaned {result.get('cleaned_count', 0)} agents",
|
|
299
|
-
data=result
|
|
300
|
-
)
|
|
301
|
-
else:
|
|
302
|
-
# Text output
|
|
303
|
-
cleaned_count = result.get('cleaned_count', 0)
|
|
304
|
-
if cleaned_count > 0:
|
|
305
|
-
print(f"✓ Cleaned {cleaned_count} deployed agents")
|
|
306
|
-
else:
|
|
307
|
-
print("No deployed agents to clean")
|
|
406
|
+
output_format = getattr(args, "format", "text")
|
|
407
|
+
dry_run = False # Regular clean is not a dry run
|
|
308
408
|
|
|
309
|
-
|
|
409
|
+
formatted = self._formatter.format_cleanup_result(
|
|
410
|
+
result, output_format=output_format, dry_run=dry_run
|
|
411
|
+
)
|
|
412
|
+
print(formatted)
|
|
413
|
+
|
|
414
|
+
cleaned_count = result.get("cleaned_count", 0)
|
|
415
|
+
return CommandResult.success_result(
|
|
416
|
+
f"Cleaned {cleaned_count} agents", data=result
|
|
417
|
+
)
|
|
310
418
|
|
|
311
419
|
except Exception as e:
|
|
312
420
|
self.logger.error(f"Error cleaning agents: {e}", exc_info=True)
|
|
@@ -315,51 +423,184 @@ class AgentsCommand(AgentCommand):
|
|
|
315
423
|
def _view_agent(self, args) -> CommandResult:
|
|
316
424
|
"""View details of a specific agent."""
|
|
317
425
|
try:
|
|
318
|
-
agent_name = getattr(args,
|
|
426
|
+
agent_name = getattr(args, "agent_name", None)
|
|
319
427
|
if not agent_name:
|
|
320
|
-
return CommandResult.error_result(
|
|
428
|
+
return CommandResult.error_result(
|
|
429
|
+
"Agent name is required for view command"
|
|
430
|
+
)
|
|
321
431
|
|
|
322
|
-
# Get agent details from
|
|
323
|
-
agent_details = self.
|
|
432
|
+
# Get agent details from listing service
|
|
433
|
+
agent_details = self.listing_service.get_agent_details(agent_name)
|
|
324
434
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
435
|
+
if not agent_details:
|
|
436
|
+
# Try to find the agent to provide helpful error message
|
|
437
|
+
agent = self.listing_service.find_agent(agent_name)
|
|
438
|
+
if not agent:
|
|
439
|
+
return CommandResult.error_result(f"Agent '{agent_name}' not found")
|
|
440
|
+
return CommandResult.error_result(
|
|
441
|
+
f"Could not retrieve details for agent '{agent_name}'"
|
|
330
442
|
)
|
|
331
|
-
else:
|
|
332
|
-
# Text output
|
|
333
|
-
print(f"Agent: {agent_name}")
|
|
334
|
-
print("-" * 40)
|
|
335
|
-
for key, value in agent_details.items():
|
|
336
|
-
print(f"{key}: {value}")
|
|
337
443
|
|
|
338
|
-
|
|
444
|
+
output_format = getattr(args, "format", "text")
|
|
445
|
+
verbose = getattr(args, "verbose", False)
|
|
446
|
+
|
|
447
|
+
formatted = self._formatter.format_agent_details(
|
|
448
|
+
agent_details, output_format=output_format, verbose=verbose
|
|
449
|
+
)
|
|
450
|
+
print(formatted)
|
|
451
|
+
|
|
452
|
+
return CommandResult.success_result(
|
|
453
|
+
f"Displayed details for {agent_name}", data=agent_details
|
|
454
|
+
)
|
|
339
455
|
|
|
340
456
|
except Exception as e:
|
|
341
457
|
self.logger.error(f"Error viewing agent: {e}", exc_info=True)
|
|
342
458
|
return CommandResult.error_result(f"Error viewing agent: {e}")
|
|
343
459
|
|
|
344
460
|
def _fix_agents(self, args) -> CommandResult:
|
|
345
|
-
"""Fix agent
|
|
461
|
+
"""Fix agent frontmatter issues using validation service."""
|
|
346
462
|
try:
|
|
347
|
-
|
|
463
|
+
dry_run = getattr(args, "dry_run", False)
|
|
464
|
+
agent_name = getattr(args, "agent_name", None)
|
|
465
|
+
fix_all = getattr(args, "all", False)
|
|
466
|
+
|
|
467
|
+
output_format = getattr(args, "format", "text")
|
|
468
|
+
|
|
469
|
+
# Determine what to fix
|
|
470
|
+
if fix_all:
|
|
471
|
+
# Fix all agents
|
|
472
|
+
result = self.validation_service.fix_all_agents(dry_run=dry_run)
|
|
473
|
+
|
|
474
|
+
if output_format in ["json", "yaml"]:
|
|
475
|
+
formatted = (
|
|
476
|
+
self._formatter.format_as_json(result)
|
|
477
|
+
if output_format == "json"
|
|
478
|
+
else self._formatter.format_as_yaml(result)
|
|
479
|
+
)
|
|
480
|
+
print(formatted)
|
|
481
|
+
else:
|
|
482
|
+
# Text output
|
|
483
|
+
mode = "DRY RUN" if dry_run else "FIX"
|
|
484
|
+
print(
|
|
485
|
+
f"\n🔧 {mode}: Checking {result.get('total_agents', 0)} agent(s) for frontmatter issues...\n"
|
|
486
|
+
)
|
|
348
487
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
488
|
+
if result.get("results"):
|
|
489
|
+
for agent_result in result["results"]:
|
|
490
|
+
print(f"📄 {agent_result['agent']}:")
|
|
491
|
+
if agent_result.get("skipped"):
|
|
492
|
+
print(
|
|
493
|
+
f" ⚠️ Skipped: {agent_result.get('reason', 'Unknown reason')}"
|
|
494
|
+
)
|
|
495
|
+
elif agent_result.get("was_valid"):
|
|
496
|
+
print(" ✓ No issues found")
|
|
497
|
+
else:
|
|
498
|
+
if agent_result.get("errors_found", 0) > 0:
|
|
499
|
+
print(
|
|
500
|
+
f" ❌ Errors found: {agent_result['errors_found']}"
|
|
501
|
+
)
|
|
502
|
+
if agent_result.get("warnings_found", 0) > 0:
|
|
503
|
+
print(
|
|
504
|
+
f" ⚠️ Warnings found: {agent_result['warnings_found']}"
|
|
505
|
+
)
|
|
506
|
+
if dry_run:
|
|
507
|
+
if agent_result.get("corrections_available", 0) > 0:
|
|
508
|
+
print(
|
|
509
|
+
f" 🔧 Would fix: {agent_result['corrections_available']} issues"
|
|
510
|
+
)
|
|
511
|
+
elif agent_result.get("corrections_made", 0) > 0:
|
|
512
|
+
print(
|
|
513
|
+
f" ✓ Fixed: {agent_result['corrections_made']} issues"
|
|
514
|
+
)
|
|
515
|
+
print()
|
|
516
|
+
|
|
517
|
+
# Summary
|
|
518
|
+
print("=" * 80)
|
|
519
|
+
print("SUMMARY:")
|
|
520
|
+
print(f" Agents checked: {result.get('agents_checked', 0)}")
|
|
521
|
+
print(
|
|
522
|
+
f" Total issues found: {result.get('total_issues_found', 0)}"
|
|
523
|
+
)
|
|
524
|
+
if dry_run:
|
|
525
|
+
print(
|
|
526
|
+
f" Issues that would be fixed: {result.get('total_corrections_available', 0)}"
|
|
527
|
+
)
|
|
528
|
+
print("\n💡 Run without --dry-run to apply fixes")
|
|
529
|
+
else:
|
|
530
|
+
print(
|
|
531
|
+
f" Issues fixed: {result.get('total_corrections_made', 0)}"
|
|
532
|
+
)
|
|
533
|
+
if result.get("total_corrections_made", 0) > 0:
|
|
534
|
+
print("\n✓ Frontmatter issues have been fixed!")
|
|
535
|
+
print("=" * 80 + "\n")
|
|
536
|
+
|
|
537
|
+
msg = f"{'Would fix' if dry_run else 'Fixed'} {result.get('total_corrections_available' if dry_run else 'total_corrections_made', 0)} issues"
|
|
538
|
+
return CommandResult.success_result(msg, data=result)
|
|
539
|
+
|
|
540
|
+
if agent_name:
|
|
541
|
+
# Fix specific agent
|
|
542
|
+
result = self.validation_service.fix_agent_frontmatter(
|
|
543
|
+
agent_name, dry_run=dry_run
|
|
354
544
|
)
|
|
355
|
-
else:
|
|
356
|
-
# Text output
|
|
357
|
-
print("✓ Agent deployment issues fixed")
|
|
358
|
-
if result.get('fixes_applied'):
|
|
359
|
-
for fix in result['fixes_applied']:
|
|
360
|
-
print(f" - {fix}")
|
|
361
545
|
|
|
362
|
-
|
|
546
|
+
if not result.get("success"):
|
|
547
|
+
return CommandResult.error_result(
|
|
548
|
+
result.get("error", "Failed to fix agent")
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
if output_format in ["json", "yaml"]:
|
|
552
|
+
formatted = (
|
|
553
|
+
self._formatter.format_as_json(result)
|
|
554
|
+
if output_format == "json"
|
|
555
|
+
else self._formatter.format_as_yaml(result)
|
|
556
|
+
)
|
|
557
|
+
print(formatted)
|
|
558
|
+
else:
|
|
559
|
+
# Text output
|
|
560
|
+
mode = "DRY RUN" if dry_run else "FIX"
|
|
561
|
+
print(
|
|
562
|
+
f"\n🔧 {mode}: Checking agent '{agent_name}' for frontmatter issues...\n"
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
print(f"📄 {agent_name}:")
|
|
566
|
+
if result.get("was_valid"):
|
|
567
|
+
print(" ✓ No issues found")
|
|
568
|
+
else:
|
|
569
|
+
if result.get("errors_found"):
|
|
570
|
+
print(" ❌ Errors:")
|
|
571
|
+
for error in result["errors_found"]:
|
|
572
|
+
print(f" - {error}")
|
|
573
|
+
if result.get("warnings_found"):
|
|
574
|
+
print(" ⚠️ Warnings:")
|
|
575
|
+
for warning in result["warnings_found"]:
|
|
576
|
+
print(f" - {warning}")
|
|
577
|
+
if dry_run:
|
|
578
|
+
if result.get("corrections_available"):
|
|
579
|
+
print(" 🔧 Would fix:")
|
|
580
|
+
for correction in result["corrections_available"]:
|
|
581
|
+
print(f" - {correction}")
|
|
582
|
+
elif result.get("corrections_made"):
|
|
583
|
+
print(" ✓ Fixed:")
|
|
584
|
+
for correction in result["corrections_made"]:
|
|
585
|
+
print(f" - {correction}")
|
|
586
|
+
print()
|
|
587
|
+
|
|
588
|
+
if dry_run and result.get("corrections_available"):
|
|
589
|
+
print("💡 Run without --dry-run to apply fixes\n")
|
|
590
|
+
elif not dry_run and result.get("corrections_made"):
|
|
591
|
+
print("✓ Frontmatter issues have been fixed!\n")
|
|
592
|
+
|
|
593
|
+
msg = f"{'Would fix' if dry_run else 'Fixed'} agent '{agent_name}'"
|
|
594
|
+
return CommandResult.success_result(msg, data=result)
|
|
595
|
+
|
|
596
|
+
# No agent specified and not --all
|
|
597
|
+
usage_msg = "Please specify an agent name or use --all to fix all agents\nUsage: claude-mpm agents fix [agent_name] [--dry-run] [--all]"
|
|
598
|
+
if output_format in ["json", "yaml"]:
|
|
599
|
+
return CommandResult.error_result(
|
|
600
|
+
"No agent specified", data={"usage": usage_msg}
|
|
601
|
+
)
|
|
602
|
+
print(f"❌ {usage_msg}")
|
|
603
|
+
return CommandResult.error_result("No agent specified")
|
|
363
604
|
|
|
364
605
|
except Exception as e:
|
|
365
606
|
self.logger.error(f"Error fixing agents: {e}", exc_info=True)
|
|
@@ -368,26 +609,25 @@ class AgentsCommand(AgentCommand):
|
|
|
368
609
|
def _check_agent_dependencies(self, args) -> CommandResult:
|
|
369
610
|
"""Check agent dependencies."""
|
|
370
611
|
try:
|
|
371
|
-
|
|
612
|
+
agent_name = getattr(args, "agent", None)
|
|
613
|
+
result = self.dependency_service.check_dependencies(agent_name=agent_name)
|
|
372
614
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
615
|
+
if not result["success"]:
|
|
616
|
+
if "available_agents" in result:
|
|
617
|
+
print(f"❌ Agent '{agent_name}' is not deployed")
|
|
618
|
+
print(
|
|
619
|
+
f" Available agents: {', '.join(result['available_agents'])}"
|
|
620
|
+
)
|
|
621
|
+
return CommandResult.error_result(
|
|
622
|
+
result.get("error", "Dependency check failed")
|
|
378
623
|
)
|
|
379
|
-
else:
|
|
380
|
-
# Text output
|
|
381
|
-
print("Agent Dependencies Check:")
|
|
382
|
-
print("-" * 40)
|
|
383
|
-
if result.get('missing_dependencies'):
|
|
384
|
-
print("Missing dependencies:")
|
|
385
|
-
for dep in result['missing_dependencies']:
|
|
386
|
-
print(f" - {dep}")
|
|
387
|
-
else:
|
|
388
|
-
print("✓ All dependencies satisfied")
|
|
389
624
|
|
|
390
|
-
|
|
625
|
+
# Print the formatted report
|
|
626
|
+
print(result["report"])
|
|
627
|
+
|
|
628
|
+
return CommandResult.success_result(
|
|
629
|
+
"Dependency check completed", data=result
|
|
630
|
+
)
|
|
391
631
|
|
|
392
632
|
except Exception as e:
|
|
393
633
|
self.logger.error(f"Error checking dependencies: {e}", exc_info=True)
|
|
@@ -396,23 +636,46 @@ class AgentsCommand(AgentCommand):
|
|
|
396
636
|
def _install_agent_dependencies(self, args) -> CommandResult:
|
|
397
637
|
"""Install agent dependencies."""
|
|
398
638
|
try:
|
|
399
|
-
|
|
639
|
+
agent_name = getattr(args, "agent", None)
|
|
640
|
+
dry_run = getattr(args, "dry_run", False)
|
|
641
|
+
result = self.dependency_service.install_dependencies(
|
|
642
|
+
agent_name=agent_name, dry_run=dry_run
|
|
643
|
+
)
|
|
400
644
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
645
|
+
if not result["success"]:
|
|
646
|
+
if "available_agents" in result:
|
|
647
|
+
print(f"❌ Agent '{agent_name}' is not deployed")
|
|
648
|
+
print(
|
|
649
|
+
f" Available agents: {', '.join(result['available_agents'])}"
|
|
650
|
+
)
|
|
651
|
+
return CommandResult.error_result(
|
|
652
|
+
result.get("error", "Installation failed")
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
if result.get("missing_count") == 0:
|
|
656
|
+
print("✅ All Python dependencies are already installed")
|
|
657
|
+
elif dry_run:
|
|
658
|
+
print(
|
|
659
|
+
f"Found {len(result['missing_dependencies'])} missing dependencies:"
|
|
406
660
|
)
|
|
661
|
+
for dep in result["missing_dependencies"]:
|
|
662
|
+
print(f" - {dep}")
|
|
663
|
+
print("\n--dry-run specified, not installing anything")
|
|
664
|
+
print(f"Would install: {result['install_command']}")
|
|
407
665
|
else:
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
666
|
+
print(
|
|
667
|
+
f"✅ Successfully installed {len(result.get('installed', []))} dependencies"
|
|
668
|
+
)
|
|
669
|
+
if result.get("still_missing"):
|
|
670
|
+
print(
|
|
671
|
+
f"⚠️ {len(result['still_missing'])} dependencies still missing after installation"
|
|
672
|
+
)
|
|
673
|
+
elif result.get("fully_resolved"):
|
|
674
|
+
print("✅ All dependencies verified after installation")
|
|
414
675
|
|
|
415
|
-
|
|
676
|
+
return CommandResult.success_result(
|
|
677
|
+
"Dependency installation completed", data=result
|
|
678
|
+
)
|
|
416
679
|
|
|
417
680
|
except Exception as e:
|
|
418
681
|
self.logger.error(f"Error installing dependencies: {e}", exc_info=True)
|
|
@@ -421,27 +684,58 @@ class AgentsCommand(AgentCommand):
|
|
|
421
684
|
def _list_agent_dependencies(self, args) -> CommandResult:
|
|
422
685
|
"""List agent dependencies."""
|
|
423
686
|
try:
|
|
424
|
-
|
|
687
|
+
output_format = getattr(args, "format", "text")
|
|
688
|
+
result = self.dependency_service.list_dependencies(
|
|
689
|
+
format_type=output_format
|
|
690
|
+
)
|
|
425
691
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
print("
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
print(
|
|
692
|
+
if not result["success"]:
|
|
693
|
+
return CommandResult.error_result(result.get("error", "Listing failed"))
|
|
694
|
+
|
|
695
|
+
# Format output based on requested format
|
|
696
|
+
if output_format == "pip":
|
|
697
|
+
for dep in result["dependencies"]:
|
|
698
|
+
print(dep)
|
|
699
|
+
elif output_format == "json":
|
|
700
|
+
print(json.dumps(result["data"], indent=2))
|
|
701
|
+
else: # text format
|
|
702
|
+
print("=" * 60)
|
|
703
|
+
print("DEPENDENCIES FROM DEPLOYED AGENTS")
|
|
704
|
+
print("=" * 60)
|
|
705
|
+
print()
|
|
706
|
+
|
|
707
|
+
if result["python_dependencies"]:
|
|
708
|
+
print(
|
|
709
|
+
f"Python Dependencies ({len(result['python_dependencies'])}):"
|
|
710
|
+
)
|
|
711
|
+
print("-" * 30)
|
|
712
|
+
for dep in result["python_dependencies"]:
|
|
713
|
+
print(f" {dep}")
|
|
714
|
+
print()
|
|
715
|
+
|
|
716
|
+
if result["system_dependencies"]:
|
|
717
|
+
print(
|
|
718
|
+
f"System Dependencies ({len(result['system_dependencies'])}):"
|
|
719
|
+
)
|
|
720
|
+
print("-" * 30)
|
|
721
|
+
for dep in result["system_dependencies"]:
|
|
722
|
+
print(f" {dep}")
|
|
723
|
+
print()
|
|
443
724
|
|
|
444
|
-
|
|
725
|
+
print("Per-Agent Dependencies:")
|
|
726
|
+
print("-" * 30)
|
|
727
|
+
for agent_id in sorted(result["per_agent"].keys()):
|
|
728
|
+
deps = result["per_agent"][agent_id]
|
|
729
|
+
python_count = len(deps.get("python", []))
|
|
730
|
+
system_count = len(deps.get("system", []))
|
|
731
|
+
if python_count or system_count:
|
|
732
|
+
print(
|
|
733
|
+
f" {agent_id}: {python_count} Python, {system_count} System"
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
return CommandResult.success_result(
|
|
737
|
+
"Dependency listing completed", data=result
|
|
738
|
+
)
|
|
445
739
|
|
|
446
740
|
except Exception as e:
|
|
447
741
|
self.logger.error(f"Error listing dependencies: {e}", exc_info=True)
|
|
@@ -450,104 +744,134 @@ class AgentsCommand(AgentCommand):
|
|
|
450
744
|
def _fix_agent_dependencies(self, args) -> CommandResult:
|
|
451
745
|
"""Fix agent dependency issues."""
|
|
452
746
|
try:
|
|
453
|
-
|
|
747
|
+
max_retries = getattr(args, "max_retries", 3)
|
|
748
|
+
agent_name = getattr(args, "agent", None)
|
|
454
749
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
750
|
+
print("=" * 70)
|
|
751
|
+
print("FIXING AGENT DEPENDENCIES WITH RETRY LOGIC")
|
|
752
|
+
print("=" * 70)
|
|
753
|
+
print()
|
|
754
|
+
|
|
755
|
+
result = self.dependency_service.fix_dependencies(
|
|
756
|
+
max_retries=max_retries, agent_name=agent_name
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
if not result["success"]:
|
|
760
|
+
if "error" in result and "not deployed" in result["error"]:
|
|
761
|
+
print(f"❌ {result['error']}")
|
|
762
|
+
return CommandResult.error_result(result.get("error", "Fix failed"))
|
|
763
|
+
|
|
764
|
+
if result.get("message") == "No deployed agents found":
|
|
765
|
+
print("No deployed agents found")
|
|
766
|
+
return CommandResult.success_result("No agents to fix")
|
|
767
|
+
|
|
768
|
+
if result.get("message") == "All dependencies are already satisfied":
|
|
769
|
+
print("\n✅ All dependencies are already satisfied!")
|
|
770
|
+
return CommandResult.success_result("All dependencies satisfied")
|
|
771
|
+
|
|
772
|
+
# Show what's missing
|
|
773
|
+
if result.get("missing_python"):
|
|
774
|
+
print(f"\n❌ Missing Python packages: {len(result['missing_python'])}")
|
|
775
|
+
for pkg in result["missing_python"][:10]:
|
|
776
|
+
print(f" - {pkg}")
|
|
777
|
+
if len(result["missing_python"]) > 10:
|
|
778
|
+
print(f" ... and {len(result['missing_python']) - 10} more")
|
|
779
|
+
|
|
780
|
+
if result.get("missing_system"):
|
|
781
|
+
print(f"\n❌ Missing system commands: {len(result['missing_system'])}")
|
|
782
|
+
for cmd in result["missing_system"]:
|
|
783
|
+
print(f" - {cmd}")
|
|
784
|
+
print("\n⚠️ System dependencies must be installed manually:")
|
|
785
|
+
print(f" macOS: brew install {' '.join(result['missing_system'])}")
|
|
786
|
+
print(f" Ubuntu: apt-get install {' '.join(result['missing_system'])}")
|
|
787
|
+
|
|
788
|
+
# Show incompatible packages
|
|
789
|
+
if result.get("incompatible"):
|
|
790
|
+
print(
|
|
791
|
+
f"\n⚠️ Skipping {len(result['incompatible'])} incompatible packages:"
|
|
460
792
|
)
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
793
|
+
for pkg in result["incompatible"][:5]:
|
|
794
|
+
print(f" - {pkg}")
|
|
795
|
+
if len(result["incompatible"]) > 5:
|
|
796
|
+
print(f" ... and {len(result['incompatible']) - 5} more")
|
|
797
|
+
|
|
798
|
+
# Show installation results
|
|
799
|
+
if result.get("fixed_python") or result.get("failed_python"):
|
|
800
|
+
print("\n" + "=" * 70)
|
|
801
|
+
print("INSTALLATION RESULTS:")
|
|
802
|
+
print("=" * 70)
|
|
803
|
+
|
|
804
|
+
if result.get("fixed_python"):
|
|
805
|
+
print(
|
|
806
|
+
f"✅ Successfully installed: {len(result['fixed_python'])} packages"
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
if result.get("failed_python"):
|
|
810
|
+
print(
|
|
811
|
+
f"❌ Failed to install: {len(result['failed_python'])} packages"
|
|
812
|
+
)
|
|
813
|
+
errors = result.get("errors", {})
|
|
814
|
+
for pkg in result["failed_python"]:
|
|
815
|
+
print(f" - {pkg}: {errors.get(pkg, 'Unknown error')}")
|
|
816
|
+
|
|
817
|
+
# Final verification
|
|
818
|
+
if result.get("still_missing") is not None:
|
|
819
|
+
if not result["still_missing"]:
|
|
820
|
+
print("\n✅ All Python dependencies are now satisfied!")
|
|
821
|
+
else:
|
|
822
|
+
print(
|
|
823
|
+
f"\n⚠️ Still missing {len(result['still_missing'])} packages"
|
|
824
|
+
)
|
|
825
|
+
print("\nTry running again or install manually:")
|
|
826
|
+
missing_sample = result["still_missing"][:3]
|
|
827
|
+
print(f" pip install {' '.join(missing_sample)}")
|
|
467
828
|
|
|
468
|
-
|
|
829
|
+
print("\n" + "=" * 70)
|
|
830
|
+
print("DONE")
|
|
831
|
+
print("=" * 70)
|
|
832
|
+
|
|
833
|
+
return CommandResult.success_result("Dependency fix completed", data=result)
|
|
469
834
|
|
|
470
835
|
except Exception as e:
|
|
471
836
|
self.logger.error(f"Error fixing dependencies: {e}", exc_info=True)
|
|
472
837
|
return CommandResult.error_result(f"Error fixing dependencies: {e}")
|
|
473
|
-
|
|
838
|
+
|
|
474
839
|
def _cleanup_orphaned_agents(self, args) -> CommandResult:
|
|
475
840
|
"""Clean up orphaned agents that don't have templates."""
|
|
476
841
|
try:
|
|
477
|
-
from ...services.agents.deployment.multi_source_deployment_service import (
|
|
478
|
-
MultiSourceAgentDeploymentService
|
|
479
|
-
)
|
|
480
|
-
|
|
481
842
|
# Determine agents directory
|
|
482
|
-
|
|
843
|
+
agents_dir = None
|
|
844
|
+
if hasattr(args, "agents_dir") and args.agents_dir:
|
|
483
845
|
agents_dir = args.agents_dir
|
|
484
|
-
|
|
485
|
-
# Check for project-level .claude/agents first
|
|
486
|
-
project_agents_dir = Path.cwd() / ".claude" / "agents"
|
|
487
|
-
if project_agents_dir.exists():
|
|
488
|
-
agents_dir = project_agents_dir
|
|
489
|
-
else:
|
|
490
|
-
# Fall back to user home directory
|
|
491
|
-
agents_dir = Path.home() / ".claude" / "agents"
|
|
492
|
-
|
|
493
|
-
if not agents_dir.exists():
|
|
494
|
-
return CommandResult.success_result(f"Agents directory not found: {agents_dir}")
|
|
495
|
-
|
|
496
|
-
# Initialize service
|
|
497
|
-
service = MultiSourceAgentDeploymentService()
|
|
498
|
-
|
|
846
|
+
|
|
499
847
|
# Determine if we're doing a dry run
|
|
500
|
-
dry_run = getattr(args,
|
|
501
|
-
if hasattr(args,
|
|
848
|
+
dry_run = getattr(args, "dry_run", True)
|
|
849
|
+
if hasattr(args, "force") and args.force:
|
|
502
850
|
dry_run = False
|
|
503
|
-
|
|
504
|
-
# Perform cleanup
|
|
505
|
-
results =
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
if not quiet:
|
|
522
|
-
print(f"\nFound {len(results['orphaned'])} orphaned agent(s):")
|
|
523
|
-
for orphan in results["orphaned"]:
|
|
524
|
-
print(f" - {orphan['name']} v{orphan['version']}")
|
|
525
|
-
|
|
526
|
-
if dry_run:
|
|
527
|
-
print(
|
|
528
|
-
f"\n📝 This was a dry run. Use --force to actually remove "
|
|
529
|
-
f"{len(results['orphaned'])} orphaned agent(s)"
|
|
530
|
-
)
|
|
531
|
-
else:
|
|
532
|
-
if results.get("removed"):
|
|
533
|
-
print(
|
|
534
|
-
f"\n✅ Successfully removed {len(results['removed'])} orphaned agent(s)"
|
|
535
|
-
)
|
|
536
|
-
|
|
537
|
-
if results.get("errors"):
|
|
538
|
-
print(f"\n❌ Encountered {len(results['errors'])} error(s):")
|
|
539
|
-
for error in results["errors"]:
|
|
540
|
-
print(f" - {error}")
|
|
541
|
-
return CommandResult.error_result(
|
|
542
|
-
f"Cleanup completed with {len(results['errors'])} errors",
|
|
543
|
-
data=results
|
|
544
|
-
)
|
|
545
|
-
|
|
546
|
-
return CommandResult.success_result(
|
|
547
|
-
f"Cleanup {'preview' if dry_run else 'completed'}",
|
|
548
|
-
data=results
|
|
851
|
+
|
|
852
|
+
# Perform cleanup using the cleanup service
|
|
853
|
+
results = self.cleanup_service.clean_orphaned_agents(
|
|
854
|
+
agents_dir=agents_dir, dry_run=dry_run
|
|
855
|
+
)
|
|
856
|
+
|
|
857
|
+
output_format = getattr(args, "format", "text")
|
|
858
|
+
|
|
859
|
+
formatted = self._formatter.format_cleanup_result(
|
|
860
|
+
results, output_format=output_format, dry_run=dry_run
|
|
861
|
+
)
|
|
862
|
+
print(formatted)
|
|
863
|
+
|
|
864
|
+
# Determine success/error based on results
|
|
865
|
+
if results.get("errors") and not dry_run:
|
|
866
|
+
return CommandResult.error_result(
|
|
867
|
+
f"Cleanup completed with {len(results['errors'])} errors",
|
|
868
|
+
data=results,
|
|
549
869
|
)
|
|
550
|
-
|
|
870
|
+
|
|
871
|
+
return CommandResult.success_result(
|
|
872
|
+
f"Cleanup {'preview' if dry_run else 'completed'}", data=results
|
|
873
|
+
)
|
|
874
|
+
|
|
551
875
|
except Exception as e:
|
|
552
876
|
self.logger.error(f"Error during cleanup: {e}", exc_info=True)
|
|
553
877
|
return CommandResult.error_result(f"Error during cleanup: {e}")
|
|
@@ -563,781 +887,7 @@ def manage_agents(args):
|
|
|
563
887
|
result = command.execute(args)
|
|
564
888
|
|
|
565
889
|
# Print result if structured output format is requested
|
|
566
|
-
if hasattr(args,
|
|
890
|
+
if hasattr(args, "format") and args.format in ["json", "yaml"]:
|
|
567
891
|
command.print_result(result, args)
|
|
568
892
|
|
|
569
893
|
return result.exit_code
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
def _list_agents(args, deployment_service):
|
|
573
|
-
"""Legacy function for backward compatibility."""
|
|
574
|
-
command = AgentsCommand()
|
|
575
|
-
command._deployment_service = deployment_service
|
|
576
|
-
result = command._list_agents(args)
|
|
577
|
-
return result.exit_code
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
def _deploy_agents(args, deployment_service, force=False):
|
|
581
|
-
"""
|
|
582
|
-
Deploy both system and project agents.
|
|
583
|
-
|
|
584
|
-
WHY: Agents need to be deployed to the working directory for Claude Code to use them.
|
|
585
|
-
This function handles both regular and forced deployment, including project-specific agents.
|
|
586
|
-
|
|
587
|
-
Args:
|
|
588
|
-
args: Command arguments with optional 'target' path
|
|
589
|
-
deployment_service: Agent deployment service instance
|
|
590
|
-
force: Whether to force rebuild all agents
|
|
591
|
-
"""
|
|
592
|
-
# Load configuration to get exclusion settings using ConfigLoader
|
|
593
|
-
config_loader = ConfigLoader()
|
|
594
|
-
config = config_loader.load_main_config()
|
|
595
|
-
|
|
596
|
-
# Check if user wants to override exclusions
|
|
597
|
-
if hasattr(args, "include_all") and args.include_all:
|
|
598
|
-
# Clear exclusion list if --include-all flag is set
|
|
599
|
-
config.set("agent_deployment.excluded_agents", [])
|
|
600
|
-
print("✅ Including all agents (exclusion configuration overridden)\n")
|
|
601
|
-
else:
|
|
602
|
-
excluded_agents = config.get("agent_deployment.excluded_agents", [])
|
|
603
|
-
|
|
604
|
-
# Display exclusion information if agents are being excluded
|
|
605
|
-
if excluded_agents:
|
|
606
|
-
logger = get_logger("cli")
|
|
607
|
-
logger.info(f"Configured agent exclusions: {excluded_agents}")
|
|
608
|
-
print(
|
|
609
|
-
f"\n⚠️ Excluding agents from deployment: {', '.join(excluded_agents)}"
|
|
610
|
-
)
|
|
611
|
-
|
|
612
|
-
# Warn if commonly used agents are being excluded
|
|
613
|
-
common_agents = {"engineer", "qa", "security", "documentation"}
|
|
614
|
-
excluded_common = set(a.lower() for a in excluded_agents) & common_agents
|
|
615
|
-
if excluded_common:
|
|
616
|
-
print(
|
|
617
|
-
f"⚠️ Warning: Common agents are being excluded: {', '.join(excluded_common)}"
|
|
618
|
-
)
|
|
619
|
-
print(
|
|
620
|
-
" This may affect normal operations. Use 'claude-mpm agents deploy --include-all' to override.\n"
|
|
621
|
-
)
|
|
622
|
-
|
|
623
|
-
# Deploy system agents first
|
|
624
|
-
if force:
|
|
625
|
-
print("Force deploying all system agents...")
|
|
626
|
-
else:
|
|
627
|
-
print("Deploying system agents...")
|
|
628
|
-
|
|
629
|
-
results = deployment_service.deploy_agents(None, force_rebuild=force, config=config)
|
|
630
|
-
|
|
631
|
-
# Also deploy project agents if they exist
|
|
632
|
-
import os
|
|
633
|
-
from pathlib import Path
|
|
634
|
-
|
|
635
|
-
# Use the user's working directory if available
|
|
636
|
-
if "CLAUDE_MPM_USER_PWD" in os.environ:
|
|
637
|
-
project_dir = Path(os.environ["CLAUDE_MPM_USER_PWD"])
|
|
638
|
-
else:
|
|
639
|
-
project_dir = Path.cwd()
|
|
640
|
-
|
|
641
|
-
project_agents_dir = project_dir / ".claude-mpm" / "agents"
|
|
642
|
-
if project_agents_dir.exists():
|
|
643
|
-
json_files = list(project_agents_dir.glob("*.json"))
|
|
644
|
-
if json_files:
|
|
645
|
-
print(f"\nDeploying {len(json_files)} project agents...")
|
|
646
|
-
from claude_mpm.services.agents.deployment.agent_deployment import (
|
|
647
|
-
AgentDeploymentService,
|
|
648
|
-
)
|
|
649
|
-
|
|
650
|
-
project_service = AgentDeploymentService(
|
|
651
|
-
templates_dir=project_agents_dir,
|
|
652
|
-
base_agent_path=(
|
|
653
|
-
project_agents_dir / "base_agent.json"
|
|
654
|
-
if (project_agents_dir / "base_agent.json").exists()
|
|
655
|
-
else None
|
|
656
|
-
),
|
|
657
|
-
working_directory=project_dir, # Pass the project directory
|
|
658
|
-
)
|
|
659
|
-
# Pass the same configuration to project agent deployment
|
|
660
|
-
# For project agents, let the service determine they should stay in project directory
|
|
661
|
-
project_results = project_service.deploy_agents(
|
|
662
|
-
target_dir=None, # Let service detect it's a project deployment
|
|
663
|
-
force_rebuild=force,
|
|
664
|
-
deployment_mode="project",
|
|
665
|
-
config=config,
|
|
666
|
-
)
|
|
667
|
-
|
|
668
|
-
# Merge project results into main results
|
|
669
|
-
if project_results.get("deployed"):
|
|
670
|
-
results["deployed"].extend(project_results["deployed"])
|
|
671
|
-
print(f"✓ Deployed {len(project_results['deployed'])} project agents")
|
|
672
|
-
if project_results.get("updated"):
|
|
673
|
-
results["updated"].extend(project_results["updated"])
|
|
674
|
-
print(f"✓ Updated {len(project_results['updated'])} project agents")
|
|
675
|
-
if project_results.get("errors"):
|
|
676
|
-
results["errors"].extend(project_results["errors"])
|
|
677
|
-
|
|
678
|
-
if results["deployed"]:
|
|
679
|
-
print(
|
|
680
|
-
f"\n✓ Successfully deployed {len(results['deployed'])} agents to {results['target_dir']}"
|
|
681
|
-
)
|
|
682
|
-
for agent in results["deployed"]:
|
|
683
|
-
print(f" - {agent['name']}")
|
|
684
|
-
|
|
685
|
-
if force and results.get("updated", []):
|
|
686
|
-
print(f"\n✓ Updated {len(results['updated'])} agents")
|
|
687
|
-
for agent in results["updated"]:
|
|
688
|
-
print(f" - {agent['name']}")
|
|
689
|
-
|
|
690
|
-
if force and results.get("skipped", []):
|
|
691
|
-
print(f"\n✓ Skipped {len(results['skipped'])} up-to-date agents")
|
|
692
|
-
|
|
693
|
-
if results["errors"]:
|
|
694
|
-
print("\n❌ Errors during deployment:")
|
|
695
|
-
for error in results["errors"]:
|
|
696
|
-
print(f" - {error}")
|
|
697
|
-
|
|
698
|
-
if force:
|
|
699
|
-
# Set environment for force deploy
|
|
700
|
-
env_vars = deployment_service.set_claude_environment(
|
|
701
|
-
args.target.parent if args.target else None
|
|
702
|
-
)
|
|
703
|
-
print(f"\n✓ Set Claude environment variables:")
|
|
704
|
-
for key, value in env_vars.items():
|
|
705
|
-
print(f" - {key}={value}")
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
def _clean_agents(args, deployment_service):
|
|
709
|
-
"""
|
|
710
|
-
Clean deployed system agents.
|
|
711
|
-
|
|
712
|
-
WHY: Users may want to remove deployed agents to start fresh or clean up
|
|
713
|
-
their working directory.
|
|
714
|
-
|
|
715
|
-
Args:
|
|
716
|
-
args: Command arguments with optional 'target' path
|
|
717
|
-
deployment_service: Agent deployment service instance
|
|
718
|
-
"""
|
|
719
|
-
print("Cleaning deployed system agents...")
|
|
720
|
-
results = deployment_service.clean_deployment(args.target)
|
|
721
|
-
|
|
722
|
-
if results["removed"]:
|
|
723
|
-
print(f"\n✓ Removed {len(results['removed'])} agents")
|
|
724
|
-
for path in results["removed"]:
|
|
725
|
-
print(f" - {Path(path).name}")
|
|
726
|
-
else:
|
|
727
|
-
print("No system agents found to remove")
|
|
728
|
-
|
|
729
|
-
if results["errors"]:
|
|
730
|
-
print("\n❌ Errors during cleanup:")
|
|
731
|
-
for error in results["errors"]:
|
|
732
|
-
print(f" - {error}")
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
def _list_agents_by_tier():
|
|
736
|
-
"""
|
|
737
|
-
List agents grouped by precedence tier.
|
|
738
|
-
|
|
739
|
-
WHY: Users need to understand which agents are active across different tiers
|
|
740
|
-
and which version takes precedence when multiple versions exist.
|
|
741
|
-
"""
|
|
742
|
-
try:
|
|
743
|
-
adapter = AgentRegistryAdapter()
|
|
744
|
-
if not adapter.registry:
|
|
745
|
-
print("❌ Could not initialize agent registry")
|
|
746
|
-
return
|
|
747
|
-
|
|
748
|
-
# Get all agents and group by tier
|
|
749
|
-
all_agents = adapter.registry.list_agents()
|
|
750
|
-
|
|
751
|
-
# Group agents by tier and name
|
|
752
|
-
tiers = {"project": {}, "user": {}, "system": {}}
|
|
753
|
-
agent_names = set()
|
|
754
|
-
|
|
755
|
-
for agent_id, metadata in all_agents.items():
|
|
756
|
-
tier = metadata.get("tier", "system")
|
|
757
|
-
if tier in tiers:
|
|
758
|
-
tiers[tier][agent_id] = metadata
|
|
759
|
-
agent_names.add(agent_id)
|
|
760
|
-
|
|
761
|
-
# Display header
|
|
762
|
-
print("\n" + "=" * 80)
|
|
763
|
-
print(" " * 25 + "AGENT HIERARCHY BY TIER")
|
|
764
|
-
print("=" * 80)
|
|
765
|
-
print("\nPrecedence: PROJECT > USER > SYSTEM")
|
|
766
|
-
print("(Agents in higher tiers override those in lower tiers)\n")
|
|
767
|
-
|
|
768
|
-
# Display each tier
|
|
769
|
-
tier_order = [("PROJECT", "project"), ("USER", "user"), ("SYSTEM", "system")]
|
|
770
|
-
|
|
771
|
-
for tier_display, tier_key in tier_order:
|
|
772
|
-
agents = tiers[tier_key]
|
|
773
|
-
print(f"\n{'─' * 35} {tier_display} TIER {'─' * 35}")
|
|
774
|
-
|
|
775
|
-
if not agents:
|
|
776
|
-
print(f" No agents at {tier_key} level")
|
|
777
|
-
else:
|
|
778
|
-
# Check paths to determine actual locations
|
|
779
|
-
if tier_key == "project":
|
|
780
|
-
print(f" Location: .claude-mpm/agents/ (in current project)")
|
|
781
|
-
elif tier_key == "user":
|
|
782
|
-
print(f" Location: ~/.claude-mpm/agents/")
|
|
783
|
-
else:
|
|
784
|
-
print(f" Location: Built-in framework agents")
|
|
785
|
-
|
|
786
|
-
print(f"\n Found {len(agents)} agent(s):\n")
|
|
787
|
-
|
|
788
|
-
for agent_id, metadata in sorted(agents.items()):
|
|
789
|
-
# Check if this agent is overridden by higher tiers
|
|
790
|
-
is_active = True
|
|
791
|
-
overridden_by = []
|
|
792
|
-
|
|
793
|
-
for check_tier_display, check_tier_key in tier_order:
|
|
794
|
-
if check_tier_key == tier_key:
|
|
795
|
-
break
|
|
796
|
-
if agent_id in tiers[check_tier_key]:
|
|
797
|
-
is_active = False
|
|
798
|
-
overridden_by.append(check_tier_display)
|
|
799
|
-
|
|
800
|
-
# Display agent info
|
|
801
|
-
status = (
|
|
802
|
-
"✓ ACTIVE"
|
|
803
|
-
if is_active
|
|
804
|
-
else f"⊗ OVERRIDDEN by {', '.join(overridden_by)}"
|
|
805
|
-
)
|
|
806
|
-
print(f" 📄 {agent_id:<20} [{status}]")
|
|
807
|
-
|
|
808
|
-
# Show metadata
|
|
809
|
-
if "description" in metadata:
|
|
810
|
-
print(f" Description: {metadata['description']}")
|
|
811
|
-
if "path" in metadata:
|
|
812
|
-
path = Path(metadata["path"])
|
|
813
|
-
print(f" File: {path.name}")
|
|
814
|
-
print()
|
|
815
|
-
|
|
816
|
-
# Summary
|
|
817
|
-
print("\n" + "=" * 80)
|
|
818
|
-
print("SUMMARY:")
|
|
819
|
-
print(f" Total unique agents: {len(agent_names)}")
|
|
820
|
-
print(f" Project agents: {len(tiers['project'])}")
|
|
821
|
-
print(f" User agents: {len(tiers['user'])}")
|
|
822
|
-
print(f" System agents: {len(tiers['system'])}")
|
|
823
|
-
print("=" * 80 + "\n")
|
|
824
|
-
|
|
825
|
-
except Exception as e:
|
|
826
|
-
print(f"❌ Error listing agents by tier: {e}")
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
def _view_agent(args):
|
|
830
|
-
"""
|
|
831
|
-
View detailed information about a specific agent.
|
|
832
|
-
|
|
833
|
-
WHY: Users need to inspect agent configurations, frontmatter, and instructions
|
|
834
|
-
to understand what an agent does and how it's configured.
|
|
835
|
-
|
|
836
|
-
Args:
|
|
837
|
-
args: Command arguments with 'agent_name' attribute
|
|
838
|
-
"""
|
|
839
|
-
if not hasattr(args, "agent_name") or not args.agent_name:
|
|
840
|
-
print("❌ Please specify an agent name to view")
|
|
841
|
-
print("Usage: claude-mpm agents view <agent_name>")
|
|
842
|
-
return
|
|
843
|
-
|
|
844
|
-
try:
|
|
845
|
-
adapter = AgentRegistryAdapter()
|
|
846
|
-
if not adapter.registry:
|
|
847
|
-
print("❌ Could not initialize agent registry")
|
|
848
|
-
return
|
|
849
|
-
|
|
850
|
-
# Get the agent
|
|
851
|
-
agent = adapter.registry.get_agent(args.agent_name)
|
|
852
|
-
if not agent:
|
|
853
|
-
print(f"❌ Agent '{args.agent_name}' not found")
|
|
854
|
-
print("\nAvailable agents:")
|
|
855
|
-
all_agents = adapter.registry.list_agents()
|
|
856
|
-
for agent_id in sorted(all_agents.keys()):
|
|
857
|
-
print(f" - {agent_id}")
|
|
858
|
-
return
|
|
859
|
-
|
|
860
|
-
# Read the agent file
|
|
861
|
-
agent_path = Path(agent.path)
|
|
862
|
-
if not agent_path.exists():
|
|
863
|
-
print(f"❌ Agent file not found: {agent_path}")
|
|
864
|
-
return
|
|
865
|
-
|
|
866
|
-
with open(agent_path, "r") as f:
|
|
867
|
-
content = f.read()
|
|
868
|
-
|
|
869
|
-
# Display agent information
|
|
870
|
-
print("\n" + "=" * 80)
|
|
871
|
-
print(f" AGENT: {agent.name}")
|
|
872
|
-
print("=" * 80)
|
|
873
|
-
|
|
874
|
-
# Basic info
|
|
875
|
-
print(f"\n📋 BASIC INFORMATION:")
|
|
876
|
-
print(f" Name: {agent.name}")
|
|
877
|
-
print(f" Type: {agent.type}")
|
|
878
|
-
print(f" Tier: {agent.tier.upper()}")
|
|
879
|
-
print(f" Path: {agent_path}")
|
|
880
|
-
if agent.description:
|
|
881
|
-
print(f" Description: {agent.description}")
|
|
882
|
-
if agent.specializations:
|
|
883
|
-
print(f" Specializations: {', '.join(agent.specializations)}")
|
|
884
|
-
|
|
885
|
-
# Extract and display frontmatter
|
|
886
|
-
if content.startswith("---"):
|
|
887
|
-
try:
|
|
888
|
-
end_marker = content.find("\n---\n", 4)
|
|
889
|
-
if end_marker == -1:
|
|
890
|
-
end_marker = content.find("\n---\r\n", 4)
|
|
891
|
-
|
|
892
|
-
if end_marker != -1:
|
|
893
|
-
frontmatter_str = content[4:end_marker]
|
|
894
|
-
frontmatter = yaml.safe_load(frontmatter_str)
|
|
895
|
-
|
|
896
|
-
print(f"\n📝 FRONTMATTER:")
|
|
897
|
-
for key, value in frontmatter.items():
|
|
898
|
-
if isinstance(value, list):
|
|
899
|
-
print(f" {key}: [{', '.join(str(v) for v in value)}]")
|
|
900
|
-
elif isinstance(value, dict):
|
|
901
|
-
print(f" {key}:")
|
|
902
|
-
for k, v in value.items():
|
|
903
|
-
print(f" {k}: {v}")
|
|
904
|
-
else:
|
|
905
|
-
print(f" {key}: {value}")
|
|
906
|
-
|
|
907
|
-
# Extract instructions preview
|
|
908
|
-
instructions_start = end_marker + 5
|
|
909
|
-
instructions = content[instructions_start:].strip()
|
|
910
|
-
|
|
911
|
-
if instructions:
|
|
912
|
-
print(f"\n📖 INSTRUCTIONS PREVIEW (first 500 chars):")
|
|
913
|
-
print(" " + "-" * 76)
|
|
914
|
-
preview = instructions[:500]
|
|
915
|
-
if len(instructions) > 500:
|
|
916
|
-
preview += "...\n\n [Truncated - {:.1f}KB total]".format(
|
|
917
|
-
len(instructions) / 1024
|
|
918
|
-
)
|
|
919
|
-
|
|
920
|
-
for line in preview.split("\n"):
|
|
921
|
-
print(f" {line}")
|
|
922
|
-
print(" " + "-" * 76)
|
|
923
|
-
except Exception as e:
|
|
924
|
-
print(f"\n⚠️ Could not parse frontmatter: {e}")
|
|
925
|
-
else:
|
|
926
|
-
print(f"\n⚠️ No frontmatter found in agent file")
|
|
927
|
-
|
|
928
|
-
# File stats
|
|
929
|
-
import os
|
|
930
|
-
|
|
931
|
-
stat = os.stat(agent_path)
|
|
932
|
-
from datetime import datetime
|
|
933
|
-
|
|
934
|
-
modified = datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
|
|
935
|
-
print(f"\n📊 FILE STATS:")
|
|
936
|
-
print(f" Size: {stat.st_size:,} bytes")
|
|
937
|
-
print(f" Last modified: {modified}")
|
|
938
|
-
|
|
939
|
-
print("\n" + "=" * 80 + "\n")
|
|
940
|
-
|
|
941
|
-
except Exception as e:
|
|
942
|
-
print(f"❌ Error viewing agent: {e}")
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
def _fix_agents(args):
|
|
946
|
-
"""
|
|
947
|
-
Fix agent frontmatter issues using FrontmatterValidator.
|
|
948
|
-
|
|
949
|
-
WHY: Agent files may have formatting issues in their frontmatter that prevent
|
|
950
|
-
proper loading. This command automatically fixes common issues.
|
|
951
|
-
|
|
952
|
-
Args:
|
|
953
|
-
args: Command arguments with 'agent_name', 'dry_run', and 'all' flags
|
|
954
|
-
"""
|
|
955
|
-
validator = FrontmatterValidator()
|
|
956
|
-
|
|
957
|
-
try:
|
|
958
|
-
adapter = AgentRegistryAdapter()
|
|
959
|
-
if not adapter.registry:
|
|
960
|
-
print("❌ Could not initialize agent registry")
|
|
961
|
-
return
|
|
962
|
-
|
|
963
|
-
# Determine which agents to fix
|
|
964
|
-
agents_to_fix = []
|
|
965
|
-
|
|
966
|
-
if hasattr(args, "all") and args.all:
|
|
967
|
-
# Fix all agents
|
|
968
|
-
all_agents = adapter.registry.list_agents()
|
|
969
|
-
for agent_id, metadata in all_agents.items():
|
|
970
|
-
agents_to_fix.append((agent_id, metadata["path"]))
|
|
971
|
-
print(
|
|
972
|
-
f"\n🔧 Checking {len(agents_to_fix)} agent(s) for frontmatter issues...\n"
|
|
973
|
-
)
|
|
974
|
-
elif hasattr(args, "agent_name") and args.agent_name:
|
|
975
|
-
# Fix specific agent
|
|
976
|
-
agent = adapter.registry.get_agent(args.agent_name)
|
|
977
|
-
if not agent:
|
|
978
|
-
print(f"❌ Agent '{args.agent_name}' not found")
|
|
979
|
-
return
|
|
980
|
-
agents_to_fix.append((agent.name, agent.path))
|
|
981
|
-
print(f"\n🔧 Checking agent '{agent.name}' for frontmatter issues...\n")
|
|
982
|
-
else:
|
|
983
|
-
print("❌ Please specify an agent name or use --all to fix all agents")
|
|
984
|
-
print("Usage: claude-mpm agents fix [agent_name] [--dry-run] [--all]")
|
|
985
|
-
return
|
|
986
|
-
|
|
987
|
-
dry_run = hasattr(args, "dry_run") and args.dry_run
|
|
988
|
-
if dry_run:
|
|
989
|
-
print("🔍 DRY RUN MODE - No changes will be made\n")
|
|
990
|
-
|
|
991
|
-
# Process each agent
|
|
992
|
-
total_issues = 0
|
|
993
|
-
total_fixed = 0
|
|
994
|
-
|
|
995
|
-
for agent_name, agent_path in agents_to_fix:
|
|
996
|
-
path = Path(agent_path)
|
|
997
|
-
if not path.exists():
|
|
998
|
-
print(f"⚠️ Skipping {agent_name}: File not found at {path}")
|
|
999
|
-
continue
|
|
1000
|
-
|
|
1001
|
-
print(f"📄 {agent_name}:")
|
|
1002
|
-
|
|
1003
|
-
# Validate and potentially fix
|
|
1004
|
-
result = validator.correct_file(path, dry_run=dry_run)
|
|
1005
|
-
|
|
1006
|
-
if result.is_valid and not result.corrections:
|
|
1007
|
-
print(" ✓ No issues found")
|
|
1008
|
-
else:
|
|
1009
|
-
if result.errors:
|
|
1010
|
-
print(" ❌ Errors:")
|
|
1011
|
-
for error in result.errors:
|
|
1012
|
-
print(f" - {error}")
|
|
1013
|
-
total_issues += len(result.errors)
|
|
1014
|
-
|
|
1015
|
-
if result.warnings:
|
|
1016
|
-
print(" ⚠️ Warnings:")
|
|
1017
|
-
for warning in result.warnings:
|
|
1018
|
-
print(f" - {warning}")
|
|
1019
|
-
total_issues += len(result.warnings)
|
|
1020
|
-
|
|
1021
|
-
if result.corrections:
|
|
1022
|
-
if dry_run:
|
|
1023
|
-
print(" 🔧 Would fix:")
|
|
1024
|
-
else:
|
|
1025
|
-
print(" ✓ Fixed:")
|
|
1026
|
-
total_fixed += len(result.corrections)
|
|
1027
|
-
for correction in result.corrections:
|
|
1028
|
-
print(f" - {correction}")
|
|
1029
|
-
|
|
1030
|
-
print()
|
|
1031
|
-
|
|
1032
|
-
# Summary
|
|
1033
|
-
print("=" * 80)
|
|
1034
|
-
print("SUMMARY:")
|
|
1035
|
-
print(f" Agents checked: {len(agents_to_fix)}")
|
|
1036
|
-
print(f" Total issues found: {total_issues}")
|
|
1037
|
-
if dry_run:
|
|
1038
|
-
print(
|
|
1039
|
-
f" Issues that would be fixed: {sum(1 for _, path in agents_to_fix if validator.validate_file(Path(path)).corrections)}"
|
|
1040
|
-
)
|
|
1041
|
-
print("\n💡 Run without --dry-run to apply fixes")
|
|
1042
|
-
else:
|
|
1043
|
-
print(f" Issues fixed: {total_fixed}")
|
|
1044
|
-
if total_fixed > 0:
|
|
1045
|
-
print("\n✓ Frontmatter issues have been fixed!")
|
|
1046
|
-
print("=" * 80 + "\n")
|
|
1047
|
-
|
|
1048
|
-
except Exception as e:
|
|
1049
|
-
print(f"❌ Error fixing agents: {e}")
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
def _check_agent_dependencies(args):
|
|
1053
|
-
"""
|
|
1054
|
-
Check dependencies for deployed agents.
|
|
1055
|
-
|
|
1056
|
-
Args:
|
|
1057
|
-
args: Parsed command line arguments
|
|
1058
|
-
"""
|
|
1059
|
-
from ...utils.agent_dependency_loader import AgentDependencyLoader
|
|
1060
|
-
|
|
1061
|
-
verbose = getattr(args, "verbose", False)
|
|
1062
|
-
specific_agent = getattr(args, "agent", None)
|
|
1063
|
-
|
|
1064
|
-
loader = AgentDependencyLoader(auto_install=False)
|
|
1065
|
-
|
|
1066
|
-
# Discover deployed agents
|
|
1067
|
-
loader.discover_deployed_agents()
|
|
1068
|
-
|
|
1069
|
-
# Filter to specific agent if requested
|
|
1070
|
-
if specific_agent:
|
|
1071
|
-
if specific_agent not in loader.deployed_agents:
|
|
1072
|
-
print(f"❌ Agent '{specific_agent}' is not deployed")
|
|
1073
|
-
print(f" Available agents: {', '.join(loader.deployed_agents.keys())}")
|
|
1074
|
-
return
|
|
1075
|
-
# Keep only the specified agent
|
|
1076
|
-
loader.deployed_agents = {
|
|
1077
|
-
specific_agent: loader.deployed_agents[specific_agent]
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
# Load dependencies and check
|
|
1081
|
-
loader.load_agent_dependencies()
|
|
1082
|
-
results = loader.analyze_dependencies()
|
|
1083
|
-
|
|
1084
|
-
# Print report
|
|
1085
|
-
report = loader.format_report(results)
|
|
1086
|
-
print(report)
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
def _install_agent_dependencies(args):
|
|
1090
|
-
"""
|
|
1091
|
-
Install missing dependencies for deployed agents.
|
|
1092
|
-
|
|
1093
|
-
Args:
|
|
1094
|
-
args: Parsed command line arguments
|
|
1095
|
-
"""
|
|
1096
|
-
import sys
|
|
1097
|
-
|
|
1098
|
-
from ...utils.agent_dependency_loader import AgentDependencyLoader
|
|
1099
|
-
|
|
1100
|
-
specific_agent = getattr(args, "agent", None)
|
|
1101
|
-
dry_run = getattr(args, "dry_run", False)
|
|
1102
|
-
|
|
1103
|
-
loader = AgentDependencyLoader(auto_install=not dry_run)
|
|
1104
|
-
|
|
1105
|
-
# Discover deployed agents
|
|
1106
|
-
loader.discover_deployed_agents()
|
|
1107
|
-
|
|
1108
|
-
# Filter to specific agent if requested
|
|
1109
|
-
if specific_agent:
|
|
1110
|
-
if specific_agent not in loader.deployed_agents:
|
|
1111
|
-
print(f"❌ Agent '{specific_agent}' is not deployed")
|
|
1112
|
-
print(f" Available agents: {', '.join(loader.deployed_agents.keys())}")
|
|
1113
|
-
return
|
|
1114
|
-
loader.deployed_agents = {
|
|
1115
|
-
specific_agent: loader.deployed_agents[specific_agent]
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
# Load dependencies
|
|
1119
|
-
loader.load_agent_dependencies()
|
|
1120
|
-
results = loader.analyze_dependencies()
|
|
1121
|
-
|
|
1122
|
-
missing_deps = results["summary"]["missing_python"]
|
|
1123
|
-
|
|
1124
|
-
if not missing_deps:
|
|
1125
|
-
print("✅ All Python dependencies are already installed")
|
|
1126
|
-
return
|
|
1127
|
-
|
|
1128
|
-
print(f"Found {len(missing_deps)} missing dependencies:")
|
|
1129
|
-
for dep in missing_deps:
|
|
1130
|
-
print(f" - {dep}")
|
|
1131
|
-
|
|
1132
|
-
if dry_run:
|
|
1133
|
-
print("\n--dry-run specified, not installing anything")
|
|
1134
|
-
print(f"Would install: pip install {' '.join(missing_deps)}")
|
|
1135
|
-
else:
|
|
1136
|
-
print(f"\nInstalling {len(missing_deps)} dependencies...")
|
|
1137
|
-
success, error = loader.install_missing_dependencies(missing_deps)
|
|
1138
|
-
|
|
1139
|
-
if success:
|
|
1140
|
-
print("✅ Successfully installed all dependencies")
|
|
1141
|
-
|
|
1142
|
-
# Re-check after installation
|
|
1143
|
-
loader.checked_packages.clear()
|
|
1144
|
-
results = loader.analyze_dependencies()
|
|
1145
|
-
|
|
1146
|
-
if results["summary"]["missing_python"]:
|
|
1147
|
-
print(
|
|
1148
|
-
f"⚠️ {len(results['summary']['missing_python'])} dependencies still missing after installation"
|
|
1149
|
-
)
|
|
1150
|
-
else:
|
|
1151
|
-
print("✅ All dependencies verified after installation")
|
|
1152
|
-
else:
|
|
1153
|
-
print(f"❌ Failed to install dependencies: {error}")
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
def _list_agent_dependencies(args):
|
|
1157
|
-
"""
|
|
1158
|
-
List all dependencies from deployed agents.
|
|
1159
|
-
|
|
1160
|
-
Args:
|
|
1161
|
-
args: Parsed command line arguments
|
|
1162
|
-
"""
|
|
1163
|
-
import json
|
|
1164
|
-
|
|
1165
|
-
from ...utils.agent_dependency_loader import AgentDependencyLoader
|
|
1166
|
-
|
|
1167
|
-
output_format = getattr(args, "format", "text")
|
|
1168
|
-
|
|
1169
|
-
loader = AgentDependencyLoader(auto_install=False)
|
|
1170
|
-
|
|
1171
|
-
# Discover and load
|
|
1172
|
-
loader.discover_deployed_agents()
|
|
1173
|
-
loader.load_agent_dependencies()
|
|
1174
|
-
|
|
1175
|
-
# Collect all unique dependencies
|
|
1176
|
-
all_python_deps = set()
|
|
1177
|
-
all_system_deps = set()
|
|
1178
|
-
|
|
1179
|
-
for agent_id, deps in loader.agent_dependencies.items():
|
|
1180
|
-
if "python" in deps:
|
|
1181
|
-
all_python_deps.update(deps["python"])
|
|
1182
|
-
if "system" in deps:
|
|
1183
|
-
all_system_deps.update(deps["system"])
|
|
1184
|
-
|
|
1185
|
-
# Format output based on requested format
|
|
1186
|
-
if output_format == "pip":
|
|
1187
|
-
# Output pip-installable format
|
|
1188
|
-
for dep in sorted(all_python_deps):
|
|
1189
|
-
print(dep)
|
|
1190
|
-
|
|
1191
|
-
elif output_format == "json":
|
|
1192
|
-
# Output JSON format
|
|
1193
|
-
output = {
|
|
1194
|
-
"python": sorted(list(all_python_deps)),
|
|
1195
|
-
"system": sorted(list(all_system_deps)),
|
|
1196
|
-
"agents": {},
|
|
1197
|
-
}
|
|
1198
|
-
for agent_id, deps in loader.agent_dependencies.items():
|
|
1199
|
-
output["agents"][agent_id] = deps
|
|
1200
|
-
print(json.dumps(output, indent=2))
|
|
1201
|
-
|
|
1202
|
-
else: # text format
|
|
1203
|
-
print("=" * 60)
|
|
1204
|
-
print("DEPENDENCIES FROM DEPLOYED AGENTS")
|
|
1205
|
-
print("=" * 60)
|
|
1206
|
-
print()
|
|
1207
|
-
|
|
1208
|
-
if all_python_deps:
|
|
1209
|
-
print(f"Python Dependencies ({len(all_python_deps)}):")
|
|
1210
|
-
print("-" * 30)
|
|
1211
|
-
for dep in sorted(all_python_deps):
|
|
1212
|
-
print(f" {dep}")
|
|
1213
|
-
print()
|
|
1214
|
-
|
|
1215
|
-
if all_system_deps:
|
|
1216
|
-
print(f"System Dependencies ({len(all_system_deps)}):")
|
|
1217
|
-
print("-" * 30)
|
|
1218
|
-
for dep in sorted(all_system_deps):
|
|
1219
|
-
print(f" {dep}")
|
|
1220
|
-
print()
|
|
1221
|
-
|
|
1222
|
-
print("Per-Agent Dependencies:")
|
|
1223
|
-
print("-" * 30)
|
|
1224
|
-
for agent_id in sorted(loader.agent_dependencies.keys()):
|
|
1225
|
-
deps = loader.agent_dependencies[agent_id]
|
|
1226
|
-
python_count = len(deps.get("python", []))
|
|
1227
|
-
system_count = len(deps.get("system", []))
|
|
1228
|
-
if python_count or system_count:
|
|
1229
|
-
print(f" {agent_id}: {python_count} Python, {system_count} System")
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
def _fix_agent_dependencies(args):
|
|
1233
|
-
"""
|
|
1234
|
-
Fix missing agent dependencies with robust retry logic.
|
|
1235
|
-
|
|
1236
|
-
WHY: Network issues and temporary package unavailability can cause
|
|
1237
|
-
dependency installation to fail. This command uses robust retry logic
|
|
1238
|
-
to maximize success rate.
|
|
1239
|
-
|
|
1240
|
-
Args:
|
|
1241
|
-
args: Parsed command line arguments
|
|
1242
|
-
"""
|
|
1243
|
-
from ...utils.agent_dependency_loader import AgentDependencyLoader
|
|
1244
|
-
from ...utils.robust_installer import RobustPackageInstaller
|
|
1245
|
-
|
|
1246
|
-
max_retries = getattr(args, "max_retries", 3)
|
|
1247
|
-
|
|
1248
|
-
print("=" * 70)
|
|
1249
|
-
print("FIXING AGENT DEPENDENCIES WITH RETRY LOGIC")
|
|
1250
|
-
print("=" * 70)
|
|
1251
|
-
print()
|
|
1252
|
-
|
|
1253
|
-
loader = AgentDependencyLoader(auto_install=False)
|
|
1254
|
-
|
|
1255
|
-
# Discover and analyze
|
|
1256
|
-
print("Discovering deployed agents...")
|
|
1257
|
-
loader.discover_deployed_agents()
|
|
1258
|
-
|
|
1259
|
-
if not loader.deployed_agents:
|
|
1260
|
-
print("No deployed agents found")
|
|
1261
|
-
return
|
|
1262
|
-
|
|
1263
|
-
print(f"Found {len(loader.deployed_agents)} deployed agents")
|
|
1264
|
-
print("Analyzing dependencies...")
|
|
1265
|
-
|
|
1266
|
-
loader.load_agent_dependencies()
|
|
1267
|
-
results = loader.analyze_dependencies()
|
|
1268
|
-
|
|
1269
|
-
missing_python = results["summary"]["missing_python"]
|
|
1270
|
-
missing_system = results["summary"]["missing_system"]
|
|
1271
|
-
|
|
1272
|
-
if not missing_python and not missing_system:
|
|
1273
|
-
print("\n✅ All dependencies are already satisfied!")
|
|
1274
|
-
return
|
|
1275
|
-
|
|
1276
|
-
# Show what's missing
|
|
1277
|
-
if missing_python:
|
|
1278
|
-
print(f"\n❌ Missing Python packages: {len(missing_python)}")
|
|
1279
|
-
for pkg in missing_python[:10]:
|
|
1280
|
-
print(f" - {pkg}")
|
|
1281
|
-
if len(missing_python) > 10:
|
|
1282
|
-
print(f" ... and {len(missing_python) - 10} more")
|
|
1283
|
-
|
|
1284
|
-
if missing_system:
|
|
1285
|
-
print(f"\n❌ Missing system commands: {len(missing_system)}")
|
|
1286
|
-
for cmd in missing_system:
|
|
1287
|
-
print(f" - {cmd}")
|
|
1288
|
-
print("\n⚠️ System dependencies must be installed manually:")
|
|
1289
|
-
print(f" macOS: brew install {' '.join(missing_system)}")
|
|
1290
|
-
print(f" Ubuntu: apt-get install {' '.join(missing_system)}")
|
|
1291
|
-
|
|
1292
|
-
# Fix Python dependencies with robust installer
|
|
1293
|
-
if missing_python:
|
|
1294
|
-
print(
|
|
1295
|
-
f"\n🔧 Fixing Python dependencies with {max_retries} retries per package..."
|
|
1296
|
-
)
|
|
1297
|
-
|
|
1298
|
-
# Check compatibility
|
|
1299
|
-
compatible, incompatible = loader.check_python_compatibility(missing_python)
|
|
1300
|
-
|
|
1301
|
-
if incompatible:
|
|
1302
|
-
print(f"\n⚠️ Skipping {len(incompatible)} incompatible packages:")
|
|
1303
|
-
for pkg in incompatible[:5]:
|
|
1304
|
-
print(f" - {pkg}")
|
|
1305
|
-
if len(incompatible) > 5:
|
|
1306
|
-
print(f" ... and {len(incompatible) - 5} more")
|
|
1307
|
-
|
|
1308
|
-
if compatible:
|
|
1309
|
-
installer = RobustPackageInstaller(
|
|
1310
|
-
max_retries=max_retries, retry_delay=2.0, timeout=300
|
|
1311
|
-
)
|
|
1312
|
-
|
|
1313
|
-
print(f"\nInstalling {len(compatible)} compatible packages...")
|
|
1314
|
-
successful, failed, errors = installer.install_packages(compatible)
|
|
1315
|
-
|
|
1316
|
-
print("\n" + "=" * 70)
|
|
1317
|
-
print("INSTALLATION RESULTS:")
|
|
1318
|
-
print("=" * 70)
|
|
1319
|
-
|
|
1320
|
-
if successful:
|
|
1321
|
-
print(f"✅ Successfully installed: {len(successful)} packages")
|
|
1322
|
-
|
|
1323
|
-
if failed:
|
|
1324
|
-
print(f"❌ Failed to install: {len(failed)} packages")
|
|
1325
|
-
for pkg in failed:
|
|
1326
|
-
print(f" - {pkg}: {errors.get(pkg, 'Unknown error')}")
|
|
1327
|
-
|
|
1328
|
-
# Re-check
|
|
1329
|
-
print("\nVerifying installation...")
|
|
1330
|
-
loader.checked_packages.clear()
|
|
1331
|
-
final_results = loader.analyze_dependencies()
|
|
1332
|
-
|
|
1333
|
-
final_missing = final_results["summary"]["missing_python"]
|
|
1334
|
-
if not final_missing:
|
|
1335
|
-
print("✅ All Python dependencies are now satisfied!")
|
|
1336
|
-
else:
|
|
1337
|
-
print(f"⚠️ Still missing {len(final_missing)} packages")
|
|
1338
|
-
print("\nTry running again or install manually:")
|
|
1339
|
-
print(f" pip install {' '.join(final_missing[:3])}")
|
|
1340
|
-
|
|
1341
|
-
print("\n" + "=" * 70)
|
|
1342
|
-
print("DONE")
|
|
1343
|
-
print("=" * 70)
|