claude-mpm 4.3.22__py3-none-any.whl → 4.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/WORKFLOW.md +2 -14
  3. claude_mpm/cli/commands/configure.py +2 -29
  4. claude_mpm/cli/commands/doctor.py +2 -2
  5. claude_mpm/cli/commands/mpm_init.py +3 -3
  6. claude_mpm/cli/parsers/configure_parser.py +4 -15
  7. claude_mpm/core/framework/__init__.py +38 -0
  8. claude_mpm/core/framework/formatters/__init__.py +11 -0
  9. claude_mpm/core/framework/formatters/capability_generator.py +356 -0
  10. claude_mpm/core/framework/formatters/content_formatter.py +283 -0
  11. claude_mpm/core/framework/formatters/context_generator.py +180 -0
  12. claude_mpm/core/framework/loaders/__init__.py +13 -0
  13. claude_mpm/core/framework/loaders/agent_loader.py +202 -0
  14. claude_mpm/core/framework/loaders/file_loader.py +213 -0
  15. claude_mpm/core/framework/loaders/instruction_loader.py +151 -0
  16. claude_mpm/core/framework/loaders/packaged_loader.py +208 -0
  17. claude_mpm/core/framework/processors/__init__.py +11 -0
  18. claude_mpm/core/framework/processors/memory_processor.py +222 -0
  19. claude_mpm/core/framework/processors/metadata_processor.py +146 -0
  20. claude_mpm/core/framework/processors/template_processor.py +238 -0
  21. claude_mpm/core/framework_loader.py +277 -1798
  22. claude_mpm/hooks/__init__.py +9 -1
  23. claude_mpm/hooks/kuzu_memory_hook.py +352 -0
  24. claude_mpm/hooks/memory_integration_hook.py +1 -1
  25. claude_mpm/services/agents/memory/content_manager.py +5 -2
  26. claude_mpm/services/agents/memory/memory_file_service.py +1 -0
  27. claude_mpm/services/agents/memory/memory_limits_service.py +1 -0
  28. claude_mpm/services/core/path_resolver.py +1 -0
  29. claude_mpm/services/diagnostics/diagnostic_runner.py +1 -0
  30. claude_mpm/services/mcp_config_manager.py +67 -4
  31. claude_mpm/services/mcp_gateway/core/process_pool.py +281 -0
  32. claude_mpm/services/mcp_gateway/core/startup_verification.py +2 -2
  33. claude_mpm/services/mcp_gateway/main.py +3 -13
  34. claude_mpm/services/mcp_gateway/server/stdio_server.py +4 -10
  35. claude_mpm/services/mcp_gateway/tools/__init__.py +13 -2
  36. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +36 -6
  37. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +542 -0
  38. claude_mpm/services/shared/__init__.py +2 -1
  39. claude_mpm/services/shared/service_factory.py +8 -5
  40. claude_mpm/services/unified/__init__.py +65 -0
  41. claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
  42. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +473 -0
  43. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +643 -0
  44. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +804 -0
  45. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +661 -0
  46. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +696 -0
  47. claude_mpm/services/unified/config_strategies/__init__.py +190 -0
  48. claude_mpm/services/unified/config_strategies/config_schema.py +689 -0
  49. claude_mpm/services/unified/config_strategies/context_strategy.py +748 -0
  50. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +999 -0
  51. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +871 -0
  52. claude_mpm/services/unified/config_strategies/unified_config_service.py +802 -0
  53. claude_mpm/services/unified/config_strategies/validation_strategy.py +1105 -0
  54. claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
  55. claude_mpm/services/unified/deployment_strategies/base.py +557 -0
  56. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +486 -0
  57. claude_mpm/services/unified/deployment_strategies/local.py +594 -0
  58. claude_mpm/services/unified/deployment_strategies/utils.py +672 -0
  59. claude_mpm/services/unified/deployment_strategies/vercel.py +471 -0
  60. claude_mpm/services/unified/interfaces.py +499 -0
  61. claude_mpm/services/unified/migration.py +532 -0
  62. claude_mpm/services/unified/strategies.py +551 -0
  63. claude_mpm/services/unified/unified_analyzer.py +534 -0
  64. claude_mpm/services/unified/unified_config.py +688 -0
  65. claude_mpm/services/unified/unified_deployment.py +470 -0
  66. {claude_mpm-4.3.22.dist-info → claude_mpm-4.4.3.dist-info}/METADATA +15 -15
  67. {claude_mpm-4.3.22.dist-info → claude_mpm-4.4.3.dist-info}/RECORD +71 -32
  68. claude_mpm/cli/commands/configure_tui.py +0 -1927
  69. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +0 -645
  70. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +0 -602
  71. {claude_mpm-4.3.22.dist-info → claude_mpm-4.4.3.dist-info}/WHEEL +0 -0
  72. {claude_mpm-4.3.22.dist-info → claude_mpm-4.4.3.dist-info}/entry_points.txt +0 -0
  73. {claude_mpm-4.3.22.dist-info → claude_mpm-4.4.3.dist-info}/licenses/LICENSE +0 -0
  74. {claude_mpm-4.3.22.dist-info → claude_mpm-4.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,97 @@
1
+ """
2
+ Unified Deployment Strategies
3
+ =============================
4
+
5
+ This module consolidates 45+ deployment services into a unified strategy-based architecture.
6
+ Reduces ~17,938 LOC of duplicated deployment code to ~6,000 LOC through strategic pattern
7
+ application and code reuse.
8
+
9
+ Deployment Strategies:
10
+ - LocalDeploymentStrategy: Deploy to local filesystem/project
11
+ - VercelDeploymentStrategy: Deploy to Vercel platform
12
+ - RailwayDeploymentStrategy: Deploy to Railway platform
13
+ - AWSDeploymentStrategy: Deploy to AWS (Lambda, EC2, ECS)
14
+ - DockerDeploymentStrategy: Docker container deployments
15
+ - GitDeploymentStrategy: Git-based deployments (GitHub, GitLab)
16
+
17
+ Consolidates services from:
18
+ - agent_deployment.py (887 LOC)
19
+ - multi_source_deployment_service.py (1,055 LOC)
20
+ - agent_template_builder.py (1,134 LOC)
21
+ - 8+ pipeline services with duplicated patterns
22
+ - 30+ other deployment-related services
23
+
24
+ Design Principles:
25
+ 1. Strategy pattern for pluggable deployment types
26
+ 2. Common utilities for shared functionality
27
+ 3. Health checks and rollback capabilities
28
+ 4. Validation and pre-flight checks
29
+ 5. Metrics and monitoring integration
30
+ """
31
+
32
+ from .base import DeploymentStrategy, DeploymentContext, DeploymentResult
33
+ from .local import LocalDeploymentStrategy
34
+ from .vercel import VercelDeploymentStrategy
35
+ from .cloud_strategies import (
36
+ RailwayDeploymentStrategy,
37
+ AWSDeploymentStrategy,
38
+ DockerDeploymentStrategy,
39
+ GitDeploymentStrategy,
40
+ )
41
+ from .utils import (
42
+ validate_deployment_config,
43
+ prepare_deployment_artifact,
44
+ verify_deployment_health,
45
+ rollback_deployment,
46
+ )
47
+
48
+ __all__ = [
49
+ # Base classes
50
+ "DeploymentStrategy",
51
+ "DeploymentContext",
52
+ "DeploymentResult",
53
+ # Strategy implementations
54
+ "LocalDeploymentStrategy",
55
+ "VercelDeploymentStrategy",
56
+ "RailwayDeploymentStrategy",
57
+ "AWSDeploymentStrategy",
58
+ "DockerDeploymentStrategy",
59
+ "GitDeploymentStrategy",
60
+ # Utilities
61
+ "validate_deployment_config",
62
+ "prepare_deployment_artifact",
63
+ "verify_deployment_health",
64
+ "rollback_deployment",
65
+ ]
66
+
67
+ # Strategy registry for automatic discovery
68
+ DEPLOYMENT_STRATEGIES = {
69
+ "local": LocalDeploymentStrategy,
70
+ "vercel": VercelDeploymentStrategy,
71
+ "railway": RailwayDeploymentStrategy,
72
+ "aws": AWSDeploymentStrategy,
73
+ "docker": DockerDeploymentStrategy,
74
+ "git": GitDeploymentStrategy,
75
+ }
76
+
77
+
78
+ def get_deployment_strategy(deployment_type: str) -> type[DeploymentStrategy]:
79
+ """
80
+ Get deployment strategy class by type.
81
+
82
+ Args:
83
+ deployment_type: Type of deployment (local, vercel, aws, etc.)
84
+
85
+ Returns:
86
+ DeploymentStrategy class
87
+
88
+ Raises:
89
+ ValueError: If deployment type not supported
90
+ """
91
+ strategy_class = DEPLOYMENT_STRATEGIES.get(deployment_type.lower())
92
+ if not strategy_class:
93
+ raise ValueError(
94
+ f"Unsupported deployment type: {deployment_type}. "
95
+ f"Supported types: {', '.join(DEPLOYMENT_STRATEGIES.keys())}"
96
+ )
97
+ return strategy_class
@@ -0,0 +1,557 @@
1
+ """
2
+ Base Deployment Strategy
3
+ ========================
4
+
5
+ Enhanced base class for all deployment strategies, extending the generic
6
+ DeploymentStrategy from the unified strategies module.
7
+
8
+ This module provides:
9
+ - Abstract base class with common deployment operations
10
+ - Deployment context and result data structures
11
+ - Health check and rollback interfaces
12
+ - Validation and pre-flight check framework
13
+ - Metrics collection hooks
14
+ """
15
+
16
+ from abc import abstractmethod
17
+ from dataclasses import dataclass, field
18
+ from datetime import datetime
19
+ from enum import Enum
20
+ from pathlib import Path
21
+ from typing import Any, Dict, List, Optional, Union
22
+
23
+ from claude_mpm.core.logging_utils import get_logger
24
+ from claude_mpm.services.unified.strategies import (
25
+ DeploymentStrategy as BaseDeploymentStrategy,
26
+ StrategyContext,
27
+ StrategyMetadata,
28
+ StrategyPriority,
29
+ )
30
+
31
+
32
+ class DeploymentStatus(Enum):
33
+ """Deployment status enumeration."""
34
+ PENDING = "pending"
35
+ VALIDATING = "validating"
36
+ PREPARING = "preparing"
37
+ DEPLOYING = "deploying"
38
+ VERIFYING = "verifying"
39
+ COMPLETED = "completed"
40
+ FAILED = "failed"
41
+ ROLLED_BACK = "rolled_back"
42
+
43
+
44
+ class DeploymentType(Enum):
45
+ """Types of deployments."""
46
+ AGENT = "agent"
47
+ CONFIG = "config"
48
+ RESOURCE = "resource"
49
+ TEMPLATE = "template"
50
+ APPLICATION = "application"
51
+ SERVICE = "service"
52
+ INFRASTRUCTURE = "infrastructure"
53
+
54
+
55
+ @dataclass
56
+ class DeploymentContext:
57
+ """
58
+ Enhanced context for deployment operations.
59
+
60
+ Consolidates context patterns from:
61
+ - agent_deployment.py
62
+ - multi_source_deployment_service.py
63
+ - deployment_config_loader.py
64
+ """
65
+ # Core deployment info
66
+ source: Union[str, Path]
67
+ target: Union[str, Path]
68
+ deployment_type: DeploymentType
69
+
70
+ # Configuration
71
+ config: Dict[str, Any] = field(default_factory=dict)
72
+ environment: Dict[str, str] = field(default_factory=dict)
73
+
74
+ # Options
75
+ force: bool = False
76
+ dry_run: bool = False
77
+ validate_only: bool = False
78
+ backup_enabled: bool = True
79
+
80
+ # Versioning
81
+ version: Optional[str] = None
82
+ previous_version: Optional[str] = None
83
+
84
+ # Metadata
85
+ tags: List[str] = field(default_factory=list)
86
+ labels: Dict[str, str] = field(default_factory=dict)
87
+ annotations: Dict[str, Any] = field(default_factory=dict)
88
+
89
+ def to_strategy_context(self) -> StrategyContext:
90
+ """Convert to generic strategy context."""
91
+ return StrategyContext(
92
+ target_type=self.deployment_type.value,
93
+ operation="deploy",
94
+ parameters={
95
+ "source": str(self.source),
96
+ "target": str(self.target),
97
+ "config": self.config,
98
+ "force": self.force,
99
+ "dry_run": self.dry_run,
100
+ },
101
+ constraints=self.tags,
102
+ preferences={"environment": self.environment},
103
+ )
104
+
105
+
106
+ @dataclass
107
+ class DeploymentResult:
108
+ """
109
+ Result of deployment operation.
110
+
111
+ Consolidates result patterns from multiple deployment services.
112
+ """
113
+ # Core result
114
+ success: bool
115
+ status: DeploymentStatus
116
+ message: str = ""
117
+
118
+ # Deployment details
119
+ deployment_id: Optional[str] = None
120
+ deployed_path: Optional[Path] = None
121
+ deployment_url: Optional[str] = None
122
+
123
+ # Versioning
124
+ version: Optional[str] = None
125
+ previous_version: Optional[str] = None
126
+
127
+ # Timing
128
+ started_at: Optional[datetime] = None
129
+ completed_at: Optional[datetime] = None
130
+ duration_seconds: Optional[float] = None
131
+
132
+ # Artifacts
133
+ artifacts: List[Path] = field(default_factory=list)
134
+ logs: List[str] = field(default_factory=list)
135
+
136
+ # Rollback info
137
+ rollback_available: bool = False
138
+ rollback_info: Dict[str, Any] = field(default_factory=dict)
139
+
140
+ # Metrics
141
+ metrics: Dict[str, Any] = field(default_factory=dict)
142
+
143
+ # Errors and warnings
144
+ errors: List[str] = field(default_factory=list)
145
+ warnings: List[str] = field(default_factory=list)
146
+
147
+ def to_dict(self) -> Dict[str, Any]:
148
+ """Convert to dictionary for serialization."""
149
+ return {
150
+ "success": self.success,
151
+ "status": self.status.value,
152
+ "message": self.message,
153
+ "deployment_id": self.deployment_id,
154
+ "deployed_path": str(self.deployed_path) if self.deployed_path else None,
155
+ "deployment_url": self.deployment_url,
156
+ "version": self.version,
157
+ "previous_version": self.previous_version,
158
+ "started_at": self.started_at.isoformat() if self.started_at else None,
159
+ "completed_at": self.completed_at.isoformat() if self.completed_at else None,
160
+ "duration_seconds": self.duration_seconds,
161
+ "artifacts": [str(a) for a in self.artifacts],
162
+ "logs": self.logs,
163
+ "rollback_available": self.rollback_available,
164
+ "rollback_info": self.rollback_info,
165
+ "metrics": self.metrics,
166
+ "errors": self.errors,
167
+ "warnings": self.warnings,
168
+ }
169
+
170
+
171
+ class DeploymentStrategy(BaseDeploymentStrategy):
172
+ """
173
+ Enhanced base class for deployment strategies.
174
+
175
+ This class consolidates common deployment patterns from 45+ deployment
176
+ services into a single, reusable base class.
177
+
178
+ Subclasses should implement:
179
+ - validate(): Validate deployment configuration
180
+ - prepare(): Prepare deployment artifacts
181
+ - execute(): Execute the actual deployment
182
+ - verify(): Verify deployment success
183
+ - rollback(): Rollback on failure
184
+ - get_health_status(): Check deployment health
185
+ """
186
+
187
+ def __init__(self, metadata: Optional[StrategyMetadata] = None):
188
+ """
189
+ Initialize deployment strategy.
190
+
191
+ Args:
192
+ metadata: Strategy metadata
193
+ """
194
+ super().__init__(metadata or self._create_metadata())
195
+ self._logger = get_logger(f"{__name__}.{self.__class__.__name__}")
196
+ self._current_deployment: Optional[DeploymentContext] = None
197
+ self._deployment_history: List[DeploymentResult] = []
198
+
199
+ def _create_metadata(self) -> StrategyMetadata:
200
+ """Create default metadata for strategy."""
201
+ return StrategyMetadata(
202
+ name=self.__class__.__name__,
203
+ description=self.__doc__ or "Deployment strategy",
204
+ priority=StrategyPriority.NORMAL,
205
+ supported_types=["*"],
206
+ supported_operations=["deploy", "rollback", "verify"],
207
+ )
208
+
209
+ # Override base class method
210
+ def deploy(
211
+ self,
212
+ source: Union[str, Path],
213
+ target: Union[str, Path],
214
+ config: Optional[Dict[str, Any]] = None,
215
+ ) -> Dict[str, Any]:
216
+ """
217
+ Execute deployment (implements base class abstract method).
218
+
219
+ Args:
220
+ source: Deployment source
221
+ target: Deployment target
222
+ config: Deployment configuration
223
+
224
+ Returns:
225
+ Deployment result dictionary
226
+ """
227
+ context = DeploymentContext(
228
+ source=Path(source),
229
+ target=Path(target),
230
+ deployment_type=self._detect_deployment_type(source, config),
231
+ config=config or {},
232
+ )
233
+ result = self.deploy_with_context(context)
234
+ return result.to_dict()
235
+
236
+ def deploy_with_context(self, context: DeploymentContext) -> DeploymentResult:
237
+ """
238
+ Execute deployment with full context.
239
+
240
+ This is the main entry point for deployment operations.
241
+
242
+ Args:
243
+ context: Deployment context
244
+
245
+ Returns:
246
+ Deployment result
247
+ """
248
+ self._current_deployment = context
249
+ result = DeploymentResult(
250
+ success=False,
251
+ status=DeploymentStatus.PENDING,
252
+ started_at=datetime.now(),
253
+ )
254
+
255
+ try:
256
+ # Validation phase
257
+ result.status = DeploymentStatus.VALIDATING
258
+ self._logger.info(f"Validating deployment: {context.source} -> {context.target}")
259
+
260
+ validation_errors = self.validate(context)
261
+ if validation_errors:
262
+ result.errors = validation_errors
263
+ result.message = f"Validation failed: {'; '.join(validation_errors)}"
264
+ result.status = DeploymentStatus.FAILED
265
+ return result
266
+
267
+ if context.validate_only:
268
+ result.success = True
269
+ result.status = DeploymentStatus.COMPLETED
270
+ result.message = "Validation successful"
271
+ return result
272
+
273
+ # Preparation phase
274
+ result.status = DeploymentStatus.PREPARING
275
+ self._logger.info("Preparing deployment artifacts")
276
+
277
+ artifacts = self.prepare(context)
278
+ result.artifacts = artifacts
279
+
280
+ # Execution phase
281
+ if not context.dry_run:
282
+ result.status = DeploymentStatus.DEPLOYING
283
+ self._logger.info("Executing deployment")
284
+
285
+ deployment_info = self.execute(context, artifacts)
286
+ result.deployment_id = deployment_info.get("deployment_id")
287
+ result.deployed_path = deployment_info.get("deployed_path")
288
+ result.deployment_url = deployment_info.get("deployment_url")
289
+ result.version = context.version
290
+ result.previous_version = context.previous_version
291
+
292
+ # Verification phase
293
+ result.status = DeploymentStatus.VERIFYING
294
+ self._logger.info("Verifying deployment")
295
+
296
+ if self.verify(context, deployment_info):
297
+ result.success = True
298
+ result.status = DeploymentStatus.COMPLETED
299
+ result.message = "Deployment successful"
300
+
301
+ # Prepare rollback info
302
+ result.rollback_available = True
303
+ result.rollback_info = self.prepare_rollback(deployment_info)
304
+ else:
305
+ raise Exception("Deployment verification failed")
306
+ else:
307
+ result.success = True
308
+ result.status = DeploymentStatus.COMPLETED
309
+ result.message = "Dry run completed successfully"
310
+
311
+ except Exception as e:
312
+ self._logger.error(f"Deployment failed: {str(e)}")
313
+ result.status = DeploymentStatus.FAILED
314
+ result.message = str(e)
315
+ result.errors.append(str(e))
316
+
317
+ # Attempt rollback if not dry run
318
+ if not context.dry_run and context.backup_enabled:
319
+ self._logger.info("Attempting rollback")
320
+ try:
321
+ self.rollback(context, result)
322
+ result.status = DeploymentStatus.ROLLED_BACK
323
+ result.message += " (rolled back)"
324
+ except Exception as rollback_error:
325
+ self._logger.error(f"Rollback failed: {str(rollback_error)}")
326
+ result.errors.append(f"Rollback failed: {str(rollback_error)}")
327
+
328
+ finally:
329
+ result.completed_at = datetime.now()
330
+ if result.started_at:
331
+ result.duration_seconds = (
332
+ result.completed_at - result.started_at
333
+ ).total_seconds()
334
+
335
+ # Store in history
336
+ self._deployment_history.append(result)
337
+
338
+ # Collect metrics
339
+ result.metrics = self._collect_metrics(context, result)
340
+
341
+ return result
342
+
343
+ # Abstract methods to be implemented by subclasses
344
+
345
+ @abstractmethod
346
+ def validate(self, context: DeploymentContext) -> List[str]:
347
+ """
348
+ Validate deployment configuration.
349
+
350
+ Args:
351
+ context: Deployment context
352
+
353
+ Returns:
354
+ List of validation errors (empty if valid)
355
+ """
356
+ pass
357
+
358
+ @abstractmethod
359
+ def prepare(self, context: DeploymentContext) -> List[Path]:
360
+ """
361
+ Prepare deployment artifacts.
362
+
363
+ Args:
364
+ context: Deployment context
365
+
366
+ Returns:
367
+ List of prepared artifact paths
368
+ """
369
+ pass
370
+
371
+ @abstractmethod
372
+ def execute(
373
+ self, context: DeploymentContext, artifacts: List[Path]
374
+ ) -> Dict[str, Any]:
375
+ """
376
+ Execute the deployment.
377
+
378
+ Args:
379
+ context: Deployment context
380
+ artifacts: Prepared artifacts
381
+
382
+ Returns:
383
+ Deployment information including deployment_id, deployed_path, etc.
384
+ """
385
+ pass
386
+
387
+ @abstractmethod
388
+ def verify(
389
+ self, context: DeploymentContext, deployment_info: Dict[str, Any]
390
+ ) -> bool:
391
+ """
392
+ Verify deployment success.
393
+
394
+ Args:
395
+ context: Deployment context
396
+ deployment_info: Information from execute phase
397
+
398
+ Returns:
399
+ True if deployment verified successfully
400
+ """
401
+ pass
402
+
403
+ @abstractmethod
404
+ def rollback(
405
+ self, context: DeploymentContext, result: DeploymentResult
406
+ ) -> bool:
407
+ """
408
+ Rollback failed deployment.
409
+
410
+ Args:
411
+ context: Deployment context
412
+ result: Current deployment result
413
+
414
+ Returns:
415
+ True if rollback successful
416
+ """
417
+ pass
418
+
419
+ @abstractmethod
420
+ def get_health_status(
421
+ self, deployment_info: Dict[str, Any]
422
+ ) -> Dict[str, Any]:
423
+ """
424
+ Get health status of deployment.
425
+
426
+ Args:
427
+ deployment_info: Deployment information
428
+
429
+ Returns:
430
+ Health status information
431
+ """
432
+ pass
433
+
434
+ # Helper methods
435
+
436
+ def _detect_deployment_type(
437
+ self, source: Union[str, Path], config: Optional[Dict[str, Any]]
438
+ ) -> DeploymentType:
439
+ """
440
+ Detect deployment type from source and config.
441
+
442
+ Args:
443
+ source: Deployment source
444
+ config: Deployment configuration
445
+
446
+ Returns:
447
+ Detected deployment type
448
+ """
449
+ if config and "type" in config:
450
+ type_str = config["type"].lower()
451
+ try:
452
+ return DeploymentType[type_str.upper()]
453
+ except KeyError:
454
+ pass
455
+
456
+ # Detect from source path patterns
457
+ source_path = Path(source)
458
+
459
+ if "agent" in source_path.name.lower():
460
+ return DeploymentType.AGENT
461
+ elif "config" in source_path.name.lower():
462
+ return DeploymentType.CONFIG
463
+ elif "template" in source_path.name.lower():
464
+ return DeploymentType.TEMPLATE
465
+ elif source_path.suffix in [".yaml", ".yml", ".json"]:
466
+ return DeploymentType.CONFIG
467
+ else:
468
+ return DeploymentType.RESOURCE
469
+
470
+ def _collect_metrics(
471
+ self, context: DeploymentContext, result: DeploymentResult
472
+ ) -> Dict[str, Any]:
473
+ """
474
+ Collect deployment metrics.
475
+
476
+ Args:
477
+ context: Deployment context
478
+ result: Deployment result
479
+
480
+ Returns:
481
+ Collected metrics
482
+ """
483
+ return {
484
+ "deployment_type": context.deployment_type.value,
485
+ "source_size": self._get_size(context.source) if context.source else 0,
486
+ "artifact_count": len(result.artifacts),
487
+ "artifact_total_size": sum(
488
+ self._get_size(a) for a in result.artifacts
489
+ ),
490
+ "duration_seconds": result.duration_seconds,
491
+ "error_count": len(result.errors),
492
+ "warning_count": len(result.warnings),
493
+ }
494
+
495
+ def _get_size(self, path: Union[str, Path]) -> int:
496
+ """Get size of file or directory in bytes."""
497
+ path = Path(path)
498
+ if path.is_file():
499
+ return path.stat().st_size
500
+ elif path.is_dir():
501
+ return sum(f.stat().st_size for f in path.rglob("*") if f.is_file())
502
+ return 0
503
+
504
+ # Implement base class abstract methods
505
+
506
+ def can_handle(self, context: StrategyContext) -> bool:
507
+ """Check if strategy can handle the given context."""
508
+ # Check if operation is supported
509
+ if context.operation not in self.metadata.supported_operations:
510
+ return False
511
+
512
+ # Check if target type is supported
513
+ if self.metadata.supported_types != ["*"]:
514
+ if context.target_type not in self.metadata.supported_types:
515
+ return False
516
+
517
+ return True
518
+
519
+ def validate_input(self, input_data: Any) -> List[str]:
520
+ """Validate input data for strategy."""
521
+ errors = []
522
+
523
+ if not isinstance(input_data, dict):
524
+ errors.append("Input must be a dictionary")
525
+ return errors
526
+
527
+ if "source" not in input_data:
528
+ errors.append("Source is required")
529
+
530
+ if "target" not in input_data:
531
+ errors.append("Target is required")
532
+
533
+ return errors
534
+
535
+ def prepare_rollback(self, deployment_info: Dict[str, Any]) -> Dict[str, Any]:
536
+ """Prepare rollback information."""
537
+ return {
538
+ "deployment_id": deployment_info.get("deployment_id"),
539
+ "deployed_path": str(deployment_info.get("deployed_path")),
540
+ "timestamp": datetime.now().isoformat(),
541
+ "artifacts": deployment_info.get("artifacts", []),
542
+ }
543
+
544
+ def cleanup(self, target: Union[str, Path]) -> bool:
545
+ """Clean up deployment artifacts."""
546
+ try:
547
+ target_path = Path(target)
548
+ if target_path.exists():
549
+ if target_path.is_file():
550
+ target_path.unlink()
551
+ elif target_path.is_dir():
552
+ import shutil
553
+ shutil.rmtree(target_path)
554
+ return True
555
+ except Exception as e:
556
+ self._logger.error(f"Cleanup failed: {str(e)}")
557
+ return False