claude-mpm 4.21.3__py3-none-any.whl → 5.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_PM.md +12 -0
- claude_mpm/agents/OUTPUT_STYLE.md +3 -48
- claude_mpm/agents/PM_INSTRUCTIONS.md +632 -334
- 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/frontmatter_validator.py +1 -1
- claude_mpm/agents/templates/{circuit_breakers.md → circuit-breakers.md} +370 -3
- 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 +38 -2
- claude_mpm/cli/commands/agent_source.py +774 -0
- claude_mpm/cli/commands/agent_state_manager.py +125 -20
- claude_mpm/cli/commands/agents.py +684 -13
- 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 +2 -6
- claude_mpm/cli/commands/cleanup.py +1 -1
- claude_mpm/cli/commands/config.py +7 -4
- claude_mpm/cli/commands/configure.py +478 -44
- 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/run.py +124 -128
- claude_mpm/cli/commands/skill_source.py +694 -0
- claude_mpm/cli/commands/skills.py +435 -1
- claude_mpm/cli/executor.py +78 -3
- claude_mpm/cli/interactive/agent_wizard.py +919 -41
- claude_mpm/cli/parsers/agent_source_parser.py +171 -0
- claude_mpm/cli/parsers/agents_parser.py +173 -4
- claude_mpm/cli/parsers/base_parser.py +49 -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 +138 -0
- claude_mpm/cli/parsers/source_parser.py +138 -0
- claude_mpm/cli/startup.py +499 -84
- 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 +11 -2
- claude_mpm/commands/mpm-init.md +27 -2
- claude_mpm/commands/mpm-monitor.md +9 -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 +258 -0
- claude_mpm/config/agent_sources.py +325 -0
- claude_mpm/config/skill_sources.py +590 -0
- claude_mpm/constants.py +12 -0
- claude_mpm/core/api_validator.py +1 -1
- claude_mpm/core/claude_runner.py +17 -10
- 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/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 +112 -5
- claude_mpm/core/logger.py +3 -1
- claude_mpm/core/oneshot_session.py +94 -4
- 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/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/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/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 +115 -15
- 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 +363 -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 +1055 -0
- claude_mpm/services/agents/startup_sync.py +239 -0
- claude_mpm/services/agents/toolchain_detector.py +474 -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 +92 -1
- 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/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_gateway/config/configuration.py +1 -1
- 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/memory/optimizer.py +1 -1
- claude_mpm/services/model/model_router.py +8 -9
- claude_mpm/services/monitor/daemon.py +1 -1
- 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/toolchain_analyzer.py +3 -1
- claude_mpm/services/runner_configuration_service.py +1 -0
- claude_mpm/services/self_upgrade_service.py +165 -7
- 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/dependency_cache.py +3 -1
- claude_mpm/utils/gitignore.py +241 -0
- claude_mpm/utils/log_cleanup.py +3 -3
- claude_mpm/utils/progress.py +383 -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.0.2.dist-info}/METADATA +429 -59
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.0.2.dist-info}/RECORD +252 -425
- 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/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.0.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.0.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.0.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.21.3.dist-info → claude_mpm-5.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
"""Git source manager for multi-repository agent sync and discovery."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from claude_mpm.models.git_repository import GitRepository
|
|
9
|
+
from claude_mpm.services.agents.deployment.remote_agent_discovery_service import (
|
|
10
|
+
RemoteAgentDiscoveryService,
|
|
11
|
+
)
|
|
12
|
+
from claude_mpm.services.agents.sources.git_source_sync_service import (
|
|
13
|
+
GitSourceSyncService,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class GitSourceManager:
|
|
20
|
+
"""Manages Git repository sources for agents.
|
|
21
|
+
|
|
22
|
+
This service coordinates syncing and discovery across multiple Git repositories.
|
|
23
|
+
It handles:
|
|
24
|
+
- Multi-repository sync with priority resolution
|
|
25
|
+
- ETag-based incremental updates
|
|
26
|
+
- Agent discovery from cached repositories
|
|
27
|
+
- Priority-based agent resolution (lower priority = higher precedence)
|
|
28
|
+
|
|
29
|
+
Design Decision: Composition over inheritance
|
|
30
|
+
|
|
31
|
+
Rationale: GitSourceManager composes GitSourceSyncService and
|
|
32
|
+
RemoteAgentDiscoveryService rather than inheriting. This provides
|
|
33
|
+
better separation of concerns and makes it easier to test each
|
|
34
|
+
component independently.
|
|
35
|
+
|
|
36
|
+
Trade-offs:
|
|
37
|
+
- Flexibility: Easy to swap implementations or mock for testing
|
|
38
|
+
- Complexity: Slightly more code than inheritance
|
|
39
|
+
- Maintainability: Clear boundaries between sync and discovery
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> manager = GitSourceManager()
|
|
43
|
+
>>> repo = GitRepository(url="https://github.com/owner/repo")
|
|
44
|
+
>>> result = manager.sync_repository(repo)
|
|
45
|
+
>>> agents = manager.list_cached_agents()
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, cache_root: Optional[Path] = None):
|
|
49
|
+
"""Initialize Git source manager.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
cache_root: Root directory for repository caches.
|
|
53
|
+
Defaults to ~/.claude-mpm/cache/remote-agents/
|
|
54
|
+
"""
|
|
55
|
+
if cache_root is None:
|
|
56
|
+
cache_root = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
|
|
57
|
+
|
|
58
|
+
self.cache_root = cache_root
|
|
59
|
+
self.cache_root.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
|
|
61
|
+
# Bug #2 fix: Store repository metadata for source attribution
|
|
62
|
+
# Maps repo_identifier -> {priority, url, ...}
|
|
63
|
+
self._repo_metadata: Dict[str, Dict[str, Any]] = {}
|
|
64
|
+
|
|
65
|
+
logger.info(f"GitSourceManager initialized with cache: {self.cache_root}")
|
|
66
|
+
|
|
67
|
+
def sync_repository(
|
|
68
|
+
self, repo: GitRepository, force: bool = False, show_progress: bool = True
|
|
69
|
+
) -> Dict[str, Any]:
|
|
70
|
+
"""
|
|
71
|
+
Sync a single repository from Git.
|
|
72
|
+
|
|
73
|
+
This method:
|
|
74
|
+
1. Creates a GitSourceSyncService for the repository
|
|
75
|
+
2. Syncs agents using ETag-based caching (unless force=True)
|
|
76
|
+
3. Discovers agents in the cached directory
|
|
77
|
+
4. Returns sync results with metadata
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
repo: GitRepository to sync
|
|
81
|
+
force: Force sync even if cache is fresh (bypasses ETag)
|
|
82
|
+
show_progress: Show ASCII progress bar during sync (default: True)
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Dictionary with sync results:
|
|
86
|
+
{
|
|
87
|
+
"synced": bool, # Overall success
|
|
88
|
+
"etag": str, # HTTP ETag from sync
|
|
89
|
+
"files_updated": int, # Files downloaded
|
|
90
|
+
"files_added": int, # New files
|
|
91
|
+
"files_removed": int, # Deleted files
|
|
92
|
+
"files_cached": int, # Cache hits (304)
|
|
93
|
+
"agents_discovered": List[str], # Agent names found
|
|
94
|
+
"timestamp": str, # ISO timestamp
|
|
95
|
+
"error": str # Error message (if failed)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
>>> repo = GitRepository(url="https://github.com/owner/repo")
|
|
100
|
+
>>> result = manager.sync_repository(repo)
|
|
101
|
+
>>> if result["synced"]:
|
|
102
|
+
... print(f"Synced {result['files_updated']} files")
|
|
103
|
+
"""
|
|
104
|
+
logger.info(f"Syncing repository: {repo.identifier}")
|
|
105
|
+
|
|
106
|
+
# Bug #2 fix: Store repository metadata for later source attribution
|
|
107
|
+
self._repo_metadata[repo.identifier] = {
|
|
108
|
+
"priority": repo.priority,
|
|
109
|
+
"url": repo.url,
|
|
110
|
+
"subdirectory": repo.subdirectory,
|
|
111
|
+
"enabled": repo.enabled,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
# Build source URL for raw GitHub content
|
|
116
|
+
# Format: https://raw.githubusercontent.com/owner/repo/main/subdirectory
|
|
117
|
+
owner, repo_name = repo._parse_github_url(repo.url)
|
|
118
|
+
branch = "main" # TODO: Make configurable
|
|
119
|
+
|
|
120
|
+
if repo.subdirectory:
|
|
121
|
+
subdirectory = repo.subdirectory.strip("/")
|
|
122
|
+
source_url = f"https://raw.githubusercontent.com/{owner}/{repo_name}/{branch}/{subdirectory}"
|
|
123
|
+
else:
|
|
124
|
+
source_url = (
|
|
125
|
+
f"https://raw.githubusercontent.com/{owner}/{repo_name}/{branch}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Initialize sync service
|
|
129
|
+
sync_service = GitSourceSyncService(
|
|
130
|
+
source_url=source_url,
|
|
131
|
+
cache_dir=repo.cache_path,
|
|
132
|
+
source_id=repo.identifier,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Sync agents with progress bar
|
|
136
|
+
sync_results = sync_service.sync_agents(
|
|
137
|
+
force_refresh=force, show_progress=show_progress
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Discover agents in cache
|
|
141
|
+
discovery_service = RemoteAgentDiscoveryService(repo.cache_path)
|
|
142
|
+
discovered_agents = discovery_service.discover_remote_agents()
|
|
143
|
+
|
|
144
|
+
# Build result
|
|
145
|
+
result = {
|
|
146
|
+
"synced": True,
|
|
147
|
+
"files_updated": sync_results.get("total_downloaded", 0),
|
|
148
|
+
"files_added": len(sync_results.get("synced", [])),
|
|
149
|
+
"files_removed": 0, # TODO: Track deletions
|
|
150
|
+
"files_cached": sync_results.get("cache_hits", 0),
|
|
151
|
+
"agents_discovered": [
|
|
152
|
+
agent.get("metadata", {}).get(
|
|
153
|
+
"name", agent.get("agent_id", "unknown")
|
|
154
|
+
)
|
|
155
|
+
for agent in discovered_agents
|
|
156
|
+
],
|
|
157
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
logger.info(
|
|
161
|
+
f"Sync complete: {result['files_updated']} updated, "
|
|
162
|
+
f"{result['files_cached']} cached, "
|
|
163
|
+
f"{len(result['agents_discovered'])} agents"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return result
|
|
167
|
+
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.error(f"Failed to sync {repo.identifier}: {e}")
|
|
170
|
+
return {
|
|
171
|
+
"synced": False,
|
|
172
|
+
"error": str(e),
|
|
173
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
def sync_all_repositories(
|
|
177
|
+
self,
|
|
178
|
+
repos: List[GitRepository],
|
|
179
|
+
force: bool = False,
|
|
180
|
+
show_progress: bool = True,
|
|
181
|
+
) -> Dict[str, Dict[str, Any]]:
|
|
182
|
+
"""Sync multiple repositories.
|
|
183
|
+
|
|
184
|
+
Syncs repositories in priority order (lower priority first).
|
|
185
|
+
Individual failures don't stop overall sync.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
repos: List of repositories to sync
|
|
189
|
+
force: Force sync even if cache is fresh
|
|
190
|
+
show_progress: Show ASCII progress bar during sync (default: True)
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Dictionary mapping repository identifier to sync results:
|
|
194
|
+
{
|
|
195
|
+
"owner/repo/subdir": {...},
|
|
196
|
+
"owner/repo2": {...}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
>>> repos = [
|
|
201
|
+
... GitRepository(url="https://github.com/owner/repo1", priority=100),
|
|
202
|
+
... GitRepository(url="https://github.com/owner/repo2", priority=50)
|
|
203
|
+
... ]
|
|
204
|
+
>>> results = manager.sync_all_repositories(repos)
|
|
205
|
+
>>> for repo_id, result in results.items():
|
|
206
|
+
... print(f"{repo_id}: {'✓' if result['synced'] else '✗'}")
|
|
207
|
+
"""
|
|
208
|
+
logger.info(f"Syncing {len(repos)} repositories")
|
|
209
|
+
|
|
210
|
+
# Sort by priority (lower = higher precedence)
|
|
211
|
+
sorted_repos = sorted(repos, key=lambda r: r.priority)
|
|
212
|
+
|
|
213
|
+
results = {}
|
|
214
|
+
|
|
215
|
+
for repo in sorted_repos:
|
|
216
|
+
# Skip disabled repositories
|
|
217
|
+
if not repo.enabled:
|
|
218
|
+
logger.debug(f"Skipping disabled repository: {repo.identifier}")
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
result = self.sync_repository(
|
|
223
|
+
repo, force=force, show_progress=show_progress
|
|
224
|
+
)
|
|
225
|
+
results[repo.identifier] = result
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.error(f"Exception syncing {repo.identifier}: {e}")
|
|
229
|
+
results[repo.identifier] = {"synced": False, "error": str(e)}
|
|
230
|
+
|
|
231
|
+
logger.info(
|
|
232
|
+
f"Sync complete: {sum(1 for r in results.values() if r.get('synced'))} "
|
|
233
|
+
f"succeeded, {sum(1 for r in results.values() if not r.get('synced'))} failed"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
return results
|
|
237
|
+
|
|
238
|
+
def _ensure_metadata_loaded(self) -> None:
|
|
239
|
+
"""Load repository metadata from configuration if not already loaded.
|
|
240
|
+
|
|
241
|
+
Bug #2 fix: Ensures repository metadata (priority, URL) is available
|
|
242
|
+
for source attribution even when list_cached_agents() is called
|
|
243
|
+
without prior sync_repository() calls.
|
|
244
|
+
|
|
245
|
+
Attempts to load from AgentSourceConfiguration and populates
|
|
246
|
+
_repo_metadata dictionary with priority and URL for each repository.
|
|
247
|
+
"""
|
|
248
|
+
# Skip if metadata already populated
|
|
249
|
+
if self._repo_metadata:
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
from ...config.agent_sources import AgentSourceConfiguration
|
|
254
|
+
|
|
255
|
+
config = AgentSourceConfiguration()
|
|
256
|
+
sources = config.list_sources()
|
|
257
|
+
|
|
258
|
+
for source in sources:
|
|
259
|
+
# Source dict has: identifier, url, subdirectory, enabled, priority
|
|
260
|
+
self._repo_metadata[source["identifier"]] = {
|
|
261
|
+
"priority": source.get("priority", 100),
|
|
262
|
+
"url": source.get("url", ""),
|
|
263
|
+
"subdirectory": source.get("subdirectory"),
|
|
264
|
+
"enabled": source.get("enabled", True),
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
logger.debug(f"Loaded metadata for {len(self._repo_metadata)} repositories")
|
|
268
|
+
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logger.warning(f"Failed to load repository metadata from config: {e}")
|
|
271
|
+
# Continue with empty metadata - will use defaults in _discover_agents_in_directory
|
|
272
|
+
|
|
273
|
+
def list_cached_agents(
|
|
274
|
+
self, repo_identifier: Optional[str] = None
|
|
275
|
+
) -> List[Dict[str, Any]]:
|
|
276
|
+
"""List all cached agents, optionally filtered by repository.
|
|
277
|
+
|
|
278
|
+
Scans cache directories for agent markdown files and returns
|
|
279
|
+
metadata for each discovered agent with source attribution.
|
|
280
|
+
|
|
281
|
+
Bug #2 fix: Enriches agents with repository metadata (priority, URL)
|
|
282
|
+
from stored configuration or attempts to load from config file.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
repo_identifier: Optional repository filter (e.g., "owner/repo/agents")
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
List of agent metadata dictionaries:
|
|
289
|
+
[
|
|
290
|
+
{
|
|
291
|
+
"name": "engineer",
|
|
292
|
+
"version": "2.5.0",
|
|
293
|
+
"path": "/cache/owner/repo/agents/engineer.md",
|
|
294
|
+
"repository": "owner/repo/agents",
|
|
295
|
+
"source": {
|
|
296
|
+
"identifier": "owner/repo/agents",
|
|
297
|
+
"priority": 100,
|
|
298
|
+
"url": "https://github.com/owner/repo"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
]
|
|
302
|
+
|
|
303
|
+
Example:
|
|
304
|
+
>>> agents = manager.list_cached_agents()
|
|
305
|
+
>>> for agent in agents:
|
|
306
|
+
... print(f"{agent['name']} v{agent['version']} from {agent['repository']}")
|
|
307
|
+
"""
|
|
308
|
+
logger.debug(
|
|
309
|
+
f"[DEBUG] list_cached_agents START: repo_identifier={repo_identifier}"
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Bug #2 fix: Load metadata from config if not already loaded
|
|
313
|
+
logger.debug("[DEBUG] Calling _ensure_metadata_loaded()")
|
|
314
|
+
self._ensure_metadata_loaded()
|
|
315
|
+
logger.debug("[DEBUG] Metadata loaded successfully")
|
|
316
|
+
|
|
317
|
+
agents = []
|
|
318
|
+
|
|
319
|
+
# If repo_identifier specified, only scan that repository
|
|
320
|
+
if repo_identifier:
|
|
321
|
+
# Parse identifier to find cache path
|
|
322
|
+
parts = repo_identifier.split("/")
|
|
323
|
+
if len(parts) >= 2:
|
|
324
|
+
cache_path = self.cache_root / "/".join(parts)
|
|
325
|
+
|
|
326
|
+
if cache_path.exists():
|
|
327
|
+
metadata = self._repo_metadata.get(repo_identifier, {})
|
|
328
|
+
agents.extend(
|
|
329
|
+
self._discover_agents_in_directory(
|
|
330
|
+
cache_path, repo_identifier, metadata
|
|
331
|
+
)
|
|
332
|
+
)
|
|
333
|
+
else:
|
|
334
|
+
# Scan all cached repositories
|
|
335
|
+
logger.debug("[DEBUG] Scanning all cached repositories")
|
|
336
|
+
if not self.cache_root.exists():
|
|
337
|
+
logger.debug(f"[DEBUG] Cache root doesn't exist: {self.cache_root}")
|
|
338
|
+
return []
|
|
339
|
+
|
|
340
|
+
# Walk cache directory structure
|
|
341
|
+
logger.debug(f"[DEBUG] Walking cache root: {self.cache_root}")
|
|
342
|
+
for owner_dir in self.cache_root.iterdir():
|
|
343
|
+
if not owner_dir.is_dir():
|
|
344
|
+
continue
|
|
345
|
+
logger.debug(f"[DEBUG] Processing owner_dir: {owner_dir.name}")
|
|
346
|
+
|
|
347
|
+
for repo_dir in owner_dir.iterdir():
|
|
348
|
+
if not repo_dir.is_dir():
|
|
349
|
+
continue
|
|
350
|
+
logger.debug(f"[DEBUG] Processing repo_dir: {repo_dir.name}")
|
|
351
|
+
|
|
352
|
+
# Bug #5 fix: Don't iterate subdirectories - RemoteAgentDiscoveryService
|
|
353
|
+
# now handles the /agents/ subdirectory internally (Bug #4 fix).
|
|
354
|
+
# Iterating subdirectories caused it to treat /agents/ hierarchy as
|
|
355
|
+
# separate repositories (engineer/backend, ops/tooling, etc.)
|
|
356
|
+
repo_id = f"{owner_dir.name}/{repo_dir.name}"
|
|
357
|
+
metadata = self._repo_metadata.get(repo_id, {})
|
|
358
|
+
logger.debug(f"[DEBUG] Discovering agents in repo root: {repo_id}")
|
|
359
|
+
agents.extend(
|
|
360
|
+
self._discover_agents_in_directory(repo_dir, repo_id, metadata)
|
|
361
|
+
)
|
|
362
|
+
logger.debug(f"[DEBUG] Found {len(agents)} agents so far")
|
|
363
|
+
|
|
364
|
+
logger.debug(f"[DEBUG] list_cached_agents COMPLETE: {len(agents)} total agents")
|
|
365
|
+
return agents
|
|
366
|
+
|
|
367
|
+
def _discover_agents_in_directory(
|
|
368
|
+
self,
|
|
369
|
+
directory: Path,
|
|
370
|
+
repo_identifier: str,
|
|
371
|
+
repo_metadata: Optional[Dict[str, Any]] = None,
|
|
372
|
+
) -> List[Dict[str, Any]]:
|
|
373
|
+
"""Discover agents in a specific directory with source attribution.
|
|
374
|
+
|
|
375
|
+
Bug #2 fix: Enriches agent metadata with source information including
|
|
376
|
+
repository identifier, priority, and URL for proper attribution.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
directory: Directory to scan
|
|
380
|
+
repo_identifier: Repository identifier for metadata
|
|
381
|
+
repo_metadata: Optional repository metadata (priority, URL, etc.)
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
List of agent metadata dictionaries with source attribution
|
|
385
|
+
"""
|
|
386
|
+
try:
|
|
387
|
+
discovery_service = RemoteAgentDiscoveryService(directory)
|
|
388
|
+
discovered = discovery_service.discover_remote_agents()
|
|
389
|
+
|
|
390
|
+
# Default metadata if not provided
|
|
391
|
+
if repo_metadata is None:
|
|
392
|
+
repo_metadata = {}
|
|
393
|
+
|
|
394
|
+
# Bug #2 fix: Enrich each agent with proper source attribution
|
|
395
|
+
for agent in discovered:
|
|
396
|
+
agent["repository"] = repo_identifier
|
|
397
|
+
|
|
398
|
+
# Add source attribution at root level for CLI compatibility
|
|
399
|
+
# source: repository identifier string
|
|
400
|
+
# priority: numeric priority from config
|
|
401
|
+
# source_url: full GitHub URL for reference
|
|
402
|
+
if "source" not in agent or agent["source"] == "remote":
|
|
403
|
+
agent["source"] = repo_identifier
|
|
404
|
+
agent["priority"] = repo_metadata.get("priority", 100)
|
|
405
|
+
agent["source_url"] = repo_metadata.get("url", "")
|
|
406
|
+
|
|
407
|
+
return discovered
|
|
408
|
+
|
|
409
|
+
except Exception as e:
|
|
410
|
+
logger.warning(f"Failed to discover agents in {directory}: {e}")
|
|
411
|
+
return []
|
|
412
|
+
|
|
413
|
+
def get_agent_path(
|
|
414
|
+
self, agent_name: str, repo_identifier: Optional[str] = None
|
|
415
|
+
) -> Optional[Path]:
|
|
416
|
+
"""Get cached path for a specific agent.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
agent_name: Agent name (without .md extension)
|
|
420
|
+
repo_identifier: Optional repository filter
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
Path to cached agent file, or None if not found
|
|
424
|
+
|
|
425
|
+
Example:
|
|
426
|
+
>>> path = manager.get_agent_path("engineer")
|
|
427
|
+
>>> if path:
|
|
428
|
+
... print(f"Found: {path}")
|
|
429
|
+
"""
|
|
430
|
+
agents = self.list_cached_agents(repo_identifier)
|
|
431
|
+
|
|
432
|
+
for agent in agents:
|
|
433
|
+
# Agent dict has metadata.name or agent_id
|
|
434
|
+
name = agent.get("metadata", {}).get("name", "")
|
|
435
|
+
agent_id = agent.get("agent_id", "")
|
|
436
|
+
|
|
437
|
+
if name.lower().replace(" ", "-") == agent_name or agent_id == agent_name:
|
|
438
|
+
return Path(agent.get("source_file", ""))
|
|
439
|
+
|
|
440
|
+
return None
|
|
441
|
+
|
|
442
|
+
def list_cached_agents_with_filters(
|
|
443
|
+
self,
|
|
444
|
+
repo_identifier: Optional[str] = None,
|
|
445
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
446
|
+
) -> List[Dict[str, Any]]:
|
|
447
|
+
"""List cached agents with optional filters.
|
|
448
|
+
|
|
449
|
+
This method extends list_cached_agents() by adding semantic filtering
|
|
450
|
+
based on AUTO-DEPLOY-INDEX.md categories. Filters are applied using
|
|
451
|
+
the AutoDeployIndexParser to match agents by category, language,
|
|
452
|
+
framework, platform, or specialization.
|
|
453
|
+
|
|
454
|
+
Design Decision: Filter at application layer, not database layer
|
|
455
|
+
|
|
456
|
+
Rationale: Agent discovery is file-based (no database), so filtering
|
|
457
|
+
happens in-memory after discovery. For the expected dataset size
|
|
458
|
+
(100-1000 agents), this performs well (~10-50ms).
|
|
459
|
+
|
|
460
|
+
Trade-offs:
|
|
461
|
+
- Simplicity: No query optimization needed, straightforward logic
|
|
462
|
+
- Performance: Fast enough for CLI use case (< 100ms)
|
|
463
|
+
- Scalability: May need optimization for 10K+ agents
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
repo_identifier: Filter by specific repository
|
|
467
|
+
filters: Dict with optional keys:
|
|
468
|
+
- category: str (e.g., "engineer/backend", "qa")
|
|
469
|
+
- language: str (e.g., "python", "javascript")
|
|
470
|
+
- framework: str (e.g., "react", "nextjs")
|
|
471
|
+
- platform: str (e.g., "vercel", "gcp")
|
|
472
|
+
- specialization: str (e.g., "data", "security")
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
List of agent definitions with metadata:
|
|
476
|
+
[
|
|
477
|
+
{
|
|
478
|
+
"agent_id": "engineer/backend/python-engineer",
|
|
479
|
+
"name": "Python Engineer",
|
|
480
|
+
"version": "1.2.0",
|
|
481
|
+
"description": "...",
|
|
482
|
+
"source": "bobmatnyc/claude-mpm-agents",
|
|
483
|
+
"priority": 100,
|
|
484
|
+
"category": "engineer/backend",
|
|
485
|
+
"metadata": {...}
|
|
486
|
+
}
|
|
487
|
+
]
|
|
488
|
+
|
|
489
|
+
Example:
|
|
490
|
+
>>> # Filter by category
|
|
491
|
+
>>> agents = manager.list_cached_agents_with_filters(
|
|
492
|
+
... filters={"category": "engineer/backend"}
|
|
493
|
+
... )
|
|
494
|
+
>>> for agent in agents:
|
|
495
|
+
... print(f"{agent['agent_id']} from {agent['source']}")
|
|
496
|
+
|
|
497
|
+
>>> # Filter by language
|
|
498
|
+
>>> agents = manager.list_cached_agents_with_filters(
|
|
499
|
+
... filters={"language": "python"}
|
|
500
|
+
... )
|
|
501
|
+
|
|
502
|
+
>>> # Multiple filters
|
|
503
|
+
>>> agents = manager.list_cached_agents_with_filters(
|
|
504
|
+
... filters={"category": "engineer/frontend", "framework": "react"}
|
|
505
|
+
... )
|
|
506
|
+
"""
|
|
507
|
+
# Get all cached agents
|
|
508
|
+
all_agents = self.list_cached_agents(repo_identifier)
|
|
509
|
+
|
|
510
|
+
# If no filters, return all agents
|
|
511
|
+
if not filters:
|
|
512
|
+
return all_agents
|
|
513
|
+
|
|
514
|
+
# Load AUTO-DEPLOY-INDEX.md parser
|
|
515
|
+
from .auto_deploy_index_parser import AutoDeployIndexParser
|
|
516
|
+
|
|
517
|
+
# Find AUTO-DEPLOY-INDEX.md in bobmatnyc/claude-mpm-agents cache
|
|
518
|
+
index_path = (
|
|
519
|
+
self.cache_root / "bobmatnyc" / "claude-mpm-agents" / "AUTO-DEPLOY-INDEX.md"
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
if not index_path.exists():
|
|
523
|
+
logger.warning(f"AUTO-DEPLOY-INDEX.md not found at: {index_path}")
|
|
524
|
+
logger.warning("Filtering by category/language/framework unavailable")
|
|
525
|
+
# Return all agents if index not found
|
|
526
|
+
return all_agents
|
|
527
|
+
|
|
528
|
+
parser = AutoDeployIndexParser(index_path)
|
|
529
|
+
|
|
530
|
+
# Build set of matching agent IDs based on filters
|
|
531
|
+
matching_agent_ids = set()
|
|
532
|
+
|
|
533
|
+
# Filter by category
|
|
534
|
+
if "category" in filters:
|
|
535
|
+
category = filters["category"]
|
|
536
|
+
category_agents = parser.get_agents_by_category(category)
|
|
537
|
+
matching_agent_ids.update(category_agents)
|
|
538
|
+
logger.debug(
|
|
539
|
+
f"Category '{category}': {len(category_agents)} matching agents"
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
# Filter by language
|
|
543
|
+
if "language" in filters:
|
|
544
|
+
language = filters["language"]
|
|
545
|
+
lang_agents = parser.get_agents_by_language(language)
|
|
546
|
+
lang_agent_ids = lang_agents.get("core", []) + lang_agents.get(
|
|
547
|
+
"optional", []
|
|
548
|
+
)
|
|
549
|
+
if matching_agent_ids:
|
|
550
|
+
# Intersection with previous filters
|
|
551
|
+
matching_agent_ids &= set(lang_agent_ids)
|
|
552
|
+
else:
|
|
553
|
+
matching_agent_ids.update(lang_agent_ids)
|
|
554
|
+
logger.debug(
|
|
555
|
+
f"Language '{language}': {len(lang_agent_ids)} matching agents"
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
# Filter by framework
|
|
559
|
+
if "framework" in filters:
|
|
560
|
+
framework = filters["framework"]
|
|
561
|
+
framework_agents = parser.get_agents_by_framework(framework)
|
|
562
|
+
if matching_agent_ids:
|
|
563
|
+
# Intersection with previous filters
|
|
564
|
+
matching_agent_ids &= set(framework_agents)
|
|
565
|
+
else:
|
|
566
|
+
matching_agent_ids.update(framework_agents)
|
|
567
|
+
logger.debug(
|
|
568
|
+
f"Framework '{framework}': {len(framework_agents)} matching agents"
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
# Filter by platform
|
|
572
|
+
if "platform" in filters:
|
|
573
|
+
platform = filters["platform"]
|
|
574
|
+
platform_agents = parser.get_agents_by_platform(platform)
|
|
575
|
+
if matching_agent_ids:
|
|
576
|
+
# Intersection with previous filters
|
|
577
|
+
matching_agent_ids &= set(platform_agents)
|
|
578
|
+
else:
|
|
579
|
+
matching_agent_ids.update(platform_agents)
|
|
580
|
+
logger.debug(
|
|
581
|
+
f"Platform '{platform}': {len(platform_agents)} matching agents"
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
# Filter by specialization
|
|
585
|
+
if "specialization" in filters:
|
|
586
|
+
specialization = filters["specialization"]
|
|
587
|
+
spec_agents = parser.get_agents_by_specialization(specialization)
|
|
588
|
+
if matching_agent_ids:
|
|
589
|
+
# Intersection with previous filters
|
|
590
|
+
matching_agent_ids &= set(spec_agents)
|
|
591
|
+
else:
|
|
592
|
+
matching_agent_ids.update(spec_agents)
|
|
593
|
+
logger.debug(
|
|
594
|
+
f"Specialization '{specialization}': {len(spec_agents)} matching agents"
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
# Filter all_agents to only include matching IDs
|
|
598
|
+
filtered_agents = []
|
|
599
|
+
|
|
600
|
+
for agent in all_agents:
|
|
601
|
+
# Extract agent_id from metadata or infer from path
|
|
602
|
+
agent_id = agent.get("agent_id")
|
|
603
|
+
|
|
604
|
+
if not agent_id:
|
|
605
|
+
# Try to infer from metadata.name or source_file
|
|
606
|
+
name = agent.get("metadata", {}).get("name", "")
|
|
607
|
+
if name:
|
|
608
|
+
agent_id = name.lower().replace(" ", "-")
|
|
609
|
+
|
|
610
|
+
# Check if agent matches filter
|
|
611
|
+
if agent_id in matching_agent_ids:
|
|
612
|
+
# Add source attribution
|
|
613
|
+
agent["source"] = agent.get("repository", "unknown")
|
|
614
|
+
|
|
615
|
+
# Add category if not present
|
|
616
|
+
if "category" not in agent and "/" in agent_id:
|
|
617
|
+
agent["category"] = agent_id.rsplit("/", 1)[0]
|
|
618
|
+
|
|
619
|
+
filtered_agents.append(agent)
|
|
620
|
+
|
|
621
|
+
logger.info(
|
|
622
|
+
f"Filtered {len(filtered_agents)} agents from {len(all_agents)} total"
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
return filtered_agents
|
|
626
|
+
|
|
627
|
+
def __repr__(self) -> str:
|
|
628
|
+
"""Return string representation."""
|
|
629
|
+
return f"GitSourceManager(cache_root='{self.cache_root}')"
|
|
@@ -15,10 +15,7 @@ markdown-based agent profiles alongside JSON-based templates.
|
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from typing import Any, Dict, Optional
|
|
17
17
|
|
|
18
|
-
from claude_mpm.agents.agent_loader import
|
|
19
|
-
AgentTier,
|
|
20
|
-
list_agents_by_tier,
|
|
21
|
-
)
|
|
18
|
+
from claude_mpm.agents.agent_loader import AgentTier, list_agents_by_tier
|
|
22
19
|
from claude_mpm.core.logging_utils import get_logger
|
|
23
20
|
from claude_mpm.core.unified_paths import get_path_manager
|
|
24
21
|
|
|
@@ -404,8 +401,8 @@ class FrameworkAgentLoader:
|
|
|
404
401
|
**{agent_type} Agent Profile Loaded**
|
|
405
402
|
|
|
406
403
|
**Agent Identity**: {agent_type} Agent
|
|
407
|
-
**Profile Source**: {profile.get(
|
|
408
|
-
**Primary Role**: {profile.get(
|
|
404
|
+
**Profile Source**: {profile.get("source_path", "Unknown")}
|
|
405
|
+
**Primary Role**: {profile.get("role", "Not specified")}
|
|
409
406
|
|
|
410
407
|
**Core Capabilities**:
|
|
411
408
|
"""
|
|
@@ -415,9 +412,9 @@ class FrameworkAgentLoader:
|
|
|
415
412
|
|
|
416
413
|
instruction += f"""
|
|
417
414
|
**Context Preferences**:
|
|
418
|
-
- **Include**: {profile.get(
|
|
419
|
-
- **Exclude**: {profile.get(
|
|
420
|
-
- **Focus**: {profile.get(
|
|
415
|
+
- **Include**: {profile.get("context_preferences", {}).get("include", "Not specified")}
|
|
416
|
+
- **Exclude**: {profile.get("context_preferences", {}).get("exclude", "Not specified")}
|
|
417
|
+
- **Focus**: {profile.get("context_preferences", {}).get("focus", "Not specified")}
|
|
421
418
|
|
|
422
419
|
**Authority Scope**:
|
|
423
420
|
"""
|
|
@@ -426,9 +423,9 @@ class FrameworkAgentLoader:
|
|
|
426
423
|
instruction += f"- **{authority}**: Authorized operation area\n"
|
|
427
424
|
|
|
428
425
|
instruction += f"""
|
|
429
|
-
**Quality Standards**: {len(profile.get(
|
|
430
|
-
**Escalation Triggers**: {len(profile.get(
|
|
431
|
-
**Integration Partners**: {len(profile.get(
|
|
426
|
+
**Quality Standards**: {len(profile.get("quality_standards", []))} standards defined
|
|
427
|
+
**Escalation Triggers**: {len(profile.get("escalation_criteria", []))} criteria defined
|
|
428
|
+
**Integration Partners**: {len(profile.get("integration_patterns", {}))} agent coordination patterns
|
|
432
429
|
|
|
433
430
|
Please operate according to your profile specifications and maintain quality standards.
|
|
434
431
|
"""
|