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,471 @@
1
+ """
2
+ Vercel Deployment Strategy
3
+ ==========================
4
+
5
+ Handles deployment to Vercel platform for serverless applications.
6
+ Consolidates Vercel deployment patterns from multiple services.
7
+ """
8
+
9
+ import json
10
+ import subprocess
11
+ import tempfile
12
+ from datetime import datetime
13
+ from pathlib import Path
14
+ from typing import Any, Dict, List, Optional
15
+
16
+ from claude_mpm.core.logging_utils import get_logger
17
+ from claude_mpm.services.unified.strategies import StrategyMetadata, StrategyPriority
18
+
19
+ from .base import DeploymentContext, DeploymentResult, DeploymentStrategy, DeploymentType
20
+
21
+
22
+ class VercelDeploymentStrategy(DeploymentStrategy):
23
+ """
24
+ Strategy for Vercel platform deployments.
25
+
26
+ Handles deployment of serverless functions, static sites, and
27
+ full-stack applications to Vercel.
28
+
29
+ Features:
30
+ - Serverless function deployment
31
+ - Static site deployment
32
+ - Environment variable management
33
+ - Custom domain configuration
34
+ - Deployment previews
35
+ - Production deployments
36
+ - Rollback to previous deployments
37
+ """
38
+
39
+ def __init__(self):
40
+ """Initialize Vercel deployment strategy."""
41
+ metadata = StrategyMetadata(
42
+ name="VercelDeploymentStrategy",
43
+ description="Deploy to Vercel serverless platform",
44
+ supported_types=["application", "service", "agent", "*"],
45
+ supported_operations=["deploy", "rollback", "verify", "promote"],
46
+ priority=StrategyPriority.NORMAL,
47
+ tags={"vercel", "serverless", "cloud", "edge"},
48
+ )
49
+ super().__init__(metadata)
50
+ self._logger = get_logger(f"{__name__}.VercelDeploymentStrategy")
51
+ self._deployment_urls: Dict[str, str] = {}
52
+
53
+ def validate(self, context: DeploymentContext) -> List[str]:
54
+ """
55
+ Validate Vercel deployment configuration.
56
+
57
+ Args:
58
+ context: Deployment context
59
+
60
+ Returns:
61
+ List of validation errors
62
+ """
63
+ errors = []
64
+
65
+ # Check Vercel CLI is available
66
+ if not self._check_vercel_cli():
67
+ errors.append("Vercel CLI not found. Install with: npm i -g vercel")
68
+
69
+ # Check authentication
70
+ if not self._check_vercel_auth():
71
+ errors.append("Not authenticated with Vercel. Run: vercel login")
72
+
73
+ # Validate source
74
+ source_path = Path(context.source)
75
+ if not source_path.exists():
76
+ errors.append(f"Source does not exist: {source_path}")
77
+
78
+ # Check for Vercel configuration
79
+ vercel_json = source_path / "vercel.json"
80
+ if not vercel_json.exists():
81
+ self._logger.warning("No vercel.json found, using defaults")
82
+
83
+ # Validate required config
84
+ config = context.config
85
+
86
+ # Check project name
87
+ if not config.get("project_name"):
88
+ if not vercel_json.exists():
89
+ errors.append("Project name required when vercel.json is missing")
90
+
91
+ # Validate environment variables
92
+ env_vars = config.get("env", {})
93
+ for key, value in env_vars.items():
94
+ if not isinstance(value, (str, int, bool)):
95
+ errors.append(f"Invalid env var type for {key}: {type(value)}")
96
+
97
+ return errors
98
+
99
+ def prepare(self, context: DeploymentContext) -> List[Path]:
100
+ """
101
+ Prepare Vercel deployment artifacts.
102
+
103
+ Args:
104
+ context: Deployment context
105
+
106
+ Returns:
107
+ List of prepared artifact paths
108
+ """
109
+ artifacts = []
110
+ source_path = Path(context.source)
111
+
112
+ # Create deployment directory
113
+ deploy_dir = Path(tempfile.mkdtemp(prefix="vercel_deploy_"))
114
+
115
+ # Copy source to deployment directory
116
+ if source_path.is_file():
117
+ # Single file deployment (e.g., serverless function)
118
+ deploy_file = deploy_dir / source_path.name
119
+ import shutil
120
+ shutil.copy2(source_path, deploy_file)
121
+ artifacts.append(deploy_file)
122
+ else:
123
+ # Directory deployment
124
+ import shutil
125
+ shutil.copytree(source_path, deploy_dir / "app", dirs_exist_ok=True)
126
+ artifacts.append(deploy_dir / "app")
127
+
128
+ # Create/update vercel.json if needed
129
+ vercel_config = self._prepare_vercel_config(context, deploy_dir)
130
+ if vercel_config:
131
+ artifacts.append(vercel_config)
132
+
133
+ # Prepare environment file
134
+ env_file = self._prepare_env_file(context, deploy_dir)
135
+ if env_file:
136
+ artifacts.append(env_file)
137
+
138
+ return artifacts
139
+
140
+ def execute(
141
+ self, context: DeploymentContext, artifacts: List[Path]
142
+ ) -> Dict[str, Any]:
143
+ """
144
+ Execute Vercel deployment.
145
+
146
+ Args:
147
+ context: Deployment context
148
+ artifacts: Prepared artifacts
149
+
150
+ Returns:
151
+ Deployment information
152
+ """
153
+ deployment_id = self._generate_deployment_id()
154
+
155
+ # Find deployment directory
156
+ deploy_dir = artifacts[0].parent if artifacts else Path(tempfile.gettempdir())
157
+
158
+ # Build Vercel command
159
+ cmd = ["vercel"]
160
+
161
+ # Add project name if specified
162
+ if context.config.get("project_name"):
163
+ cmd.extend(["--name", context.config["project_name"]])
164
+
165
+ # Production deployment or preview
166
+ if context.config.get("production", False):
167
+ cmd.append("--prod")
168
+
169
+ # Skip confirmation
170
+ cmd.append("--yes")
171
+
172
+ # Add token if provided
173
+ if context.config.get("token"):
174
+ cmd.extend(["--token", context.config["token"]])
175
+
176
+ # Execute deployment
177
+ self._logger.info(f"Deploying to Vercel: {' '.join(cmd)}")
178
+
179
+ try:
180
+ result = subprocess.run(
181
+ cmd,
182
+ cwd=deploy_dir,
183
+ capture_output=True,
184
+ text=True,
185
+ check=True,
186
+ )
187
+
188
+ # Parse deployment URL from output
189
+ deployment_url = self._parse_deployment_url(result.stdout)
190
+
191
+ if deployment_url:
192
+ self._deployment_urls[deployment_id] = deployment_url
193
+ self._logger.info(f"Deployment successful: {deployment_url}")
194
+
195
+ return {
196
+ "deployment_id": deployment_id,
197
+ "deployment_url": deployment_url,
198
+ "deployed_path": deploy_dir,
199
+ "production": context.config.get("production", False),
200
+ "stdout": result.stdout,
201
+ "timestamp": datetime.now().isoformat(),
202
+ }
203
+ else:
204
+ raise Exception("Could not parse deployment URL from Vercel output")
205
+
206
+ except subprocess.CalledProcessError as e:
207
+ self._logger.error(f"Vercel deployment failed: {e.stderr}")
208
+ raise Exception(f"Deployment failed: {e.stderr}")
209
+
210
+ def verify(
211
+ self, context: DeploymentContext, deployment_info: Dict[str, Any]
212
+ ) -> bool:
213
+ """
214
+ Verify Vercel deployment success.
215
+
216
+ Args:
217
+ context: Deployment context
218
+ deployment_info: Deployment information
219
+
220
+ Returns:
221
+ True if deployment verified
222
+ """
223
+ deployment_url = deployment_info.get("deployment_url")
224
+
225
+ if not deployment_url:
226
+ self._logger.error("No deployment URL to verify")
227
+ return False
228
+
229
+ # Check deployment status via API or HTTP
230
+ try:
231
+ import urllib.request
232
+
233
+ # Try to access the deployment
234
+ with urllib.request.urlopen(deployment_url) as response:
235
+ if response.status == 200:
236
+ self._logger.info(f"Deployment verified: {deployment_url}")
237
+ return True
238
+ else:
239
+ self._logger.error(f"Deployment returned status: {response.status}")
240
+ return False
241
+
242
+ except Exception as e:
243
+ self._logger.error(f"Failed to verify deployment: {str(e)}")
244
+ # May still be building, check via CLI
245
+ return self._check_deployment_status(deployment_info.get("deployment_id"))
246
+
247
+ def rollback(
248
+ self, context: DeploymentContext, result: DeploymentResult
249
+ ) -> bool:
250
+ """
251
+ Rollback Vercel deployment.
252
+
253
+ Args:
254
+ context: Deployment context
255
+ result: Current deployment result
256
+
257
+ Returns:
258
+ True if rollback successful
259
+ """
260
+ try:
261
+ # Vercel doesn't support direct rollback via CLI
262
+ # Instead, we can promote a previous deployment
263
+
264
+ if context.config.get("previous_deployment_id"):
265
+ # Promote previous deployment
266
+ cmd = [
267
+ "vercel",
268
+ "promote",
269
+ context.config["previous_deployment_id"],
270
+ "--yes",
271
+ ]
272
+
273
+ if context.config.get("token"):
274
+ cmd.extend(["--token", context.config["token"]])
275
+
276
+ result = subprocess.run(
277
+ cmd,
278
+ capture_output=True,
279
+ text=True,
280
+ check=True,
281
+ )
282
+
283
+ self._logger.info(f"Rolled back to previous deployment")
284
+ return True
285
+
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
292
+
293
+ except Exception as e:
294
+ self._logger.error(f"Rollback failed: {str(e)}")
295
+ return False
296
+
297
+ def get_health_status(
298
+ self, deployment_info: Dict[str, Any]
299
+ ) -> Dict[str, Any]:
300
+ """
301
+ Get health status of Vercel deployment.
302
+
303
+ Args:
304
+ deployment_info: Deployment information
305
+
306
+ Returns:
307
+ Health status information
308
+ """
309
+ deployment_url = deployment_info.get("deployment_url")
310
+
311
+ health = {
312
+ "status": "unknown",
313
+ "deployment_url": deployment_url,
314
+ "checks": {},
315
+ }
316
+
317
+ if not deployment_url:
318
+ health["status"] = "unhealthy"
319
+ health["error"] = "No deployment URL"
320
+ return health
321
+
322
+ try:
323
+ import urllib.request
324
+
325
+ # Check main deployment URL
326
+ with urllib.request.urlopen(deployment_url) as response:
327
+ health["checks"]["main_url"] = response.status == 200
328
+ health["response_time_ms"] = response.info().get("X-Vercel-Trace", "N/A")
329
+
330
+ # Check functions if configured
331
+ if deployment_info.get("functions"):
332
+ for func_name in deployment_info["functions"]:
333
+ func_url = f"{deployment_url}/api/{func_name}"
334
+ try:
335
+ with urllib.request.urlopen(func_url) as response:
336
+ health["checks"][f"function_{func_name}"] = response.status < 500
337
+ except:
338
+ health["checks"][f"function_{func_name}"] = False
339
+
340
+ # Determine overall status
341
+ if all(health["checks"].values()):
342
+ health["status"] = "healthy"
343
+ elif any(health["checks"].values()):
344
+ health["status"] = "degraded"
345
+ else:
346
+ health["status"] = "unhealthy"
347
+
348
+ except Exception as e:
349
+ health["status"] = "unhealthy"
350
+ health["error"] = str(e)
351
+
352
+ return health
353
+
354
+ # Private helper methods
355
+
356
+ def _check_vercel_cli(self) -> bool:
357
+ """Check if Vercel CLI is installed."""
358
+ try:
359
+ result = subprocess.run(
360
+ ["vercel", "--version"],
361
+ capture_output=True,
362
+ text=True,
363
+ check=True,
364
+ )
365
+ return True
366
+ except (subprocess.CalledProcessError, FileNotFoundError):
367
+ return False
368
+
369
+ def _check_vercel_auth(self) -> bool:
370
+ """Check if authenticated with Vercel."""
371
+ try:
372
+ result = subprocess.run(
373
+ ["vercel", "whoami"],
374
+ capture_output=True,
375
+ text=True,
376
+ check=True,
377
+ )
378
+ return True
379
+ except subprocess.CalledProcessError:
380
+ return False
381
+
382
+ def _prepare_vercel_config(
383
+ self, context: DeploymentContext, deploy_dir: Path
384
+ ) -> Optional[Path]:
385
+ """Prepare vercel.json configuration."""
386
+ config = context.config
387
+ vercel_config = {}
388
+
389
+ # Add project settings
390
+ if config.get("project_name"):
391
+ vercel_config["name"] = config["project_name"]
392
+
393
+ # Add build settings
394
+ if config.get("build_command"):
395
+ vercel_config["buildCommand"] = config["build_command"]
396
+
397
+ if config.get("output_directory"):
398
+ vercel_config["outputDirectory"] = config["output_directory"]
399
+
400
+ # Add functions configuration
401
+ if config.get("functions"):
402
+ vercel_config["functions"] = config["functions"]
403
+
404
+ # Add routes/rewrites
405
+ if config.get("rewrites"):
406
+ vercel_config["rewrites"] = config["rewrites"]
407
+
408
+ if config.get("redirects"):
409
+ vercel_config["redirects"] = config["redirects"]
410
+
411
+ # Add environment configuration
412
+ if config.get("env"):
413
+ vercel_config["env"] = config["env"]
414
+
415
+ if vercel_config:
416
+ config_path = deploy_dir / "vercel.json"
417
+ with open(config_path, "w") as f:
418
+ json.dump(vercel_config, f, indent=2)
419
+ return config_path
420
+
421
+ return None
422
+
423
+ def _prepare_env_file(
424
+ self, context: DeploymentContext, deploy_dir: Path
425
+ ) -> Optional[Path]:
426
+ """Prepare environment variables file."""
427
+ env_vars = context.config.get("env", {})
428
+
429
+ if env_vars:
430
+ env_file = deploy_dir / ".env"
431
+ with open(env_file, "w") as f:
432
+ for key, value in env_vars.items():
433
+ f.write(f"{key}={value}\n")
434
+ return env_file
435
+
436
+ return None
437
+
438
+ def _parse_deployment_url(self, output: str) -> Optional[str]:
439
+ """Parse deployment URL from Vercel output."""
440
+ # Look for URL patterns in output
441
+ lines = output.split("\n")
442
+ for line in lines:
443
+ if "https://" in line:
444
+ # Extract URL
445
+ import re
446
+ url_match = re.search(r"https://[^\s]+", line)
447
+ if url_match:
448
+ return url_match.group(0)
449
+
450
+ return None
451
+
452
+ def _check_deployment_status(self, deployment_id: str) -> bool:
453
+ """Check deployment status via Vercel CLI."""
454
+ try:
455
+ cmd = ["vercel", "inspect", deployment_id]
456
+ result = subprocess.run(
457
+ cmd,
458
+ capture_output=True,
459
+ text=True,
460
+ check=True,
461
+ )
462
+
463
+ # Check for ready state in output
464
+ return "State: READY" in result.stdout or "ready" in result.stdout.lower()
465
+
466
+ except subprocess.CalledProcessError:
467
+ return False
468
+
469
+ def _generate_deployment_id(self) -> str:
470
+ """Generate unique deployment ID."""
471
+ return f"vercel_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{id(self) % 10000:04d}"