claude-mpm 4.7.4__py3-none-any.whl → 4.18.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_AGENT_TEMPLATE.md +118 -0
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +106 -1
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +397 -459
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/agent-manager.json +4 -1
- claude_mpm/agents/templates/agentic-coder-optimizer.json +13 -3
- claude_mpm/agents/templates/api_qa.json +11 -2
- claude_mpm/agents/templates/circuit_breakers.md +638 -0
- claude_mpm/agents/templates/clerk-ops.json +12 -2
- claude_mpm/agents/templates/code_analyzer.json +8 -2
- claude_mpm/agents/templates/content-agent.json +358 -0
- claude_mpm/agents/templates/dart_engineer.json +15 -2
- claude_mpm/agents/templates/data_engineer.json +15 -2
- claude_mpm/agents/templates/documentation.json +10 -2
- claude_mpm/agents/templates/engineer.json +21 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +12 -2
- claude_mpm/agents/templates/git_file_tracking.md +584 -0
- claude_mpm/agents/templates/golang_engineer.json +270 -0
- claude_mpm/agents/templates/imagemagick.json +4 -1
- claude_mpm/agents/templates/java_engineer.json +346 -0
- claude_mpm/agents/templates/local_ops_agent.json +1227 -6
- claude_mpm/agents/templates/memory_manager.json +4 -1
- claude_mpm/agents/templates/nextjs_engineer.json +141 -133
- claude_mpm/agents/templates/ops.json +12 -2
- claude_mpm/agents/templates/php-engineer.json +270 -174
- claude_mpm/agents/templates/pm_examples.md +474 -0
- claude_mpm/agents/templates/pm_red_flags.md +240 -0
- claude_mpm/agents/templates/product_owner.json +338 -0
- claude_mpm/agents/templates/project_organizer.json +14 -4
- claude_mpm/agents/templates/prompt-engineer.json +13 -2
- claude_mpm/agents/templates/python_engineer.json +174 -81
- claude_mpm/agents/templates/qa.json +11 -2
- claude_mpm/agents/templates/react_engineer.json +16 -3
- claude_mpm/agents/templates/refactoring_engineer.json +12 -2
- claude_mpm/agents/templates/research.json +34 -21
- claude_mpm/agents/templates/response_format.md +583 -0
- claude_mpm/agents/templates/ruby-engineer.json +129 -192
- claude_mpm/agents/templates/rust_engineer.json +270 -0
- claude_mpm/agents/templates/security.json +10 -2
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +10 -2
- claude_mpm/agents/templates/typescript_engineer.json +116 -125
- claude_mpm/agents/templates/validation_templates.md +312 -0
- claude_mpm/agents/templates/vercel_ops_agent.json +12 -2
- claude_mpm/agents/templates/version_control.json +12 -2
- claude_mpm/agents/templates/web_qa.json +11 -2
- claude_mpm/agents/templates/web_ui.json +15 -2
- claude_mpm/cli/__init__.py +34 -614
- claude_mpm/cli/commands/agent_manager.py +25 -12
- claude_mpm/cli/commands/agent_state_manager.py +186 -0
- claude_mpm/cli/commands/agents.py +235 -148
- claude_mpm/cli/commands/agents_detect.py +380 -0
- claude_mpm/cli/commands/agents_recommend.py +309 -0
- claude_mpm/cli/commands/aggregate.py +7 -3
- claude_mpm/cli/commands/analyze.py +9 -4
- claude_mpm/cli/commands/analyze_code.py +7 -2
- claude_mpm/cli/commands/auto_configure.py +570 -0
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +419 -1571
- claude_mpm/cli/commands/configure_agent_display.py +261 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +167 -0
- claude_mpm/cli/commands/configure_paths.py +104 -0
- claude_mpm/cli/commands/configure_persistence.py +254 -0
- claude_mpm/cli/commands/configure_startup_manager.py +646 -0
- claude_mpm/cli/commands/configure_template_editor.py +497 -0
- claude_mpm/cli/commands/configure_validators.py +73 -0
- claude_mpm/cli/commands/local_deploy.py +537 -0
- claude_mpm/cli/commands/memory.py +54 -20
- claude_mpm/cli/commands/mpm_init.py +585 -196
- claude_mpm/cli/commands/mpm_init_handler.py +37 -3
- claude_mpm/cli/commands/search.py +170 -4
- claude_mpm/cli/commands/upgrade.py +152 -0
- claude_mpm/cli/executor.py +202 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +3 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/agents_parser.py +9 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
- claude_mpm/cli/parsers/base_parser.py +110 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +65 -5
- claude_mpm/cli/shared/output_formatters.py +28 -19
- claude_mpm/cli/startup.py +481 -0
- claude_mpm/cli/utils.py +52 -1
- claude_mpm/commands/mpm-agents-detect.md +168 -0
- claude_mpm/commands/mpm-agents-recommend.md +214 -0
- claude_mpm/commands/mpm-agents.md +75 -1
- claude_mpm/commands/mpm-auto-configure.md +217 -0
- claude_mpm/commands/mpm-help.md +163 -0
- claude_mpm/commands/mpm-init.md +148 -3
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +1 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/constants.py +1 -0
- claude_mpm/core/base_service.py +13 -12
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/instruction_reinforcement_hook.py +2 -1
- claude_mpm/core/interactive_session.py +9 -3
- claude_mpm/core/log_manager.py +2 -0
- claude_mpm/core/logging_config.py +6 -2
- claude_mpm/core/oneshot_session.py +8 -4
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/core/output_style_manager.py +12 -192
- claude_mpm/core/service_registry.py +5 -1
- claude_mpm/core/types.py +2 -9
- claude_mpm/core/typing_utils.py +7 -6
- claude_mpm/dashboard/static/js/dashboard.js +0 -14
- claude_mpm/dashboard/templates/index.html +3 -41
- claude_mpm/hooks/__init__.py +20 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +4 -2
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +23 -2
- claude_mpm/hooks/failure_learning/__init__.py +60 -0
- claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
- claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
- claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
- claude_mpm/hooks/kuzu_memory_hook.py +37 -12
- claude_mpm/hooks/kuzu_response_hook.py +183 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/__init__.py +18 -5
- claude_mpm/services/agents/auto_config_manager.py +796 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
- claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
- claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
- claude_mpm/services/agents/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +568 -0
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/__init__.py +33 -1
- claude_mpm/services/core/interfaces/__init__.py +90 -3
- claude_mpm/services/core/interfaces/agent.py +184 -0
- claude_mpm/services/core/interfaces/health.py +172 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/project.py +121 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/memory_manager.py +11 -24
- claude_mpm/services/core/models/__init__.py +79 -0
- claude_mpm/services/core/models/agent_config.py +381 -0
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +235 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/models/toolchain.py +306 -0
- claude_mpm/services/core/path_resolver.py +23 -7
- claude_mpm/services/diagnostics/__init__.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
- claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
- claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
- claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
- claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
- claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
- claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +38 -33
- claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
- claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
- claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
- claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
- claude_mpm/services/diagnostics/models.py +19 -24
- claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
- claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
- claude_mpm/services/infrastructure/monitoring/base.py +5 -13
- claude_mpm/services/infrastructure/monitoring/network.py +7 -6
- claude_mpm/services/infrastructure/monitoring/process.py +13 -12
- claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
- claude_mpm/services/infrastructure/monitoring/service.py +16 -15
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +163 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
- claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
- claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
- claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
- claude_mpm/services/local_ops/health_manager.py +430 -0
- claude_mpm/services/local_ops/log_monitor.py +396 -0
- claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
- claude_mpm/services/local_ops/process_manager.py +595 -0
- claude_mpm/services/local_ops/resource_monitor.py +331 -0
- claude_mpm/services/local_ops/restart_manager.py +401 -0
- claude_mpm/services/local_ops/restart_policy.py +387 -0
- claude_mpm/services/local_ops/state_manager.py +372 -0
- claude_mpm/services/local_ops/unified_manager.py +600 -0
- claude_mpm/services/mcp_config_manager.py +9 -4
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +18 -31
- claude_mpm/services/mcp_gateway/main.py +30 -0
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +206 -32
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +25 -5
- claude_mpm/services/mcp_service_verifier.py +1 -1
- claude_mpm/services/memory/failure_tracker.py +563 -0
- claude_mpm/services/memory_hook_service.py +165 -4
- claude_mpm/services/model/__init__.py +147 -0
- claude_mpm/services/model/base_provider.py +365 -0
- claude_mpm/services/model/claude_provider.py +412 -0
- claude_mpm/services/model/model_router.py +453 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/daemon_manager.py +3 -2
- claude_mpm/services/monitor/handlers/dashboard.py +2 -1
- claude_mpm/services/monitor/handlers/hooks.py +2 -1
- claude_mpm/services/monitor/management/lifecycle.py +3 -2
- claude_mpm/services/monitor/server.py +2 -1
- claude_mpm/services/project/__init__.py +23 -0
- claude_mpm/services/project/detection_strategies.py +719 -0
- claude_mpm/services/project/toolchain_analyzer.py +581 -0
- claude_mpm/services/self_upgrade_service.py +342 -0
- claude_mpm/services/session_management_service.py +3 -2
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/shared/async_service_base.py +16 -27
- claude_mpm/services/shared/lifecycle_service_base.py +1 -14
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/hook.py +13 -2
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/server/main.py +10 -8
- claude_mpm/services/subprocess_launcher_service.py +14 -5
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
- claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
- claude_mpm/services/unified/deployment_strategies/local.py +6 -5
- claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
- claude_mpm/services/unified/interfaces.py +3 -1
- claude_mpm/services/unified/unified_analyzer.py +14 -10
- claude_mpm/services/unified/unified_config.py +2 -1
- claude_mpm/services/unified/unified_deployment.py +9 -4
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +310 -0
- claude_mpm/storage/state_storage.py +15 -15
- claude_mpm/tools/code_tree_analyzer.py +177 -141
- claude_mpm/tools/code_tree_events.py +4 -2
- claude_mpm/utils/agent_dependency_loader.py +40 -20
- claude_mpm/utils/display_helper.py +260 -0
- claude_mpm/utils/git_analyzer.py +407 -0
- claude_mpm/utils/robust_installer.py +73 -19
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +129 -12
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +295 -193
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/index-hub-backup.html +0 -713
- claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
- claude_mpm/services/project/analyzer_refactored.py +0 -450
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-Configuration Manager Service for Claude MPM Framework
|
|
3
|
+
===========================================================
|
|
4
|
+
|
|
5
|
+
WHY: Orchestrates the complete auto-configuration workflow from toolchain
|
|
6
|
+
analysis through agent deployment. Provides a single entry point for automated
|
|
7
|
+
project configuration with safety checks and rollback capabilities.
|
|
8
|
+
|
|
9
|
+
DESIGN DECISION: Implements the Facade pattern to simplify complex interactions
|
|
10
|
+
between ToolchainAnalyzer, AgentRecommender, and AgentDeployment services.
|
|
11
|
+
Uses Observer pattern for progress tracking and supports dry-run mode for
|
|
12
|
+
safe previewing.
|
|
13
|
+
|
|
14
|
+
Part of TSK-0054: Auto-Configuration Feature - Phase 4
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import time
|
|
18
|
+
import traceback
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any, Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
import yaml
|
|
24
|
+
|
|
25
|
+
from ...core.base_service import BaseService
|
|
26
|
+
from ...core.enums import OperationResult, ValidationSeverity
|
|
27
|
+
from ..core.interfaces.agent import IAgentRegistry, IAutoConfigManager
|
|
28
|
+
from ..core.models.agent_config import (
|
|
29
|
+
AgentRecommendation,
|
|
30
|
+
ConfigurationPreview,
|
|
31
|
+
ConfigurationResult,
|
|
32
|
+
ValidationIssue,
|
|
33
|
+
ValidationResult,
|
|
34
|
+
)
|
|
35
|
+
from ..core.models.toolchain import ToolchainAnalysis
|
|
36
|
+
from .observers import IDeploymentObserver, NullObserver
|
|
37
|
+
from .recommender import AgentRecommenderService
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AutoConfigManagerService(BaseService, IAutoConfigManager):
|
|
41
|
+
"""
|
|
42
|
+
Service for automated agent configuration and deployment.
|
|
43
|
+
|
|
44
|
+
This service orchestrates:
|
|
45
|
+
1. Toolchain analysis to understand project technology stack
|
|
46
|
+
2. Agent recommendations based on detected toolchain
|
|
47
|
+
3. Configuration validation with comprehensive checks
|
|
48
|
+
4. User confirmation (optional) before deployment
|
|
49
|
+
5. Agent deployment with progress tracking
|
|
50
|
+
6. Rollback on failure to maintain consistency
|
|
51
|
+
7. Configuration persistence for future reference
|
|
52
|
+
|
|
53
|
+
Safety Features:
|
|
54
|
+
- Minimum confidence threshold (default 0.8)
|
|
55
|
+
- Dry-run mode for preview without changes
|
|
56
|
+
- Validation gates to block invalid configurations
|
|
57
|
+
- Rollback capability for failed deployments
|
|
58
|
+
- User confirmation for destructive operations
|
|
59
|
+
|
|
60
|
+
Performance:
|
|
61
|
+
- Complete workflow: <30 seconds for typical projects
|
|
62
|
+
- Validation: <1 second
|
|
63
|
+
- Preview generation: <5 seconds
|
|
64
|
+
- Uses caching where applicable
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
toolchain_analyzer: Optional[Any] = None,
|
|
70
|
+
agent_recommender: Optional[AgentRecommenderService] = None,
|
|
71
|
+
agent_registry: Optional[IAgentRegistry] = None,
|
|
72
|
+
agent_deployment: Optional[Any] = None,
|
|
73
|
+
config: Optional[Dict[str, Any]] = None,
|
|
74
|
+
container: Optional[Any] = None,
|
|
75
|
+
):
|
|
76
|
+
"""
|
|
77
|
+
Initialize the Auto-Configuration Manager Service.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
toolchain_analyzer: Service for analyzing project toolchains
|
|
81
|
+
agent_recommender: Service for recommending agents
|
|
82
|
+
agent_registry: Service for agent discovery and metadata
|
|
83
|
+
agent_deployment: Service for deploying agents
|
|
84
|
+
config: Optional configuration dictionary
|
|
85
|
+
container: Optional service container for dependency injection
|
|
86
|
+
"""
|
|
87
|
+
super().__init__(
|
|
88
|
+
name="AutoConfigManagerService",
|
|
89
|
+
config=config,
|
|
90
|
+
enable_enhanced_features=False,
|
|
91
|
+
container=container,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Store service dependencies
|
|
95
|
+
self._toolchain_analyzer = toolchain_analyzer
|
|
96
|
+
self._agent_recommender = agent_recommender
|
|
97
|
+
self._agent_registry = agent_registry
|
|
98
|
+
self._agent_deployment = agent_deployment
|
|
99
|
+
|
|
100
|
+
# Configuration settings
|
|
101
|
+
self._min_confidence_default = 0.8
|
|
102
|
+
self._max_rollback_attempts = 3
|
|
103
|
+
self._deployment_timeout_seconds = 300 # 5 minutes
|
|
104
|
+
self._config_file_name = "auto-config.yaml"
|
|
105
|
+
|
|
106
|
+
self.logger.info("AutoConfigManagerService initialized")
|
|
107
|
+
|
|
108
|
+
async def _initialize(self) -> None:
|
|
109
|
+
"""Initialize the service (required by BaseService)."""
|
|
110
|
+
# Lazy initialization of dependencies if needed
|
|
111
|
+
if self._toolchain_analyzer is None:
|
|
112
|
+
try:
|
|
113
|
+
from ..project.toolchain_analyzer import ToolchainAnalyzerService
|
|
114
|
+
|
|
115
|
+
self._toolchain_analyzer = ToolchainAnalyzerService()
|
|
116
|
+
self.logger.info("Initialized ToolchainAnalyzerService")
|
|
117
|
+
except Exception as e:
|
|
118
|
+
self.logger.warning(
|
|
119
|
+
f"Failed to initialize ToolchainAnalyzerService: {e}"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if self._agent_recommender is None:
|
|
123
|
+
try:
|
|
124
|
+
self._agent_recommender = AgentRecommenderService()
|
|
125
|
+
self.logger.info("Initialized AgentRecommenderService")
|
|
126
|
+
except Exception as e:
|
|
127
|
+
self.logger.warning(
|
|
128
|
+
f"Failed to initialize AgentRecommenderService: {e}"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
async def _cleanup(self) -> None:
|
|
132
|
+
"""Cleanup service resources (required by BaseService)."""
|
|
133
|
+
# Clear any cached data
|
|
134
|
+
|
|
135
|
+
async def auto_configure(
|
|
136
|
+
self,
|
|
137
|
+
project_path: Path,
|
|
138
|
+
confirmation_required: bool = True,
|
|
139
|
+
dry_run: bool = False,
|
|
140
|
+
min_confidence: float = 0.8,
|
|
141
|
+
observer: Optional[IDeploymentObserver] = None,
|
|
142
|
+
) -> ConfigurationResult:
|
|
143
|
+
"""
|
|
144
|
+
Perform automated agent configuration.
|
|
145
|
+
|
|
146
|
+
Complete end-to-end configuration workflow:
|
|
147
|
+
1. Analyze project toolchain
|
|
148
|
+
2. Generate agent recommendations
|
|
149
|
+
3. Validate proposed configuration
|
|
150
|
+
4. Request user confirmation (if required)
|
|
151
|
+
5. Deploy approved agents
|
|
152
|
+
6. Verify deployment success
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
project_path: Path to the project root directory
|
|
156
|
+
confirmation_required: Whether to require user approval before deployment
|
|
157
|
+
dry_run: If True, preview only without deploying
|
|
158
|
+
min_confidence: Minimum confidence score for recommendations (0.0-1.0)
|
|
159
|
+
observer: Optional observer for progress tracking
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
ConfigurationResult: Complete configuration results including
|
|
163
|
+
deployed agents, validation results, and any errors
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
FileNotFoundError: If project_path does not exist
|
|
167
|
+
PermissionError: If unable to write to project directory
|
|
168
|
+
ValueError: If min_confidence is invalid
|
|
169
|
+
"""
|
|
170
|
+
# Validate inputs
|
|
171
|
+
if not project_path.exists():
|
|
172
|
+
raise FileNotFoundError(f"Project path does not exist: {project_path}")
|
|
173
|
+
if not project_path.is_dir():
|
|
174
|
+
raise ValueError(f"Project path is not a directory: {project_path}")
|
|
175
|
+
if not (0.0 <= min_confidence <= 1.0):
|
|
176
|
+
raise ValueError(
|
|
177
|
+
f"min_confidence must be between 0.0 and 1.0, got {min_confidence}"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Use NullObserver if none provided
|
|
181
|
+
if observer is None:
|
|
182
|
+
observer = NullObserver()
|
|
183
|
+
|
|
184
|
+
start_time = time.time()
|
|
185
|
+
self.logger.info(
|
|
186
|
+
f"Starting auto-configuration for project: {project_path} "
|
|
187
|
+
f"(dry_run={dry_run}, min_confidence={min_confidence})"
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
# Step 1: Analyze toolchain
|
|
192
|
+
analysis_start = time.time()
|
|
193
|
+
observer.on_analysis_started(str(project_path))
|
|
194
|
+
|
|
195
|
+
toolchain = await self._analyze_toolchain(project_path)
|
|
196
|
+
analysis_duration = (time.time() - analysis_start) * 1000
|
|
197
|
+
|
|
198
|
+
observer.on_analysis_completed(toolchain, analysis_duration)
|
|
199
|
+
self.logger.info(
|
|
200
|
+
f"Toolchain analysis complete: {toolchain.primary_language} "
|
|
201
|
+
f"with {len(toolchain.frameworks)} frameworks"
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# Step 2: Generate recommendations
|
|
205
|
+
rec_start = time.time()
|
|
206
|
+
observer.on_recommendation_started()
|
|
207
|
+
|
|
208
|
+
recommendations = await self._generate_recommendations(
|
|
209
|
+
toolchain, min_confidence
|
|
210
|
+
)
|
|
211
|
+
rec_duration = (time.time() - rec_start) * 1000
|
|
212
|
+
|
|
213
|
+
observer.on_recommendation_completed(recommendations, rec_duration)
|
|
214
|
+
self.logger.info(f"Generated {len(recommendations)} agent recommendations")
|
|
215
|
+
|
|
216
|
+
if not recommendations:
|
|
217
|
+
return ConfigurationResult(
|
|
218
|
+
status=OperationResult.SUCCESS,
|
|
219
|
+
message="No agents recommended for this project configuration",
|
|
220
|
+
recommendations=recommendations,
|
|
221
|
+
metadata={
|
|
222
|
+
"duration_ms": (time.time() - start_time) * 1000,
|
|
223
|
+
"dry_run": dry_run,
|
|
224
|
+
},
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Step 3: Validate configuration
|
|
228
|
+
observer.on_validation_started()
|
|
229
|
+
|
|
230
|
+
validation_result = self.validate_configuration(recommendations)
|
|
231
|
+
|
|
232
|
+
observer.on_validation_completed(
|
|
233
|
+
validation_result.is_valid,
|
|
234
|
+
validation_result.error_count,
|
|
235
|
+
validation_result.warning_count,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
if not validation_result.is_valid:
|
|
239
|
+
self.logger.error(
|
|
240
|
+
f"Validation failed with {validation_result.error_count} errors"
|
|
241
|
+
)
|
|
242
|
+
return ConfigurationResult(
|
|
243
|
+
status=OperationResult.ERROR,
|
|
244
|
+
validation_errors=[
|
|
245
|
+
issue.message for issue in validation_result.errors
|
|
246
|
+
],
|
|
247
|
+
validation_warnings=[
|
|
248
|
+
issue.message for issue in validation_result.warnings
|
|
249
|
+
],
|
|
250
|
+
recommendations=recommendations,
|
|
251
|
+
message="Configuration validation failed",
|
|
252
|
+
metadata={
|
|
253
|
+
"duration_ms": (time.time() - start_time) * 1000,
|
|
254
|
+
"dry_run": dry_run,
|
|
255
|
+
},
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Step 4: Handle dry-run
|
|
259
|
+
if dry_run:
|
|
260
|
+
self.logger.info("Dry-run mode: skipping deployment")
|
|
261
|
+
return ConfigurationResult(
|
|
262
|
+
status=OperationResult.SUCCESS,
|
|
263
|
+
validation_warnings=[
|
|
264
|
+
issue.message for issue in validation_result.warnings
|
|
265
|
+
],
|
|
266
|
+
recommendations=recommendations,
|
|
267
|
+
message=f"Dry-run complete: would deploy {len(recommendations)} agents",
|
|
268
|
+
metadata={
|
|
269
|
+
"duration_ms": (time.time() - start_time) * 1000,
|
|
270
|
+
"dry_run": True,
|
|
271
|
+
},
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Step 5: User confirmation
|
|
275
|
+
if confirmation_required:
|
|
276
|
+
confirmed = await self._request_confirmation(
|
|
277
|
+
recommendations, validation_result
|
|
278
|
+
)
|
|
279
|
+
if not confirmed:
|
|
280
|
+
self.logger.info("User cancelled auto-configuration")
|
|
281
|
+
return ConfigurationResult(
|
|
282
|
+
status=OperationResult.CANCELLED,
|
|
283
|
+
recommendations=recommendations,
|
|
284
|
+
message="Auto-configuration cancelled by user",
|
|
285
|
+
metadata={
|
|
286
|
+
"duration_ms": (time.time() - start_time) * 1000,
|
|
287
|
+
"dry_run": dry_run,
|
|
288
|
+
},
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Step 6: Deploy agents
|
|
292
|
+
deploy_start = time.time()
|
|
293
|
+
observer.on_deployment_started(len(recommendations))
|
|
294
|
+
|
|
295
|
+
deployed_agents, failed_agents = await self._deploy_agents(
|
|
296
|
+
project_path, recommendations, observer
|
|
297
|
+
)
|
|
298
|
+
deploy_duration = (time.time() - deploy_start) * 1000
|
|
299
|
+
|
|
300
|
+
observer.on_deployment_completed(
|
|
301
|
+
len(deployed_agents), len(failed_agents), deploy_duration
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# Step 7: Handle deployment failures
|
|
305
|
+
if failed_agents:
|
|
306
|
+
self.logger.warning(
|
|
307
|
+
f"Deployment completed with {len(failed_agents)} failures"
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
# Attempt rollback
|
|
311
|
+
if deployed_agents:
|
|
312
|
+
observer.on_rollback_started(deployed_agents)
|
|
313
|
+
rollback_success = await self._rollback_deployment(
|
|
314
|
+
project_path, deployed_agents
|
|
315
|
+
)
|
|
316
|
+
observer.on_rollback_completed(rollback_success)
|
|
317
|
+
|
|
318
|
+
return ConfigurationResult(
|
|
319
|
+
status=(
|
|
320
|
+
OperationResult.WARNING
|
|
321
|
+
if deployed_agents
|
|
322
|
+
else OperationResult.FAILED
|
|
323
|
+
),
|
|
324
|
+
deployed_agents=deployed_agents,
|
|
325
|
+
failed_agents=failed_agents,
|
|
326
|
+
validation_warnings=[
|
|
327
|
+
issue.message for issue in validation_result.warnings
|
|
328
|
+
],
|
|
329
|
+
recommendations=recommendations,
|
|
330
|
+
message=f"Deployment completed with issues: {len(deployed_agents)} succeeded, {len(failed_agents)} failed",
|
|
331
|
+
metadata={
|
|
332
|
+
"duration_ms": (time.time() - start_time) * 1000,
|
|
333
|
+
"dry_run": dry_run,
|
|
334
|
+
"rollback_attempted": len(deployed_agents) > 0,
|
|
335
|
+
},
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# Step 8: Save configuration
|
|
339
|
+
await self._save_configuration(project_path, toolchain, recommendations)
|
|
340
|
+
|
|
341
|
+
# Success!
|
|
342
|
+
total_duration = (time.time() - start_time) * 1000
|
|
343
|
+
self.logger.info(
|
|
344
|
+
f"Auto-configuration completed successfully in {total_duration:.0f}ms: "
|
|
345
|
+
f"deployed {len(deployed_agents)} agents"
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
return ConfigurationResult(
|
|
349
|
+
status=OperationResult.SUCCESS,
|
|
350
|
+
deployed_agents=deployed_agents,
|
|
351
|
+
validation_warnings=[
|
|
352
|
+
issue.message for issue in validation_result.warnings
|
|
353
|
+
],
|
|
354
|
+
recommendations=recommendations,
|
|
355
|
+
message=f"Successfully configured {len(deployed_agents)} agents",
|
|
356
|
+
metadata={
|
|
357
|
+
"duration_ms": total_duration,
|
|
358
|
+
"dry_run": dry_run,
|
|
359
|
+
},
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
except Exception as e:
|
|
363
|
+
self.logger.error(f"Auto-configuration failed: {e}", exc_info=True)
|
|
364
|
+
observer.on_error("auto-configuration", str(e), e)
|
|
365
|
+
|
|
366
|
+
return ConfigurationResult(
|
|
367
|
+
status=OperationResult.FAILED,
|
|
368
|
+
message=f"Auto-configuration failed: {e}",
|
|
369
|
+
metadata={
|
|
370
|
+
"duration_ms": (time.time() - start_time) * 1000,
|
|
371
|
+
"dry_run": dry_run,
|
|
372
|
+
"error": str(e),
|
|
373
|
+
"traceback": traceback.format_exc(),
|
|
374
|
+
},
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
def validate_configuration(
|
|
378
|
+
self, recommendations: List[AgentRecommendation]
|
|
379
|
+
) -> ValidationResult:
|
|
380
|
+
"""
|
|
381
|
+
Validate proposed configuration before deployment.
|
|
382
|
+
|
|
383
|
+
Performs comprehensive validation:
|
|
384
|
+
- Checks for agent existence
|
|
385
|
+
- Verifies no conflicts (multiple agents for same role)
|
|
386
|
+
- Validates minimum confidence threshold
|
|
387
|
+
- Checks deployment prerequisites
|
|
388
|
+
- Warns about unmatched toolchains
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
recommendations: List of agent recommendations to validate
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
ValidationResult: Validation result with any warnings or errors
|
|
395
|
+
|
|
396
|
+
Raises:
|
|
397
|
+
ValueError: If recommendations list is empty or invalid
|
|
398
|
+
"""
|
|
399
|
+
if not recommendations:
|
|
400
|
+
raise ValueError("Cannot validate empty recommendations list")
|
|
401
|
+
|
|
402
|
+
issues: List[ValidationIssue] = []
|
|
403
|
+
validated_agents: List[str] = []
|
|
404
|
+
|
|
405
|
+
# Track agent roles to detect conflicts
|
|
406
|
+
role_agents: Dict[str, List[str]] = {}
|
|
407
|
+
|
|
408
|
+
for recommendation in recommendations:
|
|
409
|
+
agent_id = recommendation.agent_id
|
|
410
|
+
validated_agents.append(agent_id)
|
|
411
|
+
|
|
412
|
+
# Check 1: Agent existence
|
|
413
|
+
if self._agent_registry:
|
|
414
|
+
try:
|
|
415
|
+
# Try to get agent metadata to verify it exists
|
|
416
|
+
agent = self._agent_registry.get_agent(agent_id)
|
|
417
|
+
if agent is None:
|
|
418
|
+
issues.append(
|
|
419
|
+
ValidationIssue(
|
|
420
|
+
severity=ValidationSeverity.ERROR,
|
|
421
|
+
message=f"Agent '{agent_id}' does not exist",
|
|
422
|
+
agent_id=agent_id,
|
|
423
|
+
suggested_fix="Remove this recommendation or ensure agent is installed",
|
|
424
|
+
)
|
|
425
|
+
)
|
|
426
|
+
continue
|
|
427
|
+
except Exception as e:
|
|
428
|
+
issues.append(
|
|
429
|
+
ValidationIssue(
|
|
430
|
+
severity=ValidationSeverity.WARNING,
|
|
431
|
+
message=f"Could not verify agent '{agent_id}': {e}",
|
|
432
|
+
agent_id=agent_id,
|
|
433
|
+
)
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
# Check 2: Confidence threshold
|
|
437
|
+
if recommendation.confidence_score < 0.5:
|
|
438
|
+
issues.append(
|
|
439
|
+
ValidationIssue(
|
|
440
|
+
severity=ValidationSeverity.WARNING,
|
|
441
|
+
message=f"Low confidence score ({recommendation.confidence_score:.2f}) for agent '{agent_id}'",
|
|
442
|
+
agent_id=agent_id,
|
|
443
|
+
suggested_fix="Consider reviewing agent capabilities or adjusting threshold",
|
|
444
|
+
)
|
|
445
|
+
)
|
|
446
|
+
elif recommendation.confidence_score < 0.7:
|
|
447
|
+
issues.append(
|
|
448
|
+
ValidationIssue(
|
|
449
|
+
severity=ValidationSeverity.INFO,
|
|
450
|
+
message=f"Moderate confidence score ({recommendation.confidence_score:.2f}) for agent '{agent_id}'",
|
|
451
|
+
agent_id=agent_id,
|
|
452
|
+
)
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# Check 3: Track roles for conflict detection
|
|
456
|
+
if recommendation.capabilities:
|
|
457
|
+
for spec in recommendation.capabilities.specializations:
|
|
458
|
+
role = spec.value
|
|
459
|
+
if role not in role_agents:
|
|
460
|
+
role_agents[role] = []
|
|
461
|
+
role_agents[role].append(agent_id)
|
|
462
|
+
|
|
463
|
+
# Check 4: Recommendation concerns
|
|
464
|
+
if recommendation.has_concerns:
|
|
465
|
+
for concern in recommendation.concerns:
|
|
466
|
+
issues.append(
|
|
467
|
+
ValidationIssue(
|
|
468
|
+
severity=ValidationSeverity.INFO,
|
|
469
|
+
message=f"Concern for '{agent_id}': {concern}",
|
|
470
|
+
agent_id=agent_id,
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Check 5: Role conflicts (multiple agents for same role)
|
|
475
|
+
for role, agents in role_agents.items():
|
|
476
|
+
if len(agents) > 1:
|
|
477
|
+
issues.append(
|
|
478
|
+
ValidationIssue(
|
|
479
|
+
severity=ValidationSeverity.WARNING,
|
|
480
|
+
message=f"Multiple agents ({', '.join(agents)}) recommended for role '{role}'",
|
|
481
|
+
suggested_fix="Consider selecting the highest confidence agent for this role",
|
|
482
|
+
)
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# Determine if validation passes (no errors)
|
|
486
|
+
is_valid = not any(
|
|
487
|
+
issue.severity == ValidationSeverity.ERROR for issue in issues
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
self.logger.info(
|
|
491
|
+
f"Validation complete: {len(validated_agents)} agents, "
|
|
492
|
+
f"{len([i for i in issues if i.severity == ValidationSeverity.ERROR])} errors, "
|
|
493
|
+
f"{len([i for i in issues if i.severity == ValidationSeverity.WARNING])} warnings"
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
return ValidationResult(
|
|
497
|
+
is_valid=is_valid,
|
|
498
|
+
issues=issues,
|
|
499
|
+
validated_agents=validated_agents,
|
|
500
|
+
metadata={"validation_timestamp": datetime.now(timezone.utc).isoformat()},
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
def preview_configuration(
|
|
504
|
+
self, project_path: Path, min_confidence: float = 0.8
|
|
505
|
+
) -> ConfigurationPreview:
|
|
506
|
+
"""
|
|
507
|
+
Preview what would be configured without applying changes.
|
|
508
|
+
|
|
509
|
+
Performs analysis and recommendation without making any changes:
|
|
510
|
+
- Analyzes project toolchain
|
|
511
|
+
- Generates recommendations
|
|
512
|
+
- Validates configuration
|
|
513
|
+
- Returns preview of what would be deployed
|
|
514
|
+
|
|
515
|
+
Args:
|
|
516
|
+
project_path: Path to the project root directory
|
|
517
|
+
min_confidence: Minimum confidence score for recommendations
|
|
518
|
+
|
|
519
|
+
Returns:
|
|
520
|
+
ConfigurationPreview: Preview of configuration that would be applied
|
|
521
|
+
|
|
522
|
+
Raises:
|
|
523
|
+
FileNotFoundError: If project_path does not exist
|
|
524
|
+
"""
|
|
525
|
+
if not project_path.exists():
|
|
526
|
+
raise FileNotFoundError(f"Project path does not exist: {project_path}")
|
|
527
|
+
|
|
528
|
+
self.logger.info(f"Generating configuration preview for: {project_path}")
|
|
529
|
+
|
|
530
|
+
try:
|
|
531
|
+
# Run analysis and recommendations synchronously for preview
|
|
532
|
+
import asyncio
|
|
533
|
+
|
|
534
|
+
loop = asyncio.get_event_loop()
|
|
535
|
+
|
|
536
|
+
# Analyze toolchain
|
|
537
|
+
if asyncio.iscoroutinefunction(self._analyze_toolchain):
|
|
538
|
+
toolchain = loop.run_until_complete(
|
|
539
|
+
self._analyze_toolchain(project_path)
|
|
540
|
+
)
|
|
541
|
+
else:
|
|
542
|
+
toolchain = self._analyze_toolchain(project_path)
|
|
543
|
+
|
|
544
|
+
# Generate recommendations
|
|
545
|
+
if asyncio.iscoroutinefunction(self._generate_recommendations):
|
|
546
|
+
recommendations = loop.run_until_complete(
|
|
547
|
+
self._generate_recommendations(toolchain, min_confidence)
|
|
548
|
+
)
|
|
549
|
+
else:
|
|
550
|
+
recommendations = self._generate_recommendations(
|
|
551
|
+
toolchain, min_confidence
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
# Validate configuration
|
|
555
|
+
validation_result = self.validate_configuration(recommendations)
|
|
556
|
+
|
|
557
|
+
# Estimate deployment time (5 seconds per agent)
|
|
558
|
+
estimated_time = len(recommendations) * 5.0
|
|
559
|
+
|
|
560
|
+
# Determine what would be deployed
|
|
561
|
+
would_deploy = [
|
|
562
|
+
rec.agent_id
|
|
563
|
+
for rec in recommendations
|
|
564
|
+
if rec.confidence_score >= min_confidence
|
|
565
|
+
]
|
|
566
|
+
would_skip = [
|
|
567
|
+
rec.agent_id
|
|
568
|
+
for rec in recommendations
|
|
569
|
+
if rec.confidence_score < min_confidence
|
|
570
|
+
]
|
|
571
|
+
|
|
572
|
+
preview = ConfigurationPreview(
|
|
573
|
+
recommendations=recommendations,
|
|
574
|
+
validation_result=validation_result,
|
|
575
|
+
estimated_deployment_time=estimated_time,
|
|
576
|
+
would_deploy=would_deploy,
|
|
577
|
+
would_skip=would_skip,
|
|
578
|
+
requires_confirmation=True,
|
|
579
|
+
metadata={
|
|
580
|
+
"preview_timestamp": datetime.now(timezone.utc).isoformat(),
|
|
581
|
+
"toolchain_summary": {
|
|
582
|
+
"primary_language": toolchain.primary_language,
|
|
583
|
+
"frameworks": [fw.name for fw in toolchain.frameworks],
|
|
584
|
+
"deployment_target": (
|
|
585
|
+
toolchain.deployment_target.platform
|
|
586
|
+
if toolchain.deployment_target
|
|
587
|
+
else None
|
|
588
|
+
),
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
self.logger.info(
|
|
594
|
+
f"Preview generated: {preview.deployment_count} agents would be deployed"
|
|
595
|
+
)
|
|
596
|
+
return preview
|
|
597
|
+
|
|
598
|
+
except Exception as e:
|
|
599
|
+
self.logger.error(f"Failed to generate preview: {e}", exc_info=True)
|
|
600
|
+
raise
|
|
601
|
+
|
|
602
|
+
# Private helper methods
|
|
603
|
+
|
|
604
|
+
async def _analyze_toolchain(self, project_path: Path) -> ToolchainAnalysis:
|
|
605
|
+
"""Analyze project toolchain."""
|
|
606
|
+
if self._toolchain_analyzer is None:
|
|
607
|
+
raise RuntimeError("ToolchainAnalyzer not initialized")
|
|
608
|
+
|
|
609
|
+
return self._toolchain_analyzer.analyze_toolchain(project_path)
|
|
610
|
+
|
|
611
|
+
async def _generate_recommendations(
|
|
612
|
+
self, toolchain: ToolchainAnalysis, min_confidence: float
|
|
613
|
+
) -> List[AgentRecommendation]:
|
|
614
|
+
"""Generate agent recommendations."""
|
|
615
|
+
if self._agent_recommender is None:
|
|
616
|
+
raise RuntimeError("AgentRecommender not initialized")
|
|
617
|
+
|
|
618
|
+
constraints = {"min_confidence": min_confidence}
|
|
619
|
+
return self._agent_recommender.recommend_agents(toolchain, constraints)
|
|
620
|
+
|
|
621
|
+
async def _request_confirmation(
|
|
622
|
+
self, recommendations: List[AgentRecommendation], validation: ValidationResult
|
|
623
|
+
) -> bool:
|
|
624
|
+
"""
|
|
625
|
+
Request user confirmation before deployment.
|
|
626
|
+
|
|
627
|
+
TODO: Implement interactive confirmation dialog.
|
|
628
|
+
For now, returns True (auto-approve) to enable testing.
|
|
629
|
+
|
|
630
|
+
Args:
|
|
631
|
+
recommendations: List of recommended agents
|
|
632
|
+
validation: Validation results
|
|
633
|
+
|
|
634
|
+
Returns:
|
|
635
|
+
bool: True if user confirms, False otherwise
|
|
636
|
+
"""
|
|
637
|
+
# TODO: Implement interactive confirmation
|
|
638
|
+
# For now, auto-approve if validation passed
|
|
639
|
+
return validation.is_valid
|
|
640
|
+
|
|
641
|
+
async def _deploy_agents(
|
|
642
|
+
self,
|
|
643
|
+
project_path: Path,
|
|
644
|
+
recommendations: List[AgentRecommendation],
|
|
645
|
+
observer: IDeploymentObserver,
|
|
646
|
+
) -> tuple[List[str], List[str]]:
|
|
647
|
+
"""
|
|
648
|
+
Deploy recommended agents.
|
|
649
|
+
|
|
650
|
+
Args:
|
|
651
|
+
project_path: Project root directory
|
|
652
|
+
recommendations: List of recommendations to deploy
|
|
653
|
+
observer: Observer for progress tracking
|
|
654
|
+
|
|
655
|
+
Returns:
|
|
656
|
+
Tuple of (deployed_agent_ids, failed_agent_ids)
|
|
657
|
+
"""
|
|
658
|
+
deployed = []
|
|
659
|
+
failed = []
|
|
660
|
+
|
|
661
|
+
# Sort recommendations by deployment priority
|
|
662
|
+
sorted_recs = sorted(recommendations, key=lambda r: r.deployment_priority)
|
|
663
|
+
|
|
664
|
+
for index, recommendation in enumerate(sorted_recs, 1):
|
|
665
|
+
agent_id = recommendation.agent_id
|
|
666
|
+
agent_name = recommendation.agent_name
|
|
667
|
+
|
|
668
|
+
try:
|
|
669
|
+
observer.on_agent_deployment_started(
|
|
670
|
+
agent_id, agent_name, index, len(sorted_recs)
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
# TODO: Integrate with actual AgentDeploymentService
|
|
674
|
+
# For now, simulate deployment
|
|
675
|
+
await self._deploy_single_agent(agent_id, project_path)
|
|
676
|
+
|
|
677
|
+
observer.on_agent_deployment_completed(
|
|
678
|
+
agent_id, agent_name, success=True
|
|
679
|
+
)
|
|
680
|
+
deployed.append(agent_id)
|
|
681
|
+
self.logger.debug(f"Successfully deployed agent: {agent_id}")
|
|
682
|
+
|
|
683
|
+
except Exception as e:
|
|
684
|
+
self.logger.error(
|
|
685
|
+
f"Failed to deploy agent '{agent_id}': {e}", exc_info=True
|
|
686
|
+
)
|
|
687
|
+
observer.on_agent_deployment_completed(
|
|
688
|
+
agent_id, agent_name, success=False, error=str(e)
|
|
689
|
+
)
|
|
690
|
+
failed.append(agent_id)
|
|
691
|
+
|
|
692
|
+
return deployed, failed
|
|
693
|
+
|
|
694
|
+
async def _deploy_single_agent(self, agent_id: str, project_path: Path) -> None:
|
|
695
|
+
"""
|
|
696
|
+
Deploy a single agent.
|
|
697
|
+
|
|
698
|
+
TODO: Integrate with AgentDeploymentService.
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
agent_id: Agent identifier
|
|
702
|
+
project_path: Project root directory
|
|
703
|
+
|
|
704
|
+
Raises:
|
|
705
|
+
Exception: If deployment fails
|
|
706
|
+
"""
|
|
707
|
+
# TODO: Implement actual deployment logic
|
|
708
|
+
# For now, simulate deployment with a small delay
|
|
709
|
+
import asyncio
|
|
710
|
+
|
|
711
|
+
await asyncio.sleep(0.1)
|
|
712
|
+
|
|
713
|
+
# Placeholder: will integrate with AgentDeploymentService
|
|
714
|
+
self.logger.debug(f"Deployed agent {agent_id} to {project_path}")
|
|
715
|
+
|
|
716
|
+
async def _rollback_deployment(
|
|
717
|
+
self, project_path: Path, deployed_agents: List[str]
|
|
718
|
+
) -> bool:
|
|
719
|
+
"""
|
|
720
|
+
Rollback deployed agents after failure.
|
|
721
|
+
|
|
722
|
+
Args:
|
|
723
|
+
project_path: Project root directory
|
|
724
|
+
deployed_agents: List of agent IDs to rollback
|
|
725
|
+
|
|
726
|
+
Returns:
|
|
727
|
+
bool: True if rollback succeeded, False otherwise
|
|
728
|
+
"""
|
|
729
|
+
self.logger.warning(f"Rolling back {len(deployed_agents)} deployed agents")
|
|
730
|
+
|
|
731
|
+
try:
|
|
732
|
+
# TODO: Implement actual rollback logic
|
|
733
|
+
# For now, log the rollback attempt
|
|
734
|
+
for agent_id in deployed_agents:
|
|
735
|
+
self.logger.info(f"Rolling back agent: {agent_id}")
|
|
736
|
+
|
|
737
|
+
return True
|
|
738
|
+
|
|
739
|
+
except Exception as e:
|
|
740
|
+
self.logger.error(f"Rollback failed: {e}", exc_info=True)
|
|
741
|
+
return False
|
|
742
|
+
|
|
743
|
+
async def _save_configuration(
|
|
744
|
+
self,
|
|
745
|
+
project_path: Path,
|
|
746
|
+
toolchain: ToolchainAnalysis,
|
|
747
|
+
recommendations: List[AgentRecommendation],
|
|
748
|
+
) -> None:
|
|
749
|
+
"""
|
|
750
|
+
Save auto-configuration metadata to project.
|
|
751
|
+
|
|
752
|
+
Args:
|
|
753
|
+
project_path: Project root directory
|
|
754
|
+
toolchain: Toolchain analysis results
|
|
755
|
+
recommendations: Agent recommendations that were deployed
|
|
756
|
+
"""
|
|
757
|
+
config_dir = project_path / ".claude-mpm"
|
|
758
|
+
config_dir.mkdir(exist_ok=True)
|
|
759
|
+
|
|
760
|
+
config_file = config_dir / self._config_file_name
|
|
761
|
+
|
|
762
|
+
config_data = {
|
|
763
|
+
"auto_config": {
|
|
764
|
+
"enabled": True,
|
|
765
|
+
"last_run": datetime.now(timezone.utc).isoformat(),
|
|
766
|
+
"toolchain_snapshot": {
|
|
767
|
+
"primary_language": toolchain.primary_language,
|
|
768
|
+
"frameworks": [fw.name for fw in toolchain.frameworks],
|
|
769
|
+
"deployment_targets": (
|
|
770
|
+
[toolchain.deployment_target.platform]
|
|
771
|
+
if toolchain.deployment_target
|
|
772
|
+
else []
|
|
773
|
+
),
|
|
774
|
+
},
|
|
775
|
+
"deployed_agents": [
|
|
776
|
+
{
|
|
777
|
+
"agent_id": rec.agent_id,
|
|
778
|
+
"agent_name": rec.agent_name,
|
|
779
|
+
"confidence": rec.confidence_score,
|
|
780
|
+
"deployed_at": datetime.now(timezone.utc).isoformat(),
|
|
781
|
+
}
|
|
782
|
+
for rec in recommendations
|
|
783
|
+
],
|
|
784
|
+
"user_overrides": {"disabled_agents": [], "custom_agents": []},
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
try:
|
|
789
|
+
with config_file.open("w", encoding="utf-8") as f:
|
|
790
|
+
yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)
|
|
791
|
+
|
|
792
|
+
self.logger.info(f"Saved auto-configuration to: {config_file}")
|
|
793
|
+
|
|
794
|
+
except Exception as e:
|
|
795
|
+
self.logger.error(f"Failed to save configuration: {e}", exc_info=True)
|
|
796
|
+
# Don't raise - configuration save is non-critical
|