claude-mpm 4.4.3__py3-none-any.whl → 4.4.5__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 (118) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/agent_loader.py +3 -2
  3. claude_mpm/agents/agent_loader_integration.py +2 -1
  4. claude_mpm/agents/async_agent_loader.py +2 -2
  5. claude_mpm/agents/base_agent_loader.py +2 -2
  6. claude_mpm/agents/frontmatter_validator.py +1 -0
  7. claude_mpm/agents/system_agent_config.py +2 -1
  8. claude_mpm/cli/commands/doctor.py +44 -5
  9. claude_mpm/cli/commands/mpm_init.py +116 -62
  10. claude_mpm/cli/parsers/configure_parser.py +3 -1
  11. claude_mpm/cli/startup_logging.py +1 -3
  12. claude_mpm/config/agent_config.py +1 -1
  13. claude_mpm/config/paths.py +2 -1
  14. claude_mpm/core/agent_name_normalizer.py +1 -0
  15. claude_mpm/core/config.py +2 -1
  16. claude_mpm/core/config_aliases.py +2 -1
  17. claude_mpm/core/file_utils.py +0 -1
  18. claude_mpm/core/framework/__init__.py +6 -6
  19. claude_mpm/core/framework/formatters/__init__.py +2 -2
  20. claude_mpm/core/framework/formatters/capability_generator.py +19 -8
  21. claude_mpm/core/framework/formatters/content_formatter.py +8 -3
  22. claude_mpm/core/framework/formatters/context_generator.py +7 -3
  23. claude_mpm/core/framework/loaders/__init__.py +3 -3
  24. claude_mpm/core/framework/loaders/agent_loader.py +7 -3
  25. claude_mpm/core/framework/loaders/file_loader.py +16 -6
  26. claude_mpm/core/framework/loaders/instruction_loader.py +16 -6
  27. claude_mpm/core/framework/loaders/packaged_loader.py +36 -12
  28. claude_mpm/core/framework/processors/__init__.py +2 -2
  29. claude_mpm/core/framework/processors/memory_processor.py +14 -6
  30. claude_mpm/core/framework/processors/metadata_processor.py +5 -5
  31. claude_mpm/core/framework/processors/template_processor.py +12 -6
  32. claude_mpm/core/framework_loader.py +44 -20
  33. claude_mpm/core/log_manager.py +2 -1
  34. claude_mpm/core/tool_access_control.py +1 -0
  35. claude_mpm/core/unified_agent_registry.py +2 -1
  36. claude_mpm/core/unified_paths.py +1 -0
  37. claude_mpm/experimental/cli_enhancements.py +1 -0
  38. claude_mpm/hooks/base_hook.py +1 -0
  39. claude_mpm/hooks/instruction_reinforcement.py +1 -0
  40. claude_mpm/hooks/kuzu_memory_hook.py +20 -13
  41. claude_mpm/hooks/validation_hooks.py +1 -1
  42. claude_mpm/scripts/mpm_doctor.py +1 -0
  43. claude_mpm/services/agents/loading/agent_profile_loader.py +1 -1
  44. claude_mpm/services/agents/loading/base_agent_manager.py +1 -1
  45. claude_mpm/services/agents/loading/framework_agent_loader.py +1 -1
  46. claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -0
  47. claude_mpm/services/agents/management/agent_management_service.py +1 -1
  48. claude_mpm/services/agents/memory/memory_categorization_service.py +0 -1
  49. claude_mpm/services/agents/memory/memory_file_service.py +6 -2
  50. claude_mpm/services/agents/memory/memory_format_service.py +0 -1
  51. claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
  52. claude_mpm/services/async_session_logger.py +1 -1
  53. claude_mpm/services/claude_session_logger.py +1 -0
  54. claude_mpm/services/core/path_resolver.py +1 -0
  55. claude_mpm/services/diagnostics/checks/__init__.py +2 -0
  56. claude_mpm/services/diagnostics/checks/installation_check.py +126 -25
  57. claude_mpm/services/diagnostics/checks/mcp_services_check.py +451 -0
  58. claude_mpm/services/diagnostics/diagnostic_runner.py +3 -0
  59. claude_mpm/services/diagnostics/doctor_reporter.py +259 -32
  60. claude_mpm/services/event_bus/direct_relay.py +2 -1
  61. claude_mpm/services/event_bus/event_bus.py +1 -0
  62. claude_mpm/services/event_bus/relay.py +3 -2
  63. claude_mpm/services/framework_claude_md_generator/content_assembler.py +1 -1
  64. claude_mpm/services/infrastructure/daemon_manager.py +1 -1
  65. claude_mpm/services/mcp_config_manager.py +301 -54
  66. claude_mpm/services/mcp_gateway/core/process_pool.py +62 -23
  67. claude_mpm/services/mcp_gateway/tools/__init__.py +6 -5
  68. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +3 -1
  69. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +16 -31
  70. claude_mpm/services/memory/cache/simple_cache.py +1 -1
  71. claude_mpm/services/project/archive_manager.py +159 -96
  72. claude_mpm/services/project/documentation_manager.py +64 -45
  73. claude_mpm/services/project/enhanced_analyzer.py +132 -89
  74. claude_mpm/services/project/project_organizer.py +225 -131
  75. claude_mpm/services/response_tracker.py +1 -1
  76. claude_mpm/services/socketio/server/eventbus_integration.py +1 -1
  77. claude_mpm/services/unified/__init__.py +1 -1
  78. claude_mpm/services/unified/analyzer_strategies/__init__.py +3 -3
  79. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +97 -53
  80. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +81 -40
  81. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +277 -178
  82. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +196 -112
  83. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +83 -49
  84. claude_mpm/services/unified/config_strategies/__init__.py +111 -126
  85. claude_mpm/services/unified/config_strategies/config_schema.py +157 -111
  86. claude_mpm/services/unified/config_strategies/context_strategy.py +91 -89
  87. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +183 -173
  88. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +160 -152
  89. claude_mpm/services/unified/config_strategies/unified_config_service.py +124 -112
  90. claude_mpm/services/unified/config_strategies/validation_strategy.py +298 -259
  91. claude_mpm/services/unified/deployment_strategies/__init__.py +7 -7
  92. claude_mpm/services/unified/deployment_strategies/base.py +24 -28
  93. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +168 -88
  94. claude_mpm/services/unified/deployment_strategies/local.py +49 -34
  95. claude_mpm/services/unified/deployment_strategies/utils.py +39 -43
  96. claude_mpm/services/unified/deployment_strategies/vercel.py +30 -24
  97. claude_mpm/services/unified/interfaces.py +0 -26
  98. claude_mpm/services/unified/migration.py +17 -40
  99. claude_mpm/services/unified/strategies.py +9 -26
  100. claude_mpm/services/unified/unified_analyzer.py +48 -44
  101. claude_mpm/services/unified/unified_config.py +21 -19
  102. claude_mpm/services/unified/unified_deployment.py +21 -26
  103. claude_mpm/storage/state_storage.py +1 -0
  104. claude_mpm/utils/agent_dependency_loader.py +18 -6
  105. claude_mpm/utils/common.py +14 -12
  106. claude_mpm/utils/database_connector.py +15 -12
  107. claude_mpm/utils/error_handler.py +1 -0
  108. claude_mpm/utils/log_cleanup.py +1 -0
  109. claude_mpm/utils/path_operations.py +1 -0
  110. claude_mpm/utils/session_logging.py +1 -1
  111. claude_mpm/utils/subprocess_utils.py +1 -0
  112. claude_mpm/validation/agent_validator.py +1 -1
  113. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/METADATA +35 -15
  114. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/RECORD +118 -117
  115. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/WHEEL +0 -0
  116. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/entry_points.txt +0 -0
  117. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/licenses/LICENSE +0 -0
  118. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.5.dist-info}/top_level.txt +0 -0
@@ -22,7 +22,12 @@ import yaml
22
22
  from claude_mpm.core.logging_utils import get_logger
23
23
  from claude_mpm.services.unified.strategies import StrategyMetadata, StrategyPriority
24
24
 
25
- from .base import DeploymentContext, DeploymentResult, DeploymentStrategy, DeploymentType
25
+ from .base import (
26
+ DeploymentContext,
27
+ DeploymentResult,
28
+ DeploymentStrategy,
29
+ DeploymentType,
30
+ )
26
31
 
27
32
 
28
33
  class LocalDeploymentStrategy(DeploymentStrategy):
@@ -83,13 +88,14 @@ class LocalDeploymentStrategy(DeploymentStrategy):
83
88
  target_parent.mkdir(parents=True, exist_ok=True)
84
89
  target_parent.rmdir() # Clean up test directory
85
90
  except PermissionError:
86
- errors.append(f"No permission to create target directory: {target_parent}")
87
- else:
88
- # Check write permissions
89
- if not target_parent.is_dir():
90
- errors.append(f"Target parent is not a directory: {target_parent}")
91
- elif not self._check_write_permission(target_parent):
92
- errors.append(f"No write permission for target: {target_parent}")
91
+ errors.append(
92
+ f"No permission to create target directory: {target_parent}"
93
+ )
94
+ # Check write permissions
95
+ elif not target_parent.is_dir():
96
+ errors.append(f"Target parent is not a directory: {target_parent}")
97
+ elif not self._check_write_permission(target_parent):
98
+ errors.append(f"No write permission for target: {target_parent}")
93
99
 
94
100
  # Validate deployment type specific requirements
95
101
  if context.deployment_type == DeploymentType.AGENT:
@@ -209,16 +215,14 @@ class LocalDeploymentStrategy(DeploymentStrategy):
209
215
  # Type-specific verification
210
216
  if context.deployment_type == DeploymentType.AGENT:
211
217
  return self._verify_agent_deployment(deployed_path, context)
212
- elif context.deployment_type == DeploymentType.CONFIG:
218
+ if context.deployment_type == DeploymentType.CONFIG:
213
219
  return self._verify_config_deployment(deployed_path, context)
214
- elif context.deployment_type == DeploymentType.TEMPLATE:
220
+ if context.deployment_type == DeploymentType.TEMPLATE:
215
221
  return self._verify_template_deployment(deployed_path, context)
216
222
 
217
223
  return True
218
224
 
219
- def rollback(
220
- self, context: DeploymentContext, result: DeploymentResult
221
- ) -> bool:
225
+ def rollback(self, context: DeploymentContext, result: DeploymentResult) -> bool:
222
226
  """
223
227
  Rollback local deployment.
224
228
 
@@ -252,12 +256,10 @@ class LocalDeploymentStrategy(DeploymentStrategy):
252
256
  return True
253
257
 
254
258
  except Exception as e:
255
- self._logger.error(f"Rollback failed: {str(e)}")
259
+ self._logger.error(f"Rollback failed: {e!s}")
256
260
  return False
257
261
 
258
- def get_health_status(
259
- self, deployment_info: Dict[str, Any]
260
- ) -> Dict[str, Any]:
262
+ def get_health_status(self, deployment_info: Dict[str, Any]) -> Dict[str, Any]:
261
263
  """
262
264
  Get health status of local deployment.
263
265
 
@@ -307,7 +309,9 @@ class LocalDeploymentStrategy(DeploymentStrategy):
307
309
 
308
310
  def _generate_deployment_id(self) -> str:
309
311
  """Generate unique deployment ID."""
310
- return f"local_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{id(self) % 10000:04d}"
312
+ return (
313
+ f"local_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{id(self) % 10000:04d}"
314
+ )
311
315
 
312
316
  def _create_backup(self, context: DeploymentContext) -> Optional[Path]:
313
317
  """Create backup of target before deployment."""
@@ -333,7 +337,7 @@ class LocalDeploymentStrategy(DeploymentStrategy):
333
337
  return backup_path
334
338
 
335
339
  except Exception as e:
336
- self._logger.warning(f"Failed to create backup: {str(e)}")
340
+ self._logger.warning(f"Failed to create backup: {e!s}")
337
341
  return None
338
342
 
339
343
  def _write_version_file(self, target_path: Path, version: str) -> None:
@@ -354,9 +358,11 @@ class LocalDeploymentStrategy(DeploymentStrategy):
354
358
  errors.append(f"Invalid agent file format: {source_path.suffix}")
355
359
  elif source_path.is_dir():
356
360
  # Check for agent definition files
357
- agent_files = list(source_path.glob("*.json")) + \
358
- list(source_path.glob("*.yaml")) + \
359
- list(source_path.glob("*.yml"))
361
+ agent_files = (
362
+ list(source_path.glob("*.json"))
363
+ + list(source_path.glob("*.yaml"))
364
+ + list(source_path.glob("*.yml"))
365
+ )
360
366
  if not agent_files:
361
367
  errors.append(f"No agent definition files found in: {source_path}")
362
368
 
@@ -397,8 +403,9 @@ class LocalDeploymentStrategy(DeploymentStrategy):
397
403
  ) -> bool:
398
404
  """Verify agent deployment."""
399
405
  # Check for valid YAML structure
400
- yaml_files = list(deployed_path.glob("*.yaml")) + \
401
- list(deployed_path.glob("*.yml"))
406
+ yaml_files = list(deployed_path.glob("*.yaml")) + list(
407
+ deployed_path.glob("*.yml")
408
+ )
402
409
 
403
410
  for yaml_file in yaml_files:
404
411
  try:
@@ -411,7 +418,7 @@ class LocalDeploymentStrategy(DeploymentStrategy):
411
418
  self._logger.error(f"Agent missing 'name' field: {yaml_file}")
412
419
  return False
413
420
  except Exception as e:
414
- self._logger.error(f"Invalid agent YAML: {yaml_file}: {str(e)}")
421
+ self._logger.error(f"Invalid agent YAML: {yaml_file}: {e!s}")
415
422
  return False
416
423
 
417
424
  return True
@@ -436,7 +443,14 @@ class LocalDeploymentStrategy(DeploymentStrategy):
436
443
 
437
444
  if source_path.is_file():
438
445
  # Validate config file format
439
- if source_path.suffix not in [".json", ".yaml", ".yml", ".toml", ".ini", ".env"]:
446
+ if source_path.suffix not in [
447
+ ".json",
448
+ ".yaml",
449
+ ".yml",
450
+ ".toml",
451
+ ".ini",
452
+ ".env",
453
+ ]:
440
454
  errors.append(f"Unsupported config format: {source_path.suffix}")
441
455
 
442
456
  return errors
@@ -496,15 +510,16 @@ class LocalDeploymentStrategy(DeploymentStrategy):
496
510
 
497
511
  # Process template with variables
498
512
  if source_path.is_file():
499
- processed = self._process_template(source_path, context.config.get("variables", {}))
513
+ processed = self._process_template(
514
+ source_path, context.config.get("variables", {})
515
+ )
500
516
  artifacts.append(processed)
501
517
  else:
502
518
  # Process all template files in directory
503
519
  for template_file in source_path.rglob("*"):
504
520
  if template_file.is_file():
505
521
  processed = self._process_template(
506
- template_file,
507
- context.config.get("variables", {})
522
+ template_file, context.config.get("variables", {})
508
523
  )
509
524
  artifacts.append(processed)
510
525
 
@@ -552,9 +567,7 @@ class LocalDeploymentStrategy(DeploymentStrategy):
552
567
 
553
568
  return True
554
569
 
555
- def _process_template(
556
- self, template_path: Path, variables: Dict[str, Any]
557
- ) -> Path:
570
+ def _process_template(self, template_path: Path, variables: Dict[str, Any]) -> Path:
558
571
  """Process template file with variables."""
559
572
  content = template_path.read_text()
560
573
 
@@ -578,7 +591,9 @@ class LocalDeploymentStrategy(DeploymentStrategy):
578
591
 
579
592
  for artifact in artifacts:
580
593
  if artifact.is_file():
581
- dest = target_path / artifact.name if target_path.is_dir() else target_path
594
+ dest = (
595
+ target_path / artifact.name if target_path.is_dir() else target_path
596
+ )
582
597
  shutil.copy2(artifact, dest)
583
598
  deployed.append(dest)
584
599
  elif artifact.is_dir():
@@ -591,4 +606,4 @@ class LocalDeploymentStrategy(DeploymentStrategy):
591
606
 
592
607
  self._logger.info(f"Deployed resource: {deployed[-1]}")
593
608
 
594
- return deployed
609
+ return deployed
@@ -16,7 +16,6 @@ This module reduces ~5000 LOC of duplicated utility functions across:
16
16
 
17
17
  import hashlib
18
18
  import json
19
- import os
20
19
  import shutil
21
20
  import subprocess
22
21
  import tempfile
@@ -24,8 +23,6 @@ from datetime import datetime
24
23
  from pathlib import Path
25
24
  from typing import Any, Dict, List, Optional, Tuple, Union
26
25
 
27
- import yaml
28
-
29
26
  from claude_mpm.core.logging_utils import get_logger
30
27
 
31
28
  logger = get_logger(__name__)
@@ -34,6 +31,7 @@ logger = get_logger(__name__)
34
31
  # Validation Utilities
35
32
  # ====================
36
33
 
34
+
37
35
  def validate_deployment_config(config: Dict[str, Any]) -> List[str]:
38
36
  """
39
37
  Validate deployment configuration.
@@ -57,8 +55,16 @@ def validate_deployment_config(config: Dict[str, Any]) -> List[str]:
57
55
  # Type validation
58
56
  if "type" in config:
59
57
  valid_types = [
60
- "local", "vercel", "railway", "aws", "docker", "git",
61
- "agent", "config", "template", "resource"
58
+ "local",
59
+ "vercel",
60
+ "railway",
61
+ "aws",
62
+ "docker",
63
+ "git",
64
+ "agent",
65
+ "config",
66
+ "template",
67
+ "resource",
62
68
  ]
63
69
  if config["type"] not in valid_types:
64
70
  errors.append(f"Invalid deployment type: {config['type']}")
@@ -133,10 +139,11 @@ def validate_path_security(path: Path, base_path: Path) -> bool:
133
139
  # Artifact Preparation
134
140
  # ====================
135
141
 
142
+
136
143
  def prepare_deployment_artifact(
137
144
  source: Union[str, Path],
138
145
  artifact_type: str = "auto",
139
- config: Optional[Dict[str, Any]] = None
146
+ config: Optional[Dict[str, Any]] = None,
140
147
  ) -> Tuple[Path, Dict[str, Any]]:
141
148
  """
142
149
  Prepare deployment artifact from source.
@@ -230,10 +237,11 @@ def create_tar_artifact(source: Path, output_dir: Path) -> Path:
230
237
  # Health Check Utilities
231
238
  # ======================
232
239
 
240
+
233
241
  def verify_deployment_health(
234
242
  deployment_type: str,
235
243
  deployment_info: Dict[str, Any],
236
- checks: Optional[List[str]] = None
244
+ checks: Optional[List[str]] = None,
237
245
  ) -> Dict[str, Any]:
238
246
  """
239
247
  Perform health checks on deployment.
@@ -275,8 +283,7 @@ def verify_deployment_health(
275
283
  if "integrity" in checks:
276
284
  if "checksum" in deployment_info:
277
285
  health["checks"]["integrity"] = verify_checksum(
278
- deployment_info.get("deployed_path"),
279
- deployment_info["checksum"]
286
+ deployment_info.get("deployed_path"), deployment_info["checksum"]
280
287
  )
281
288
 
282
289
  # Service-specific checks
@@ -285,9 +292,7 @@ def verify_deployment_health(
285
292
  deployment_info.get("container_id")
286
293
  )
287
294
  elif deployment_type == "aws":
288
- health["checks"]["aws_status"] = check_aws_deployment(
289
- deployment_info
290
- )
295
+ health["checks"]["aws_status"] = check_aws_deployment(deployment_info)
291
296
 
292
297
  # Determine overall status
293
298
  if all(health["checks"].values()):
@@ -308,6 +313,7 @@ def check_url_accessibility(url: str, timeout: int = 10) -> bool:
308
313
  """Check if URL is accessible."""
309
314
  try:
310
315
  import urllib.request
316
+
311
317
  with urllib.request.urlopen(url, timeout=timeout) as response:
312
318
  return response.status < 400
313
319
  except Exception:
@@ -340,10 +346,11 @@ def check_aws_deployment(deployment_info: Dict[str, Any]) -> bool:
340
346
  # Rollback Utilities
341
347
  # ==================
342
348
 
349
+
343
350
  def rollback_deployment(
344
351
  deployment_type: str,
345
352
  deployment_info: Dict[str, Any],
346
- backup_info: Optional[Dict[str, Any]] = None
353
+ backup_info: Optional[Dict[str, Any]] = None,
347
354
  ) -> bool:
348
355
  """
349
356
  Rollback deployment to previous state.
@@ -361,22 +368,20 @@ def rollback_deployment(
361
368
  try:
362
369
  if deployment_type == "local":
363
370
  return rollback_local_deployment(deployment_info, backup_info)
364
- elif deployment_type == "docker":
371
+ if deployment_type == "docker":
365
372
  return rollback_docker_deployment(deployment_info)
366
- elif deployment_type == "git":
373
+ if deployment_type == "git":
367
374
  return rollback_git_deployment(deployment_info)
368
- else:
369
- logger.warning(f"No rollback strategy for type: {deployment_type}")
370
- return False
375
+ logger.warning(f"No rollback strategy for type: {deployment_type}")
376
+ return False
371
377
 
372
378
  except Exception as e:
373
- logger.error(f"Rollback failed: {str(e)}")
379
+ logger.error(f"Rollback failed: {e!s}")
374
380
  return False
375
381
 
376
382
 
377
383
  def rollback_local_deployment(
378
- deployment_info: Dict[str, Any],
379
- backup_info: Optional[Dict[str, Any]] = None
384
+ deployment_info: Dict[str, Any], backup_info: Optional[Dict[str, Any]] = None
380
385
  ) -> bool:
381
386
  """Rollback local filesystem deployment."""
382
387
  deployed_path = Path(deployment_info.get("deployed_path", ""))
@@ -413,8 +418,7 @@ def rollback_docker_deployment(deployment_info: Dict[str, Any]) -> bool:
413
418
  # Restore previous container if specified
414
419
  if "previous_container" in deployment_info:
415
420
  subprocess.run(
416
- ["docker", "start", deployment_info["previous_container"]],
417
- check=True
421
+ ["docker", "start", deployment_info["previous_container"]], check=True
418
422
  )
419
423
 
420
424
  return True
@@ -426,11 +430,7 @@ def rollback_git_deployment(deployment_info: Dict[str, Any]) -> bool:
426
430
  previous_commit = deployment_info.get("previous_commit")
427
431
 
428
432
  if repo_path.exists() and previous_commit:
429
- subprocess.run(
430
- ["git", "checkout", previous_commit],
431
- cwd=repo_path,
432
- check=True
433
- )
433
+ subprocess.run(["git", "checkout", previous_commit], cwd=repo_path, check=True)
434
434
  return True
435
435
 
436
436
  return False
@@ -439,6 +439,7 @@ def rollback_git_deployment(deployment_info: Dict[str, Any]) -> bool:
439
439
  # Version Management
440
440
  # ==================
441
441
 
442
+
442
443
  def get_version_info(path: Union[str, Path]) -> Dict[str, Any]:
443
444
  """
444
445
  Extract version information from deployment.
@@ -474,6 +475,7 @@ def get_version_info(path: Union[str, Path]) -> Dict[str, Any]:
474
475
  elif version_file in ["setup.py", "pyproject.toml"]:
475
476
  # Simple regex extraction
476
477
  import re
478
+
477
479
  content = file_path.read_text()
478
480
  match = re.search(r'version\s*=\s*["\'](.*?)["\']', content)
479
481
  if match:
@@ -491,9 +493,7 @@ def get_version_info(path: Union[str, Path]) -> Dict[str, Any]:
491
493
 
492
494
 
493
495
  def update_version(
494
- path: Union[str, Path],
495
- new_version: str,
496
- create_backup: bool = True
496
+ path: Union[str, Path], new_version: str, create_backup: bool = True
497
497
  ) -> bool:
498
498
  """
499
499
  Update version in deployment.
@@ -520,13 +520,14 @@ def update_version(
520
520
  return True
521
521
 
522
522
  except Exception as e:
523
- logger.error(f"Failed to update version: {str(e)}")
523
+ logger.error(f"Failed to update version: {e!s}")
524
524
  return False
525
525
 
526
526
 
527
527
  # Checksum and Integrity
528
528
  # ======================
529
529
 
530
+
530
531
  def calculate_checksum(path: Union[str, Path], algorithm: str = "sha256") -> str:
531
532
  """
532
533
  Calculate checksum of file or directory.
@@ -558,9 +559,7 @@ def calculate_checksum(path: Union[str, Path], algorithm: str = "sha256") -> str
558
559
 
559
560
 
560
561
  def verify_checksum(
561
- path: Union[str, Path],
562
- expected_checksum: str,
563
- algorithm: str = "sha256"
562
+ path: Union[str, Path], expected_checksum: str, algorithm: str = "sha256"
564
563
  ) -> bool:
565
564
  """
566
565
  Verify checksum of file or directory.
@@ -594,19 +593,19 @@ def get_size(path: Union[str, Path]) -> int:
594
593
 
595
594
  if path.is_file():
596
595
  return path.stat().st_size
597
- elif path.is_dir():
596
+ if path.is_dir():
598
597
  total_size = 0
599
598
  for file_path in path.rglob("*"):
600
599
  if file_path.is_file():
601
600
  total_size += file_path.stat().st_size
602
601
  return total_size
603
- else:
604
- return 0
602
+ return 0
605
603
 
606
604
 
607
605
  # Environment Management
608
606
  # =====================
609
607
 
608
+
610
609
  def load_env_file(env_file: Union[str, Path]) -> Dict[str, str]:
611
610
  """
612
611
  Load environment variables from file.
@@ -650,10 +649,7 @@ def merge_environments(*env_dicts: Dict[str, str]) -> Dict[str, str]:
650
649
  return merged
651
650
 
652
651
 
653
- def export_env_to_file(
654
- env_vars: Dict[str, str],
655
- output_file: Union[str, Path]
656
- ) -> None:
652
+ def export_env_to_file(env_vars: Dict[str, str], output_file: Union[str, Path]) -> None:
657
653
  """
658
654
  Export environment variables to file.
659
655
 
@@ -669,4 +665,4 @@ def export_env_to_file(
669
665
  # Escape special characters in value
670
666
  if " " in value or '"' in value:
671
667
  value = f'"{value}"'
672
- f.write(f"{key}={value}\n")
668
+ f.write(f"{key}={value}\n")
@@ -16,7 +16,11 @@ from typing import Any, Dict, List, Optional
16
16
  from claude_mpm.core.logging_utils import get_logger
17
17
  from claude_mpm.services.unified.strategies import StrategyMetadata, StrategyPriority
18
18
 
19
- from .base import DeploymentContext, DeploymentResult, DeploymentStrategy, DeploymentType
19
+ from .base import (
20
+ DeploymentContext,
21
+ DeploymentResult,
22
+ DeploymentStrategy,
23
+ )
20
24
 
21
25
 
22
26
  class VercelDeploymentStrategy(DeploymentStrategy):
@@ -117,11 +121,13 @@ class VercelDeploymentStrategy(DeploymentStrategy):
117
121
  # Single file deployment (e.g., serverless function)
118
122
  deploy_file = deploy_dir / source_path.name
119
123
  import shutil
124
+
120
125
  shutil.copy2(source_path, deploy_file)
121
126
  artifacts.append(deploy_file)
122
127
  else:
123
128
  # Directory deployment
124
129
  import shutil
130
+
125
131
  shutil.copytree(source_path, deploy_dir / "app", dirs_exist_ok=True)
126
132
  artifacts.append(deploy_dir / "app")
127
133
 
@@ -200,8 +206,7 @@ class VercelDeploymentStrategy(DeploymentStrategy):
200
206
  "stdout": result.stdout,
201
207
  "timestamp": datetime.now().isoformat(),
202
208
  }
203
- else:
204
- raise Exception("Could not parse deployment URL from Vercel output")
209
+ raise Exception("Could not parse deployment URL from Vercel output")
205
210
 
206
211
  except subprocess.CalledProcessError as e:
207
212
  self._logger.error(f"Vercel deployment failed: {e.stderr}")
@@ -235,18 +240,15 @@ class VercelDeploymentStrategy(DeploymentStrategy):
235
240
  if response.status == 200:
236
241
  self._logger.info(f"Deployment verified: {deployment_url}")
237
242
  return True
238
- else:
239
- self._logger.error(f"Deployment returned status: {response.status}")
240
- return False
243
+ self._logger.error(f"Deployment returned status: {response.status}")
244
+ return False
241
245
 
242
246
  except Exception as e:
243
- self._logger.error(f"Failed to verify deployment: {str(e)}")
247
+ self._logger.error(f"Failed to verify deployment: {e!s}")
244
248
  # May still be building, check via CLI
245
249
  return self._check_deployment_status(deployment_info.get("deployment_id"))
246
250
 
247
- def rollback(
248
- self, context: DeploymentContext, result: DeploymentResult
249
- ) -> bool:
251
+ def rollback(self, context: DeploymentContext, result: DeploymentResult) -> bool:
250
252
  """
251
253
  Rollback Vercel deployment.
252
254
 
@@ -280,23 +282,20 @@ class VercelDeploymentStrategy(DeploymentStrategy):
280
282
  check=True,
281
283
  )
282
284
 
283
- self._logger.info(f"Rolled back to previous deployment")
285
+ self._logger.info("Rolled back to previous deployment")
284
286
  return True
285
287
 
286
- else:
287
- self._logger.warning(
288
- "No previous deployment ID available for rollback. "
289
- "Manual rollback required via Vercel dashboard."
290
- )
291
- return False
288
+ self._logger.warning(
289
+ "No previous deployment ID available for rollback. "
290
+ "Manual rollback required via Vercel dashboard."
291
+ )
292
+ return False
292
293
 
293
294
  except Exception as e:
294
- self._logger.error(f"Rollback failed: {str(e)}")
295
+ self._logger.error(f"Rollback failed: {e!s}")
295
296
  return False
296
297
 
297
- def get_health_status(
298
- self, deployment_info: Dict[str, Any]
299
- ) -> Dict[str, Any]:
298
+ def get_health_status(self, deployment_info: Dict[str, Any]) -> Dict[str, Any]:
300
299
  """
301
300
  Get health status of Vercel deployment.
302
301
 
@@ -325,7 +324,9 @@ class VercelDeploymentStrategy(DeploymentStrategy):
325
324
  # Check main deployment URL
326
325
  with urllib.request.urlopen(deployment_url) as response:
327
326
  health["checks"]["main_url"] = response.status == 200
328
- health["response_time_ms"] = response.info().get("X-Vercel-Trace", "N/A")
327
+ health["response_time_ms"] = response.info().get(
328
+ "X-Vercel-Trace", "N/A"
329
+ )
329
330
 
330
331
  # Check functions if configured
331
332
  if deployment_info.get("functions"):
@@ -333,7 +334,9 @@ class VercelDeploymentStrategy(DeploymentStrategy):
333
334
  func_url = f"{deployment_url}/api/{func_name}"
334
335
  try:
335
336
  with urllib.request.urlopen(func_url) as response:
336
- health["checks"][f"function_{func_name}"] = response.status < 500
337
+ health["checks"][f"function_{func_name}"] = (
338
+ response.status < 500
339
+ )
337
340
  except:
338
341
  health["checks"][f"function_{func_name}"] = False
339
342
 
@@ -443,6 +446,7 @@ class VercelDeploymentStrategy(DeploymentStrategy):
443
446
  if "https://" in line:
444
447
  # Extract URL
445
448
  import re
449
+
446
450
  url_match = re.search(r"https://[^\s]+", line)
447
451
  if url_match:
448
452
  return url_match.group(0)
@@ -468,4 +472,6 @@ class VercelDeploymentStrategy(DeploymentStrategy):
468
472
 
469
473
  def _generate_deployment_id(self) -> str:
470
474
  """Generate unique deployment ID."""
471
- return f"vercel_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{id(self) % 10000:04d}"
475
+ return (
476
+ f"vercel_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{id(self) % 10000:04d}"
477
+ )