claude-mpm 4.21.3__py3-none-any.whl → 5.1.9__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_PM.md +12 -0
- claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +3 -48
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
- claude_mpm/agents/PM_INSTRUCTIONS.md +1239 -674
- claude_mpm/agents/WORKFLOW.md +75 -2
- claude_mpm/agents/__init__.py +6 -0
- claude_mpm/agents/agent_loader.py +1 -4
- claude_mpm/agents/base_agent.json +6 -3
- claude_mpm/agents/base_agent_loader.py +10 -35
- claude_mpm/agents/frontmatter_validator.py +69 -1
- claude_mpm/agents/templates/circuit-breakers.md +1254 -0
- claude_mpm/agents/templates/context-management-examples.md +544 -0
- claude_mpm/agents/templates/{pm_red_flags.md → pm-red-flags.md} +89 -19
- claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
- claude_mpm/agents/templates/research-gate-examples.md +669 -0
- claude_mpm/agents/templates/structured-questions-examples.md +615 -0
- claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
- claude_mpm/agents/templates/ticketing-examples.md +277 -0
- claude_mpm/cli/__init__.py +37 -2
- claude_mpm/cli/commands/__init__.py +2 -0
- claude_mpm/cli/commands/agent_source.py +774 -0
- claude_mpm/cli/commands/agent_state_manager.py +188 -30
- claude_mpm/cli/commands/agents.py +1128 -36
- claude_mpm/cli/commands/agents_cleanup.py +210 -0
- claude_mpm/cli/commands/agents_discover.py +338 -0
- claude_mpm/cli/commands/aggregate.py +1 -1
- claude_mpm/cli/commands/analyze.py +3 -3
- claude_mpm/cli/commands/auto_configure.py +537 -239
- claude_mpm/cli/commands/cleanup.py +1 -1
- claude_mpm/cli/commands/config.py +7 -4
- claude_mpm/cli/commands/configure.py +935 -45
- claude_mpm/cli/commands/configure_agent_display.py +4 -4
- claude_mpm/cli/commands/configure_navigation.py +63 -46
- claude_mpm/cli/commands/debug.py +12 -12
- claude_mpm/cli/commands/doctor.py +10 -2
- claude_mpm/cli/commands/hook_errors.py +277 -0
- claude_mpm/cli/commands/local_deploy.py +1 -4
- claude_mpm/cli/commands/mcp_install_commands.py +1 -1
- claude_mpm/cli/commands/mpm_init/core.py +50 -2
- claude_mpm/cli/commands/mpm_init/git_activity.py +10 -10
- claude_mpm/cli/commands/mpm_init/prompts.py +6 -6
- claude_mpm/cli/commands/postmortem.py +401 -0
- claude_mpm/cli/commands/run.py +125 -167
- claude_mpm/cli/commands/skill_source.py +694 -0
- claude_mpm/cli/commands/skills.py +757 -20
- claude_mpm/cli/executor.py +78 -3
- claude_mpm/cli/interactive/agent_wizard.py +1032 -47
- claude_mpm/cli/parsers/agent_source_parser.py +171 -0
- claude_mpm/cli/parsers/agents_parser.py +310 -4
- claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
- claude_mpm/cli/parsers/base_parser.py +53 -0
- claude_mpm/cli/parsers/config_parser.py +96 -43
- claude_mpm/cli/parsers/skill_source_parser.py +169 -0
- claude_mpm/cli/parsers/skills_parser.py +145 -0
- claude_mpm/cli/parsers/source_parser.py +138 -0
- claude_mpm/cli/startup.py +564 -108
- claude_mpm/cli/startup_display.py +480 -0
- claude_mpm/cli/utils.py +1 -1
- claude_mpm/cli_module/commands.py +1 -1
- claude_mpm/commands/{mpm-auto-configure.md → mpm-agents-auto-configure.md} +9 -0
- claude_mpm/commands/mpm-agents-detect.md +9 -0
- claude_mpm/commands/{mpm-agents.md → mpm-agents-list.md} +9 -0
- claude_mpm/commands/mpm-agents-recommend.md +9 -0
- claude_mpm/commands/{mpm-config.md → mpm-config-view.md} +9 -0
- claude_mpm/commands/mpm-doctor.md +9 -0
- claude_mpm/commands/mpm-help.md +14 -2
- claude_mpm/commands/mpm-init.md +27 -2
- claude_mpm/commands/mpm-monitor.md +9 -0
- claude_mpm/commands/mpm-postmortem.md +123 -0
- claude_mpm/commands/{mpm-resume.md → mpm-session-resume.md} +9 -0
- claude_mpm/commands/mpm-status.md +9 -0
- claude_mpm/commands/{mpm-organize.md → mpm-ticket-organize.md} +9 -0
- claude_mpm/commands/mpm-ticket-view.md +552 -0
- claude_mpm/commands/mpm-version.md +9 -0
- claude_mpm/commands/mpm.md +10 -0
- claude_mpm/config/agent_presets.py +488 -0
- claude_mpm/config/agent_sources.py +325 -0
- claude_mpm/config/skill_presets.py +392 -0
- claude_mpm/config/skill_sources.py +590 -0
- claude_mpm/constants.py +13 -0
- claude_mpm/core/api_validator.py +1 -1
- claude_mpm/core/claude_runner.py +19 -35
- claude_mpm/core/config.py +24 -0
- claude_mpm/core/constants.py +1 -1
- claude_mpm/core/framework/__init__.py +3 -16
- claude_mpm/core/framework/loaders/file_loader.py +54 -101
- claude_mpm/core/framework/loaders/instruction_loader.py +25 -5
- claude_mpm/core/framework/processors/metadata_processor.py +1 -1
- claude_mpm/core/hook_error_memory.py +381 -0
- claude_mpm/core/hook_manager.py +41 -2
- claude_mpm/core/interactive_session.py +131 -10
- claude_mpm/core/logger.py +3 -1
- claude_mpm/core/oneshot_session.py +110 -8
- claude_mpm/core/output_style_manager.py +173 -43
- claude_mpm/core/protocols/__init__.py +23 -0
- claude_mpm/core/protocols/runner_protocol.py +103 -0
- claude_mpm/core/protocols/session_protocol.py +131 -0
- claude_mpm/core/shared/singleton_manager.py +11 -4
- claude_mpm/core/system_context.py +38 -0
- claude_mpm/core/unified_agent_registry.py +129 -1
- claude_mpm/core/unified_config.py +22 -0
- claude_mpm/dashboard/static/css/activity.css +69 -69
- claude_mpm/dashboard/static/css/connection-status.css +10 -10
- claude_mpm/dashboard/static/css/dashboard.css +15 -15
- claude_mpm/dashboard/static/js/components/activity-tree.js +178 -178
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +101 -101
- claude_mpm/dashboard/static/js/components/agent-inference.js +31 -31
- claude_mpm/dashboard/static/js/components/build-tracker.js +59 -59
- claude_mpm/dashboard/static/js/components/code-simple.js +107 -107
- claude_mpm/dashboard/static/js/components/connection-debug.js +101 -101
- claude_mpm/dashboard/static/js/components/diff-viewer.js +113 -113
- claude_mpm/dashboard/static/js/components/event-viewer.js +12 -12
- claude_mpm/dashboard/static/js/components/file-change-tracker.js +57 -57
- claude_mpm/dashboard/static/js/components/file-change-viewer.js +74 -74
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +6 -6
- claude_mpm/dashboard/static/js/components/file-viewer.js +42 -42
- claude_mpm/dashboard/static/js/components/module-viewer.js +27 -27
- claude_mpm/dashboard/static/js/components/session-manager.js +14 -14
- claude_mpm/dashboard/static/js/components/socket-manager.js +1 -1
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +14 -14
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +110 -110
- claude_mpm/dashboard/static/js/components/working-directory.js +8 -8
- claude_mpm/dashboard/static/js/connection-manager.js +76 -76
- claude_mpm/dashboard/static/js/dashboard.js +76 -58
- claude_mpm/dashboard/static/js/extension-error-handler.js +22 -22
- claude_mpm/dashboard/static/js/socket-client.js +138 -121
- claude_mpm/dashboard/templates/code_simple.html +23 -23
- claude_mpm/dashboard/templates/index.html +18 -18
- claude_mpm/experimental/cli_enhancements.py +1 -5
- claude_mpm/hooks/claude_hooks/event_handlers.py +3 -1
- claude_mpm/hooks/claude_hooks/hook_handler.py +24 -7
- claude_mpm/hooks/claude_hooks/installer.py +45 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
- claude_mpm/hooks/failure_learning/__init__.py +2 -8
- claude_mpm/hooks/failure_learning/failure_detection_hook.py +1 -6
- claude_mpm/hooks/failure_learning/fix_detection_hook.py +1 -6
- claude_mpm/hooks/failure_learning/learning_extraction_hook.py +1 -6
- claude_mpm/hooks/kuzu_response_hook.py +1 -5
- claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
- claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
- claude_mpm/models/agent_definition.py +7 -0
- claude_mpm/models/git_repository.py +198 -0
- claude_mpm/scripts/claude-hook-handler.sh +3 -3
- claude_mpm/scripts/start_activity_logging.py +3 -1
- claude_mpm/services/agents/agent_builder.py +45 -9
- claude_mpm/services/agents/agent_preset_service.py +238 -0
- claude_mpm/services/agents/agent_selection_service.py +484 -0
- claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
- claude_mpm/services/agents/cache_git_manager.py +621 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +126 -2
- claude_mpm/services/agents/deployment/agent_discovery_service.py +105 -73
- claude_mpm/services/agents/deployment/agent_format_converter.py +1 -1
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -5
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +3 -3
- claude_mpm/services/agents/deployment/agent_restore_handler.py +1 -4
- claude_mpm/services/agents/deployment/agent_template_builder.py +236 -15
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +101 -15
- claude_mpm/services/agents/deployment/async_agent_deployment.py +2 -1
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -3
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +225 -18
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +2 -2
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +1 -4
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +557 -0
- claude_mpm/services/agents/deployment/single_agent_deployer.py +2 -2
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +168 -46
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +2 -2
- claude_mpm/services/agents/git_source_manager.py +629 -0
- claude_mpm/services/agents/loading/framework_agent_loader.py +9 -12
- claude_mpm/services/agents/local_template_manager.py +50 -10
- claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
- claude_mpm/services/agents/sources/__init__.py +13 -0
- claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
- claude_mpm/services/agents/sources/git_source_sync_service.py +1087 -0
- claude_mpm/services/agents/startup_sync.py +239 -0
- claude_mpm/services/agents/toolchain_detector.py +474 -0
- claude_mpm/services/analysis/__init__.py +25 -0
- claude_mpm/services/analysis/postmortem_reporter.py +474 -0
- claude_mpm/services/analysis/postmortem_service.py +765 -0
- claude_mpm/services/cli/session_pause_manager.py +1 -1
- claude_mpm/services/cli/unified_dashboard_manager.py +1 -1
- claude_mpm/services/command_deployment_service.py +200 -6
- claude_mpm/services/core/base.py +7 -2
- claude_mpm/services/core/interfaces/__init__.py +1 -3
- claude_mpm/services/core/interfaces/health.py +1 -4
- claude_mpm/services/core/models/__init__.py +2 -11
- claude_mpm/services/diagnostics/checks/__init__.py +4 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +0 -2
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
- claude_mpm/services/diagnostics/checks/instructions_check.py +1 -2
- claude_mpm/services/diagnostics/checks/mcp_check.py +0 -1
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
- claude_mpm/services/diagnostics/checks/monitor_check.py +0 -1
- claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +9 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +40 -10
- claude_mpm/services/event_bus/direct_relay.py +3 -3
- claude_mpm/services/event_bus/event_bus.py +36 -3
- claude_mpm/services/events/consumers/logging.py +1 -2
- claude_mpm/services/git/__init__.py +21 -0
- claude_mpm/services/git/git_operations_service.py +494 -0
- claude_mpm/services/github/__init__.py +21 -0
- claude_mpm/services/github/github_cli_service.py +397 -0
- claude_mpm/services/infrastructure/monitoring/__init__.py +1 -5
- claude_mpm/services/infrastructure/monitoring/aggregator.py +1 -6
- claude_mpm/services/infrastructure/monitoring/resources.py +1 -1
- claude_mpm/services/instructions/__init__.py +9 -0
- claude_mpm/services/instructions/instruction_cache_service.py +374 -0
- claude_mpm/services/local_ops/__init__.py +3 -13
- claude_mpm/services/local_ops/health_checks/__init__.py +1 -3
- claude_mpm/services/local_ops/health_manager.py +1 -4
- claude_mpm/services/local_ops/process_manager.py +1 -1
- claude_mpm/services/local_ops/resource_monitor.py +2 -2
- claude_mpm/services/mcp_config_manager.py +75 -145
- claude_mpm/services/mcp_gateway/config/configuration.py +1 -1
- claude_mpm/services/mcp_gateway/core/process_pool.py +22 -16
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +1 -6
- claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -2
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +6 -2
- claude_mpm/services/mcp_service_verifier.py +6 -3
- claude_mpm/services/memory/optimizer.py +1 -1
- claude_mpm/services/model/model_router.py +8 -9
- claude_mpm/services/monitor/daemon.py +29 -9
- claude_mpm/services/monitor/daemon_manager.py +96 -19
- claude_mpm/services/monitor/server.py +2 -2
- claude_mpm/services/native_agent_converter.py +356 -0
- claude_mpm/services/port_manager.py +1 -1
- claude_mpm/services/pr/__init__.py +14 -0
- claude_mpm/services/pr/pr_template_service.py +329 -0
- claude_mpm/services/project/documentation_manager.py +2 -1
- claude_mpm/services/project/project_organizer.py +4 -0
- claude_mpm/services/project/toolchain_analyzer.py +3 -1
- claude_mpm/services/runner_configuration_service.py +17 -3
- claude_mpm/services/self_upgrade_service.py +165 -7
- claude_mpm/services/session_management_service.py +16 -4
- claude_mpm/services/skills/__init__.py +18 -0
- claude_mpm/services/skills/git_skill_source_manager.py +1169 -0
- claude_mpm/services/skills/skill_discovery_service.py +568 -0
- claude_mpm/services/skills_config.py +547 -0
- claude_mpm/services/skills_deployer.py +955 -0
- claude_mpm/services/socketio/handlers/connection.py +1 -1
- claude_mpm/services/socketio/handlers/git.py +2 -2
- claude_mpm/services/socketio/server/core.py +1 -4
- claude_mpm/services/socketio/server/main.py +1 -3
- claude_mpm/services/system_instructions_service.py +1 -3
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +0 -3
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +0 -1
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +1 -1
- claude_mpm/services/unified/deployment_strategies/vercel.py +1 -5
- claude_mpm/services/unified/unified_deployment.py +1 -5
- claude_mpm/services/version_control/conflict_resolution.py +6 -4
- claude_mpm/services/visualization/__init__.py +1 -5
- claude_mpm/services/visualization/mermaid_generator.py +2 -3
- claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
- claude_mpm/skills/bundled/performance-profiling.md +6 -0
- claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +2 -2
- claude_mpm/skills/skills_registry.py +0 -1
- claude_mpm/templates/questions/__init__.py +38 -0
- claude_mpm/templates/questions/base.py +193 -0
- claude_mpm/templates/questions/pr_strategy.py +311 -0
- claude_mpm/templates/questions/project_init.py +385 -0
- claude_mpm/templates/questions/ticket_mgmt.py +394 -0
- claude_mpm/tools/__main__.py +8 -8
- claude_mpm/tools/code_tree_analyzer/analysis.py +1 -1
- claude_mpm/utils/agent_dependency_loader.py +80 -13
- claude_mpm/utils/agent_filters.py +288 -0
- claude_mpm/utils/dependency_cache.py +3 -1
- claude_mpm/utils/gitignore.py +244 -0
- claude_mpm/utils/log_cleanup.py +3 -3
- claude_mpm/utils/migration.py +372 -0
- claude_mpm/utils/progress.py +387 -0
- claude_mpm/utils/robust_installer.py +3 -5
- claude_mpm/utils/structured_questions.py +619 -0
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/METADATA +496 -65
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/RECORD +284 -443
- claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -17
- claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +0 -3
- claude_mpm/agents/templates/agent-manager.json +0 -273
- claude_mpm/agents/templates/agentic-coder-optimizer.json +0 -248
- claude_mpm/agents/templates/api_qa.json +0 -180
- claude_mpm/agents/templates/circuit_breakers.md +0 -638
- claude_mpm/agents/templates/clerk-ops.json +0 -235
- claude_mpm/agents/templates/code_analyzer.json +0 -101
- claude_mpm/agents/templates/content-agent.json +0 -358
- claude_mpm/agents/templates/dart_engineer.json +0 -307
- claude_mpm/agents/templates/data_engineer.json +0 -225
- claude_mpm/agents/templates/documentation.json +0 -211
- claude_mpm/agents/templates/engineer.json +0 -210
- claude_mpm/agents/templates/gcp_ops_agent.json +0 -253
- claude_mpm/agents/templates/golang_engineer.json +0 -270
- claude_mpm/agents/templates/imagemagick.json +0 -264
- claude_mpm/agents/templates/java_engineer.json +0 -346
- claude_mpm/agents/templates/local_ops_agent.json +0 -1840
- claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +0 -39
- claude_mpm/agents/templates/logs/prompts/agent_engineer_20250901_010124_142.md +0 -400
- claude_mpm/agents/templates/memory_manager.json +0 -158
- claude_mpm/agents/templates/nextjs_engineer.json +0 -285
- claude_mpm/agents/templates/ops.json +0 -185
- claude_mpm/agents/templates/php-engineer.json +0 -287
- claude_mpm/agents/templates/product_owner.json +0 -338
- claude_mpm/agents/templates/project_organizer.json +0 -140
- claude_mpm/agents/templates/prompt-engineer.json +0 -737
- claude_mpm/agents/templates/python_engineer.json +0 -387
- claude_mpm/agents/templates/qa.json +0 -242
- claude_mpm/agents/templates/react_engineer.json +0 -238
- claude_mpm/agents/templates/refactoring_engineer.json +0 -276
- claude_mpm/agents/templates/research.json +0 -188
- claude_mpm/agents/templates/ruby-engineer.json +0 -280
- claude_mpm/agents/templates/rust_engineer.json +0 -275
- claude_mpm/agents/templates/security.json +0 -202
- claude_mpm/agents/templates/svelte-engineer.json +0 -225
- claude_mpm/agents/templates/ticketing.json +0 -177
- claude_mpm/agents/templates/typescript_engineer.json +0 -285
- claude_mpm/agents/templates/vercel_ops_agent.json +0 -412
- claude_mpm/agents/templates/version_control.json +0 -157
- claude_mpm/agents/templates/web_qa.json +0 -399
- claude_mpm/agents/templates/web_ui.json +0 -189
- claude_mpm/commands/mpm-tickets.md +0 -102
- claude_mpm/dashboard/.claude-mpm/socketio-instances.json +0 -1
- claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +0 -188
- claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +0 -156
- claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +0 -38
- claude_mpm/dashboard/react/components/shared/FilterBar.module.css +0 -92
- claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +0 -248
- claude_mpm/dashboard/static/archive/activity_dashboard_test.html +0 -61
- claude_mpm/dashboard/static/archive/test_activity_connection.html +0 -179
- claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +0 -68
- claude_mpm/dashboard/static/archive/test_dashboard.html +0 -409
- claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +0 -519
- claude_mpm/dashboard/static/archive/test_dashboard_verification.html +0 -181
- claude_mpm/dashboard/static/archive/test_file_data.html +0 -315
- claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +0 -243
- claude_mpm/dashboard/static/archive/test_file_tree_fix.html +0 -234
- claude_mpm/dashboard/static/archive/test_file_tree_rename.html +0 -117
- claude_mpm/dashboard/static/archive/test_file_tree_tab.html +0 -115
- claude_mpm/dashboard/static/archive/test_file_viewer.html +0 -224
- claude_mpm/dashboard/static/archive/test_final_activity.html +0 -220
- claude_mpm/dashboard/static/archive/test_tab_fix.html +0 -139
- claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +0 -1
- claude_mpm/dashboard/static/built/components/activity-tree.js +0 -2
- claude_mpm/dashboard/static/built/components/agent-hierarchy.js +0 -777
- claude_mpm/dashboard/static/built/components/agent-inference.js +0 -2
- claude_mpm/dashboard/static/built/components/build-tracker.js +0 -333
- claude_mpm/dashboard/static/built/components/code-simple.js +0 -857
- claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/built/components/code-tree.js +0 -2
- claude_mpm/dashboard/static/built/components/code-viewer.js +0 -2
- claude_mpm/dashboard/static/built/components/connection-debug.js +0 -654
- claude_mpm/dashboard/static/built/components/diff-viewer.js +0 -891
- claude_mpm/dashboard/static/built/components/event-processor.js +0 -2
- claude_mpm/dashboard/static/built/components/event-viewer.js +0 -2
- claude_mpm/dashboard/static/built/components/export-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/file-change-tracker.js +0 -443
- claude_mpm/dashboard/static/built/components/file-change-viewer.js +0 -690
- claude_mpm/dashboard/static/built/components/file-tool-tracker.js +0 -2
- claude_mpm/dashboard/static/built/components/file-viewer.js +0 -2
- claude_mpm/dashboard/static/built/components/hud-library-loader.js +0 -2
- claude_mpm/dashboard/static/built/components/hud-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/hud-visualizer.js +0 -2
- claude_mpm/dashboard/static/built/components/module-viewer.js +0 -2
- claude_mpm/dashboard/static/built/components/nav-bar.js +0 -145
- claude_mpm/dashboard/static/built/components/page-structure.js +0 -429
- claude_mpm/dashboard/static/built/components/session-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/socket-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/ui-state-manager.js +0 -2
- claude_mpm/dashboard/static/built/components/unified-data-viewer.js +0 -2
- claude_mpm/dashboard/static/built/components/working-directory.js +0 -2
- claude_mpm/dashboard/static/built/connection-manager.js +0 -536
- claude_mpm/dashboard/static/built/dashboard.js +0 -2
- claude_mpm/dashboard/static/built/extension-error-handler.js +0 -164
- claude_mpm/dashboard/static/built/react/events.js +0 -30
- claude_mpm/dashboard/static/built/shared/dom-helpers.js +0 -396
- claude_mpm/dashboard/static/built/shared/event-bus.js +0 -330
- claude_mpm/dashboard/static/built/shared/event-filter-service.js +0 -540
- claude_mpm/dashboard/static/built/shared/logger.js +0 -385
- claude_mpm/dashboard/static/built/shared/page-structure.js +0 -249
- claude_mpm/dashboard/static/built/shared/tooltip-service.js +0 -253
- claude_mpm/dashboard/static/built/socket-client.js +0 -2
- claude_mpm/dashboard/static/built/tab-isolation-fix.js +0 -185
- claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +0 -1
- claude_mpm/dashboard/static/dist/components/activity-tree.js +0 -2
- claude_mpm/dashboard/static/dist/components/agent-inference.js +0 -2
- claude_mpm/dashboard/static/dist/components/code-tree.js +0 -2
- claude_mpm/dashboard/static/dist/components/code-viewer.js +0 -2
- claude_mpm/dashboard/static/dist/components/event-processor.js +0 -2
- claude_mpm/dashboard/static/dist/components/event-viewer.js +0 -2
- claude_mpm/dashboard/static/dist/components/export-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +0 -2
- claude_mpm/dashboard/static/dist/components/file-viewer.js +0 -2
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +0 -2
- claude_mpm/dashboard/static/dist/components/hud-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +0 -2
- claude_mpm/dashboard/static/dist/components/module-viewer.js +0 -2
- claude_mpm/dashboard/static/dist/components/session-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/socket-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +0 -2
- claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +0 -2
- claude_mpm/dashboard/static/dist/components/working-directory.js +0 -2
- claude_mpm/dashboard/static/dist/dashboard.js +0 -2
- claude_mpm/dashboard/static/dist/react/events.js +0 -30
- claude_mpm/dashboard/static/dist/socket-client.js +0 -2
- claude_mpm/dashboard/static/events.html +0 -607
- claude_mpm/dashboard/static/index.html +0 -635
- claude_mpm/dashboard/static/js/shared/dom-helpers.js +0 -396
- claude_mpm/dashboard/static/js/shared/event-bus.js +0 -330
- claude_mpm/dashboard/static/js/shared/logger.js +0 -385
- claude_mpm/dashboard/static/js/shared/tooltip-service.js +0 -253
- claude_mpm/dashboard/static/js/stores/dashboard-store.js +0 -562
- claude_mpm/dashboard/static/legacy/activity.html +0 -736
- claude_mpm/dashboard/static/legacy/agents.html +0 -786
- claude_mpm/dashboard/static/legacy/files.html +0 -747
- claude_mpm/dashboard/static/legacy/tools.html +0 -831
- claude_mpm/dashboard/static/monitors.html +0 -431
- claude_mpm/dashboard/static/production/events.html +0 -659
- claude_mpm/dashboard/static/production/main.html +0 -698
- claude_mpm/dashboard/static/production/monitors.html +0 -483
- claude_mpm/dashboard/static/test-archive/dashboard.html +0 -635
- claude_mpm/dashboard/static/test-archive/debug-events.html +0 -147
- claude_mpm/dashboard/static/test-archive/test-navigation.html +0 -256
- claude_mpm/dashboard/static/test-archive/test-react-exports.html +0 -180
- claude_mpm/dashboard/static/test-archive/test_debug.html +0 -25
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +0 -79
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +0 -178
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +0 -577
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +0 -467
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +0 -537
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +0 -730
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +0 -112
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +0 -146
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +0 -412
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +0 -81
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +0 -362
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +0 -312
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +0 -152
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +0 -668
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +0 -587
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +0 -438
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +0 -391
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +0 -119
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +0 -148
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +0 -483
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +0 -452
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +0 -449
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +0 -411
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +0 -14
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +0 -58
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +0 -68
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +0 -69
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +0 -131
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +0 -325
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +0 -490
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +0 -425
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +0 -499
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +0 -86
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +0 -43
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +0 -47
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +0 -65
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +0 -30
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +0 -16
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +0 -160
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +0 -412
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +0 -602
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +0 -915
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +0 -916
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +0 -752
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +0 -1237
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +0 -189
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +0 -500
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +0 -464
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +0 -619
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +0 -437
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +0 -231
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +0 -170
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +0 -602
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +0 -821
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +0 -742
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +0 -726
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +0 -764
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +0 -831
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +0 -226
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +0 -901
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +0 -901
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +0 -775
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +0 -937
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +0 -770
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +0 -961
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +0 -119
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +0 -253
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +0 -145
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +0 -543
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +0 -741
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +0 -470
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +0 -458
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +0 -639
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +0 -140
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +0 -572
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +0 -411
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +0 -569
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +0 -695
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +0 -184
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +0 -459
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +0 -479
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +0 -687
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +0 -758
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +0 -868
- /claude_mpm/agents/templates/{git_file_tracking.md → git-file-tracking.md} +0 -0
- /claude_mpm/agents/templates/{pm_examples.md → pm-examples.md} +0 -0
- /claude_mpm/agents/templates/{response_format.md → response-format.md} +0 -0
- /claude_mpm/agents/templates/{validation_templates.md → validation-templates.md} +0 -0
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/WHEEL +0 -0
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.1.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
"""Single-tier agent deployment service using Git sources.
|
|
2
|
+
|
|
3
|
+
This service replaces the multi-tier deployment system with a simpler,
|
|
4
|
+
Git-source-based approach. All agents come from Git repositories with
|
|
5
|
+
priority-based conflict resolution.
|
|
6
|
+
|
|
7
|
+
WHY: The multi-tier system (PROJECT > USER > SYSTEM) added complexity
|
|
8
|
+
without providing clear value. Single-tier with Git sources provides:
|
|
9
|
+
- Simpler mental model (Git repos with priorities)
|
|
10
|
+
- Better version control and collaboration
|
|
11
|
+
- Easier testing and debugging
|
|
12
|
+
- More flexible agent distribution
|
|
13
|
+
|
|
14
|
+
Design Decision: Composition over Inheritance
|
|
15
|
+
|
|
16
|
+
This service composes Phase 1 components (GitSourceManager,
|
|
17
|
+
RemoteAgentDiscoveryService) rather than inheriting. This provides
|
|
18
|
+
better separation of concerns and makes it easier to test each
|
|
19
|
+
component independently.
|
|
20
|
+
|
|
21
|
+
Trade-offs:
|
|
22
|
+
- Flexibility: Easy to swap implementations or mock for testing
|
|
23
|
+
- Complexity: Slightly more code than inheritance
|
|
24
|
+
- Maintainability: Clear boundaries between sync, discovery, and deployment
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
import shutil
|
|
29
|
+
from datetime import datetime, timezone
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Any, Dict, List, Optional
|
|
32
|
+
|
|
33
|
+
from src.claude_mpm.config.agent_sources import AgentSourceConfiguration
|
|
34
|
+
from src.claude_mpm.models.git_repository import GitRepository
|
|
35
|
+
from src.claude_mpm.services.agents.deployment.remote_agent_discovery_service import (
|
|
36
|
+
RemoteAgentDiscoveryService,
|
|
37
|
+
)
|
|
38
|
+
from src.claude_mpm.services.agents.git_source_manager import GitSourceManager
|
|
39
|
+
|
|
40
|
+
logger = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class SingleTierDeploymentService:
|
|
44
|
+
"""Single-tier agent deployment service using Git sources.
|
|
45
|
+
|
|
46
|
+
This service manages agent deployment from Git repositories with
|
|
47
|
+
priority-based conflict resolution. It coordinates:
|
|
48
|
+
- Git repository syncing (via GitSourceManager)
|
|
49
|
+
- Agent discovery from cached repositories
|
|
50
|
+
- Priority-based agent resolution
|
|
51
|
+
- Deployment to .claude/agents/ directory
|
|
52
|
+
|
|
53
|
+
Architecture:
|
|
54
|
+
- Phase 1 GitSourceManager handles repository sync
|
|
55
|
+
- Phase 1 RemoteAgentDiscoveryService discovers agents
|
|
56
|
+
- This service orchestrates deployment workflow
|
|
57
|
+
- Priority system resolves conflicts (lower priority = higher precedence)
|
|
58
|
+
|
|
59
|
+
Example:
|
|
60
|
+
>>> config = AgentSourceConfiguration.load()
|
|
61
|
+
>>> service = SingleTierDeploymentService(
|
|
62
|
+
... config=config,
|
|
63
|
+
... deployment_dir=Path.home() / ".claude" / "agents"
|
|
64
|
+
... )
|
|
65
|
+
>>> result = service.deploy_all_agents(force_sync=True)
|
|
66
|
+
>>> print(f"Deployed {result['deployed_agents']} agents")
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
config: AgentSourceConfiguration,
|
|
72
|
+
deployment_dir: Path,
|
|
73
|
+
cache_root: Optional[Path] = None,
|
|
74
|
+
):
|
|
75
|
+
"""Initialize single-tier deployment service.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
config: Agent source configuration with repositories
|
|
79
|
+
deployment_dir: Target deployment directory (.claude/agents/)
|
|
80
|
+
cache_root: Cache root for repositories
|
|
81
|
+
(defaults to ~/.claude-mpm/cache/remote-agents/)
|
|
82
|
+
"""
|
|
83
|
+
self.config = config
|
|
84
|
+
self.deployment_dir = deployment_dir
|
|
85
|
+
self.deployment_dir.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
|
|
87
|
+
if cache_root is None:
|
|
88
|
+
cache_root = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
|
|
89
|
+
|
|
90
|
+
self.cache_root = cache_root
|
|
91
|
+
self.git_source_manager = GitSourceManager(cache_root)
|
|
92
|
+
|
|
93
|
+
logger.info(
|
|
94
|
+
f"SingleTierDeploymentService initialized: "
|
|
95
|
+
f"deployment={deployment_dir}, cache={cache_root}"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def deploy_all_agents(
|
|
99
|
+
self, force_sync: bool = False, dry_run: bool = False
|
|
100
|
+
) -> Dict[str, Any]:
|
|
101
|
+
"""Deploy all agents from configured Git sources.
|
|
102
|
+
|
|
103
|
+
Workflow:
|
|
104
|
+
1. Get all enabled repositories from configuration
|
|
105
|
+
2. Sync repositories (unless cached and not force_sync)
|
|
106
|
+
3. Discover all agents from all repositories
|
|
107
|
+
4. Resolve conflicts using priority system (lower = higher precedence)
|
|
108
|
+
5. Deploy agents to .claude/agents/
|
|
109
|
+
6. Return deployment report
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
force_sync: Force repository sync even if cache is fresh
|
|
113
|
+
dry_run: Show what would be deployed without actually deploying
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Deployment report dictionary:
|
|
117
|
+
{
|
|
118
|
+
"synced_repos": int, # Repositories successfully synced
|
|
119
|
+
"discovered_agents": int, # Total agents discovered
|
|
120
|
+
"deployed_agents": int, # Agents actually deployed
|
|
121
|
+
"skipped_agents": int, # Agents skipped (dry run or errors)
|
|
122
|
+
"conflicts_resolved": int, # Agent name conflicts resolved
|
|
123
|
+
"timestamp": str, # ISO timestamp
|
|
124
|
+
"agents": [ # Per-agent details
|
|
125
|
+
{
|
|
126
|
+
"name": "engineer",
|
|
127
|
+
"source": "bobmatnyc/claude-mpm-agents",
|
|
128
|
+
"priority": 100,
|
|
129
|
+
"deployed": true
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
Error Handling:
|
|
135
|
+
- Individual repository sync failures don't stop overall deployment
|
|
136
|
+
- Failed deployments are logged and counted as skipped
|
|
137
|
+
- Returns partial success with error details
|
|
138
|
+
"""
|
|
139
|
+
logger.info(
|
|
140
|
+
f"Starting deploy_all_agents (force_sync={force_sync}, dry_run={dry_run})"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Step 1: Get enabled repositories
|
|
144
|
+
repos = self.config.get_enabled_repositories()
|
|
145
|
+
logger.info(f"Found {len(repos)} enabled repositories")
|
|
146
|
+
|
|
147
|
+
if not repos:
|
|
148
|
+
logger.warning("No enabled repositories found")
|
|
149
|
+
return {
|
|
150
|
+
"synced_repos": 0,
|
|
151
|
+
"discovered_agents": 0,
|
|
152
|
+
"deployed_agents": 0,
|
|
153
|
+
"skipped_agents": 0,
|
|
154
|
+
"conflicts_resolved": 0,
|
|
155
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
156
|
+
"agents": [],
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# Step 2: Sync all repositories
|
|
160
|
+
sync_results = self.git_source_manager.sync_all_repositories(repos, force_sync)
|
|
161
|
+
synced_count = sum(1 for r in sync_results.values() if r.get("synced"))
|
|
162
|
+
logger.info(f"Synced {synced_count}/{len(repos)} repositories")
|
|
163
|
+
|
|
164
|
+
# Step 3: Discover all agents from all repositories
|
|
165
|
+
all_agents = []
|
|
166
|
+
for repo in repos:
|
|
167
|
+
try:
|
|
168
|
+
# Discover agents in this repository's cache
|
|
169
|
+
agents = self._discover_agents_in_repo(repo)
|
|
170
|
+
all_agents.extend(agents)
|
|
171
|
+
logger.debug(f"Discovered {len(agents)} agents from {repo.identifier}")
|
|
172
|
+
except Exception as e:
|
|
173
|
+
logger.error(f"Failed to discover agents in {repo.identifier}: {e}")
|
|
174
|
+
|
|
175
|
+
logger.info(f"Discovered {len(all_agents)} total agents")
|
|
176
|
+
|
|
177
|
+
# Step 4: Resolve conflicts (lower priority = higher precedence)
|
|
178
|
+
resolved_agents, conflicts_count = self._resolve_conflicts(all_agents)
|
|
179
|
+
logger.info(
|
|
180
|
+
f"Resolved {conflicts_count} conflicts, {len(resolved_agents)} unique agents"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Step 5: Deploy agents
|
|
184
|
+
deployed_count = 0
|
|
185
|
+
skipped_count = 0
|
|
186
|
+
agent_details = []
|
|
187
|
+
|
|
188
|
+
for agent in resolved_agents:
|
|
189
|
+
agent_name = agent.get("metadata", {}).get("name", "unknown")
|
|
190
|
+
source_repo = agent.get("repository", "unknown")
|
|
191
|
+
priority = agent.get("priority", 100)
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
if dry_run:
|
|
195
|
+
logger.info(
|
|
196
|
+
f"[DRY RUN] Would deploy {agent_name} from {source_repo}"
|
|
197
|
+
)
|
|
198
|
+
agent_details.append(
|
|
199
|
+
{
|
|
200
|
+
"name": agent_name,
|
|
201
|
+
"source": source_repo,
|
|
202
|
+
"priority": priority,
|
|
203
|
+
"deployed": False,
|
|
204
|
+
"dry_run": True,
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
skipped_count += 1
|
|
208
|
+
else:
|
|
209
|
+
# Deploy agent to .claude/agents/
|
|
210
|
+
success = self._deploy_agent_file(agent)
|
|
211
|
+
if success:
|
|
212
|
+
logger.info(f"Deployed {agent_name} from {source_repo}")
|
|
213
|
+
deployed_count += 1
|
|
214
|
+
agent_details.append(
|
|
215
|
+
{
|
|
216
|
+
"name": agent_name,
|
|
217
|
+
"source": source_repo,
|
|
218
|
+
"priority": priority,
|
|
219
|
+
"deployed": True,
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
logger.warning(f"Failed to deploy {agent_name}")
|
|
224
|
+
skipped_count += 1
|
|
225
|
+
agent_details.append(
|
|
226
|
+
{
|
|
227
|
+
"name": agent_name,
|
|
228
|
+
"source": source_repo,
|
|
229
|
+
"priority": priority,
|
|
230
|
+
"deployed": False,
|
|
231
|
+
"error": "deployment_failed",
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
except Exception as e:
|
|
235
|
+
logger.error(f"Error deploying {agent_name}: {e}")
|
|
236
|
+
skipped_count += 1
|
|
237
|
+
agent_details.append(
|
|
238
|
+
{
|
|
239
|
+
"name": agent_name,
|
|
240
|
+
"source": source_repo,
|
|
241
|
+
"priority": priority,
|
|
242
|
+
"deployed": False,
|
|
243
|
+
"error": str(e),
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# Step 6: Build and return report
|
|
248
|
+
report = {
|
|
249
|
+
"synced_repos": synced_count,
|
|
250
|
+
"discovered_agents": len(all_agents),
|
|
251
|
+
"deployed_agents": deployed_count,
|
|
252
|
+
"skipped_agents": skipped_count,
|
|
253
|
+
"conflicts_resolved": conflicts_count,
|
|
254
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
255
|
+
"agents": agent_details,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
logger.info(
|
|
259
|
+
f"Deployment complete: {deployed_count} deployed, "
|
|
260
|
+
f"{skipped_count} skipped, {conflicts_count} conflicts resolved"
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
return report
|
|
264
|
+
|
|
265
|
+
def deploy_agent(
|
|
266
|
+
self, agent_name: str, source_repo: Optional[str] = None, dry_run: bool = False
|
|
267
|
+
) -> Dict[str, Any]:
|
|
268
|
+
"""Deploy a specific agent.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
agent_name: Name of agent to deploy (e.g., "engineer", "research")
|
|
272
|
+
source_repo: Optional specific repository identifier
|
|
273
|
+
(e.g., "owner/repo/subdirectory")
|
|
274
|
+
dry_run: Show what would be deployed without actually deploying
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Deployment result:
|
|
278
|
+
{
|
|
279
|
+
"deployed": bool,
|
|
280
|
+
"agent_name": str,
|
|
281
|
+
"source": str,
|
|
282
|
+
"priority": int,
|
|
283
|
+
"path": str, # Deployment path (if successful)
|
|
284
|
+
"error": str # Error message (if failed)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
Error Handling:
|
|
288
|
+
- Returns deployed=False with error message if agent not found
|
|
289
|
+
- Returns deployed=False with error message if deployment fails
|
|
290
|
+
"""
|
|
291
|
+
logger.info(f"Deploying agent: {agent_name} (source={source_repo})")
|
|
292
|
+
|
|
293
|
+
# Discover agents (filtered by source_repo if specified)
|
|
294
|
+
if source_repo:
|
|
295
|
+
# Find specific repository
|
|
296
|
+
repos = [
|
|
297
|
+
r
|
|
298
|
+
for r in self.config.get_enabled_repositories()
|
|
299
|
+
if r.identifier == source_repo
|
|
300
|
+
]
|
|
301
|
+
if not repos:
|
|
302
|
+
return {
|
|
303
|
+
"deployed": False,
|
|
304
|
+
"agent_name": agent_name,
|
|
305
|
+
"error": f"Source repository not found: {source_repo}",
|
|
306
|
+
}
|
|
307
|
+
else:
|
|
308
|
+
repos = self.config.get_enabled_repositories()
|
|
309
|
+
|
|
310
|
+
# Discover agents from repositories
|
|
311
|
+
all_agents = []
|
|
312
|
+
for repo in repos:
|
|
313
|
+
try:
|
|
314
|
+
agents = self._discover_agents_in_repo(repo)
|
|
315
|
+
all_agents.extend(agents)
|
|
316
|
+
except Exception as e:
|
|
317
|
+
logger.warning(f"Failed to discover agents in {repo.identifier}: {e}")
|
|
318
|
+
|
|
319
|
+
# Find matching agent
|
|
320
|
+
matching_agents = [
|
|
321
|
+
a
|
|
322
|
+
for a in all_agents
|
|
323
|
+
if a.get("metadata", {}).get("name", "").lower().replace(" ", "-")
|
|
324
|
+
== agent_name.lower()
|
|
325
|
+
or a.get("agent_id", "") == agent_name.lower()
|
|
326
|
+
]
|
|
327
|
+
|
|
328
|
+
if not matching_agents:
|
|
329
|
+
return {
|
|
330
|
+
"deployed": False,
|
|
331
|
+
"agent_name": agent_name,
|
|
332
|
+
"error": f"Agent not found: {agent_name}",
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
# Resolve conflicts if multiple sources have same agent
|
|
336
|
+
if len(matching_agents) > 1:
|
|
337
|
+
# Sort by priority (lower = higher precedence)
|
|
338
|
+
matching_agents.sort(key=lambda a: a.get("priority", 100))
|
|
339
|
+
logger.info(
|
|
340
|
+
f"Multiple sources for {agent_name}, using highest priority "
|
|
341
|
+
f"(priority={matching_agents[0].get('priority')})"
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# Deploy the highest priority agent
|
|
345
|
+
agent = matching_agents[0]
|
|
346
|
+
agent_name_display = agent.get("metadata", {}).get("name", agent_name)
|
|
347
|
+
source = agent.get("repository", "unknown")
|
|
348
|
+
priority = agent.get("priority", 100)
|
|
349
|
+
|
|
350
|
+
if dry_run:
|
|
351
|
+
logger.info(f"[DRY RUN] Would deploy {agent_name_display} from {source}")
|
|
352
|
+
return {
|
|
353
|
+
"deployed": False,
|
|
354
|
+
"agent_name": agent_name_display,
|
|
355
|
+
"source": source,
|
|
356
|
+
"priority": priority,
|
|
357
|
+
"dry_run": True,
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
# Deploy agent
|
|
361
|
+
try:
|
|
362
|
+
success = self._deploy_agent_file(agent)
|
|
363
|
+
if success:
|
|
364
|
+
deployed_path = (
|
|
365
|
+
self.deployment_dir
|
|
366
|
+
/ f"{agent.get('agent_id', agent_name.lower())}.md"
|
|
367
|
+
)
|
|
368
|
+
logger.info(f"Deployed {agent_name_display} to {deployed_path}")
|
|
369
|
+
return {
|
|
370
|
+
"deployed": True,
|
|
371
|
+
"agent_name": agent_name_display,
|
|
372
|
+
"source": source,
|
|
373
|
+
"priority": priority,
|
|
374
|
+
"path": str(deployed_path),
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
"deployed": False,
|
|
378
|
+
"agent_name": agent_name_display,
|
|
379
|
+
"source": source,
|
|
380
|
+
"priority": priority,
|
|
381
|
+
"error": "Deployment failed",
|
|
382
|
+
}
|
|
383
|
+
except Exception as e:
|
|
384
|
+
logger.error(f"Error deploying {agent_name_display}: {e}")
|
|
385
|
+
return {
|
|
386
|
+
"deployed": False,
|
|
387
|
+
"agent_name": agent_name_display,
|
|
388
|
+
"source": source,
|
|
389
|
+
"priority": priority,
|
|
390
|
+
"error": str(e),
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
def list_available_agents(
|
|
394
|
+
self, source_repo: Optional[str] = None
|
|
395
|
+
) -> List[Dict[str, Any]]:
|
|
396
|
+
"""List all agents available from configured sources.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
source_repo: Optional repository filter (e.g., "owner/repo/subdirectory")
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
List of agent metadata dictionaries:
|
|
403
|
+
[
|
|
404
|
+
{
|
|
405
|
+
"name": "Engineer",
|
|
406
|
+
"agent_id": "engineer",
|
|
407
|
+
"description": "Python specialist...",
|
|
408
|
+
"source": "bobmatnyc/claude-mpm-agents",
|
|
409
|
+
"priority": 100,
|
|
410
|
+
"version": "abc123...",
|
|
411
|
+
"model": "sonnet"
|
|
412
|
+
}
|
|
413
|
+
]
|
|
414
|
+
"""
|
|
415
|
+
logger.debug(f"Listing available agents (source={source_repo})")
|
|
416
|
+
|
|
417
|
+
# Get repositories
|
|
418
|
+
if source_repo:
|
|
419
|
+
repos = [
|
|
420
|
+
r
|
|
421
|
+
for r in self.config.get_enabled_repositories()
|
|
422
|
+
if r.identifier == source_repo
|
|
423
|
+
]
|
|
424
|
+
else:
|
|
425
|
+
repos = self.config.get_enabled_repositories()
|
|
426
|
+
|
|
427
|
+
# Discover all agents
|
|
428
|
+
all_agents = []
|
|
429
|
+
for repo in repos:
|
|
430
|
+
try:
|
|
431
|
+
agents = self._discover_agents_in_repo(repo)
|
|
432
|
+
all_agents.extend(agents)
|
|
433
|
+
except Exception as e:
|
|
434
|
+
logger.warning(f"Failed to discover agents in {repo.identifier}: {e}")
|
|
435
|
+
|
|
436
|
+
# Format for return
|
|
437
|
+
formatted_agents = []
|
|
438
|
+
for agent in all_agents:
|
|
439
|
+
metadata = agent.get("metadata", {})
|
|
440
|
+
formatted_agents.append(
|
|
441
|
+
{
|
|
442
|
+
"name": metadata.get("name", "Unknown"),
|
|
443
|
+
"agent_id": agent.get("agent_id", "unknown"),
|
|
444
|
+
"description": metadata.get("description", ""),
|
|
445
|
+
"source": agent.get("repository", "unknown"),
|
|
446
|
+
"priority": agent.get("priority", 100),
|
|
447
|
+
"version": agent.get("version", "unknown"),
|
|
448
|
+
"model": agent.get("model", "sonnet"),
|
|
449
|
+
}
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
logger.debug(f"Found {len(formatted_agents)} available agents")
|
|
453
|
+
return formatted_agents
|
|
454
|
+
|
|
455
|
+
def get_deployed_agents(self) -> List[Dict[str, Any]]:
|
|
456
|
+
"""List currently deployed agents.
|
|
457
|
+
|
|
458
|
+
Scans .claude/agents/ directory for deployed agent files.
|
|
459
|
+
|
|
460
|
+
Returns:
|
|
461
|
+
List of deployed agent metadata:
|
|
462
|
+
[
|
|
463
|
+
{
|
|
464
|
+
"name": "Engineer",
|
|
465
|
+
"agent_id": "engineer",
|
|
466
|
+
"path": "/Users/user/.claude/agents/engineer.md",
|
|
467
|
+
"size": 12345
|
|
468
|
+
}
|
|
469
|
+
]
|
|
470
|
+
"""
|
|
471
|
+
logger.debug(f"Listing deployed agents from {self.deployment_dir}")
|
|
472
|
+
|
|
473
|
+
if not self.deployment_dir.exists():
|
|
474
|
+
return []
|
|
475
|
+
|
|
476
|
+
deployed = []
|
|
477
|
+
for md_file in self.deployment_dir.glob("*.md"):
|
|
478
|
+
try:
|
|
479
|
+
# Extract agent_id from filename
|
|
480
|
+
agent_id = md_file.stem
|
|
481
|
+
|
|
482
|
+
# Read first line to get agent name
|
|
483
|
+
with open(md_file) as f:
|
|
484
|
+
first_line = f.readline().strip()
|
|
485
|
+
# Extract name from markdown heading
|
|
486
|
+
name = (
|
|
487
|
+
first_line.lstrip("#").strip()
|
|
488
|
+
if first_line.startswith("#")
|
|
489
|
+
else agent_id
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
deployed.append(
|
|
493
|
+
{
|
|
494
|
+
"name": name,
|
|
495
|
+
"agent_id": agent_id,
|
|
496
|
+
"path": str(md_file),
|
|
497
|
+
"size": md_file.stat().st_size,
|
|
498
|
+
}
|
|
499
|
+
)
|
|
500
|
+
except Exception as e:
|
|
501
|
+
logger.warning(f"Failed to read deployed agent {md_file.name}: {e}")
|
|
502
|
+
|
|
503
|
+
logger.debug(f"Found {len(deployed)} deployed agents")
|
|
504
|
+
return deployed
|
|
505
|
+
|
|
506
|
+
def remove_agent(self, agent_name: str) -> bool:
|
|
507
|
+
"""Remove a deployed agent.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
agent_name: Agent ID to remove (e.g., "engineer")
|
|
511
|
+
|
|
512
|
+
Returns:
|
|
513
|
+
True if agent was removed, False if not found
|
|
514
|
+
"""
|
|
515
|
+
logger.info(f"Removing agent: {agent_name}")
|
|
516
|
+
|
|
517
|
+
# Find deployed agent file
|
|
518
|
+
agent_file = self.deployment_dir / f"{agent_name.lower()}.md"
|
|
519
|
+
|
|
520
|
+
if not agent_file.exists():
|
|
521
|
+
logger.warning(f"Agent not found: {agent_name}")
|
|
522
|
+
return False
|
|
523
|
+
|
|
524
|
+
try:
|
|
525
|
+
agent_file.unlink()
|
|
526
|
+
logger.info(f"Removed agent: {agent_name}")
|
|
527
|
+
return True
|
|
528
|
+
except Exception as e:
|
|
529
|
+
logger.error(f"Failed to remove agent {agent_name}: {e}")
|
|
530
|
+
return False
|
|
531
|
+
|
|
532
|
+
def sync_sources(
|
|
533
|
+
self, force: bool = False, repo_identifier: Optional[str] = None
|
|
534
|
+
) -> Dict[str, Any]:
|
|
535
|
+
"""Sync Git sources.
|
|
536
|
+
|
|
537
|
+
Wrapper for GitSourceManager.sync_all_repositories() with filtering.
|
|
538
|
+
|
|
539
|
+
Args:
|
|
540
|
+
force: Force sync even if cache is fresh (bypasses ETag)
|
|
541
|
+
repo_identifier: Optional repository identifier to sync
|
|
542
|
+
(e.g., "owner/repo/subdirectory")
|
|
543
|
+
|
|
544
|
+
Returns:
|
|
545
|
+
Sync results dictionary:
|
|
546
|
+
{
|
|
547
|
+
"owner/repo": {
|
|
548
|
+
"synced": bool,
|
|
549
|
+
"files_updated": int,
|
|
550
|
+
...
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
"""
|
|
554
|
+
logger.info(f"Syncing sources (force={force}, repo={repo_identifier})")
|
|
555
|
+
|
|
556
|
+
repos = self.config.get_enabled_repositories()
|
|
557
|
+
|
|
558
|
+
if repo_identifier:
|
|
559
|
+
repos = [r for r in repos if r.identifier == repo_identifier]
|
|
560
|
+
if not repos:
|
|
561
|
+
return {
|
|
562
|
+
repo_identifier: {
|
|
563
|
+
"synced": False,
|
|
564
|
+
"error": "Repository not found",
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return self.git_source_manager.sync_all_repositories(repos, force)
|
|
569
|
+
|
|
570
|
+
def _discover_agents_in_repo(self, repo: GitRepository) -> List[Dict[str, Any]]:
|
|
571
|
+
"""Discover agents in a specific repository's cache.
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
repo: Repository to discover agents from
|
|
575
|
+
|
|
576
|
+
Returns:
|
|
577
|
+
List of agent dictionaries with repository and priority metadata
|
|
578
|
+
"""
|
|
579
|
+
try:
|
|
580
|
+
discovery_service = RemoteAgentDiscoveryService(repo.cache_path)
|
|
581
|
+
agents = discovery_service.discover_remote_agents()
|
|
582
|
+
|
|
583
|
+
# Add repository identifier and priority to each agent
|
|
584
|
+
for agent in agents:
|
|
585
|
+
agent["repository"] = repo.identifier
|
|
586
|
+
agent["priority"] = repo.priority
|
|
587
|
+
|
|
588
|
+
return agents
|
|
589
|
+
|
|
590
|
+
except Exception as e:
|
|
591
|
+
logger.error(f"Failed to discover agents in {repo.identifier}: {e}")
|
|
592
|
+
return []
|
|
593
|
+
|
|
594
|
+
def _resolve_conflicts(
|
|
595
|
+
self, agents: List[Dict[str, Any]]
|
|
596
|
+
) -> tuple[List[Dict[str, Any]], int]:
|
|
597
|
+
"""Resolve agent name conflicts using priority system.
|
|
598
|
+
|
|
599
|
+
When multiple repositories provide the same agent, choose the one
|
|
600
|
+
with the lowest priority number (highest precedence).
|
|
601
|
+
|
|
602
|
+
Args:
|
|
603
|
+
agents: List of all discovered agents from all repositories
|
|
604
|
+
|
|
605
|
+
Returns:
|
|
606
|
+
Tuple of (resolved_agents, conflict_count):
|
|
607
|
+
- resolved_agents: List with one agent per name (highest priority)
|
|
608
|
+
- conflict_count: Number of conflicts resolved
|
|
609
|
+
|
|
610
|
+
Algorithm:
|
|
611
|
+
1. Group agents by name
|
|
612
|
+
2. For each group, sort by priority (ascending)
|
|
613
|
+
3. Take the first agent (lowest priority number = highest precedence)
|
|
614
|
+
4. Count groups with multiple agents as conflicts
|
|
615
|
+
"""
|
|
616
|
+
# Group agents by name
|
|
617
|
+
agents_by_name: Dict[str, List[Dict[str, Any]]] = {}
|
|
618
|
+
|
|
619
|
+
for agent in agents:
|
|
620
|
+
name = agent.get("metadata", {}).get("name", "").lower().replace(" ", "-")
|
|
621
|
+
if not name:
|
|
622
|
+
# Skip agents without valid names
|
|
623
|
+
continue
|
|
624
|
+
|
|
625
|
+
if name not in agents_by_name:
|
|
626
|
+
agents_by_name[name] = []
|
|
627
|
+
|
|
628
|
+
agents_by_name[name].append(agent)
|
|
629
|
+
|
|
630
|
+
# Resolve conflicts
|
|
631
|
+
resolved = []
|
|
632
|
+
conflict_count = 0
|
|
633
|
+
|
|
634
|
+
for name, agent_list in agents_by_name.items():
|
|
635
|
+
if len(agent_list) > 1:
|
|
636
|
+
# Conflict detected
|
|
637
|
+
conflict_count += 1
|
|
638
|
+
|
|
639
|
+
# Sort by priority (ascending - lower number = higher precedence)
|
|
640
|
+
agent_list.sort(key=lambda a: a.get("priority", 100))
|
|
641
|
+
|
|
642
|
+
logger.info(
|
|
643
|
+
f"Conflict for '{name}': {len(agent_list)} sources, "
|
|
644
|
+
f"using {agent_list[0].get('repository')} "
|
|
645
|
+
f"(priority {agent_list[0].get('priority')})"
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
# Use highest priority agent (first after sort)
|
|
649
|
+
resolved.append(agent_list[0])
|
|
650
|
+
|
|
651
|
+
return resolved, conflict_count
|
|
652
|
+
|
|
653
|
+
def _deploy_agent_file(self, agent: Dict[str, Any]) -> bool:
|
|
654
|
+
"""Deploy a single agent file to deployment directory.
|
|
655
|
+
|
|
656
|
+
Copies the agent Markdown file from cache to .claude/agents/
|
|
657
|
+
|
|
658
|
+
Args:
|
|
659
|
+
agent: Agent dictionary with source_file path
|
|
660
|
+
|
|
661
|
+
Returns:
|
|
662
|
+
True if deployment succeeded, False otherwise
|
|
663
|
+
|
|
664
|
+
Error Handling:
|
|
665
|
+
- Returns False if source file doesn't exist
|
|
666
|
+
- Returns False if copy operation fails
|
|
667
|
+
- Logs all errors for debugging
|
|
668
|
+
"""
|
|
669
|
+
try:
|
|
670
|
+
# Get source file path
|
|
671
|
+
source_file = Path(agent.get("source_file", ""))
|
|
672
|
+
if not source_file.exists():
|
|
673
|
+
logger.error(f"Source file does not exist: {source_file}")
|
|
674
|
+
return False
|
|
675
|
+
|
|
676
|
+
# Build target filename using agent_id
|
|
677
|
+
agent_id = agent.get("agent_id", "unknown")
|
|
678
|
+
target_file = self.deployment_dir / f"{agent_id}.md"
|
|
679
|
+
|
|
680
|
+
# Copy file
|
|
681
|
+
shutil.copy2(source_file, target_file)
|
|
682
|
+
|
|
683
|
+
logger.debug(f"Deployed {source_file.name} to {target_file}")
|
|
684
|
+
return True
|
|
685
|
+
|
|
686
|
+
except Exception as e:
|
|
687
|
+
logger.error(f"Failed to deploy agent file: {e}")
|
|
688
|
+
return False
|
|
689
|
+
|
|
690
|
+
def __repr__(self) -> str:
|
|
691
|
+
"""Return string representation."""
|
|
692
|
+
return (
|
|
693
|
+
f"SingleTierDeploymentService("
|
|
694
|
+
f"deployment_dir='{self.deployment_dir}', "
|
|
695
|
+
f"repositories={len(self.config.get_enabled_repositories())})"
|
|
696
|
+
)
|