claude-mpm 4.1.4__py3-none-any.whl → 4.1.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 (41) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/cli/commands/tickets.py +365 -784
  3. claude_mpm/core/output_style_manager.py +24 -0
  4. claude_mpm/core/unified_agent_registry.py +46 -15
  5. claude_mpm/services/agents/deployment/agent_discovery_service.py +12 -3
  6. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +172 -233
  7. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +575 -0
  8. claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
  9. claude_mpm/services/agents/deployment/agent_record_service.py +419 -0
  10. claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
  11. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +4 -2
  12. claude_mpm/services/infrastructure/__init__.py +31 -5
  13. claude_mpm/services/infrastructure/monitoring/__init__.py +43 -0
  14. claude_mpm/services/infrastructure/monitoring/aggregator.py +437 -0
  15. claude_mpm/services/infrastructure/monitoring/base.py +130 -0
  16. claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
  17. claude_mpm/services/infrastructure/monitoring/network.py +218 -0
  18. claude_mpm/services/infrastructure/monitoring/process.py +342 -0
  19. claude_mpm/services/infrastructure/monitoring/resources.py +243 -0
  20. claude_mpm/services/infrastructure/monitoring/service.py +367 -0
  21. claude_mpm/services/infrastructure/monitoring.py +67 -1030
  22. claude_mpm/services/project/analyzer.py +13 -4
  23. claude_mpm/services/project/analyzer_refactored.py +450 -0
  24. claude_mpm/services/project/analyzer_v2.py +566 -0
  25. claude_mpm/services/project/architecture_analyzer.py +461 -0
  26. claude_mpm/services/project/dependency_analyzer.py +462 -0
  27. claude_mpm/services/project/language_analyzer.py +265 -0
  28. claude_mpm/services/project/metrics_collector.py +410 -0
  29. claude_mpm/services/ticket_manager.py +5 -1
  30. claude_mpm/services/ticket_services/__init__.py +26 -0
  31. claude_mpm/services/ticket_services/crud_service.py +328 -0
  32. claude_mpm/services/ticket_services/formatter_service.py +290 -0
  33. claude_mpm/services/ticket_services/search_service.py +324 -0
  34. claude_mpm/services/ticket_services/validation_service.py +303 -0
  35. claude_mpm/services/ticket_services/workflow_service.py +244 -0
  36. {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/METADATA +1 -1
  37. {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/RECORD +41 -17
  38. {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/WHEEL +0 -0
  39. {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/entry_points.txt +0 -0
  40. {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/licenses/LICENSE +0 -0
  41. {claude_mpm-4.1.4.dist-info → claude_mpm-4.1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,461 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Architecture Analyzer Service
4
+ =============================
5
+
6
+ WHY: Separates architectural pattern detection from the main analyzer to follow
7
+ single responsibility principle. Identifies project architecture, structure
8
+ patterns, and organization principles.
9
+
10
+ DECISION: Create a focused service for architecture analysis that can
11
+ identify patterns like MVC, microservices, layered architecture, etc.
12
+ """
13
+
14
+ import logging
15
+ import re
16
+ from dataclasses import asdict, dataclass
17
+ from pathlib import Path
18
+ from typing import Dict, List
19
+
20
+
21
+ @dataclass
22
+ class ArchitectureInfo:
23
+ """Container for architecture analysis results."""
24
+
25
+ architecture_type: str
26
+ patterns_detected: List[str]
27
+ main_modules: List[str]
28
+ key_directories: List[str]
29
+ entry_points: List[str]
30
+ api_patterns: List[str]
31
+ configuration_patterns: List[str]
32
+ project_terminology: List[str]
33
+
34
+ def to_dict(self) -> Dict:
35
+ """Convert to dictionary."""
36
+ return asdict(self)
37
+
38
+
39
+ class ArchitectureAnalyzerService:
40
+ """Analyzes project architecture and structural patterns.
41
+
42
+ WHY: Understanding project architecture helps agents work within
43
+ established patterns and maintain architectural consistency.
44
+ """
45
+
46
+ # Common architectural directories
47
+ ARCHITECTURE_INDICATORS = {
48
+ "mvc": ["models", "views", "controllers"],
49
+ "mvvm": ["models", "views", "viewmodels"],
50
+ "layered": ["presentation", "business", "data", "domain"],
51
+ "hexagonal": ["domain", "application", "infrastructure", "adapters"],
52
+ "clean": ["entities", "usecases", "interfaces", "frameworks"],
53
+ "microservices": ["services", "api-gateway", "service-discovery"],
54
+ "modular": ["modules", "packages", "components"],
55
+ "plugin": ["plugins", "extensions", "addons"],
56
+ "event_driven": ["events", "handlers", "listeners", "publishers"],
57
+ "serverless": ["functions", "lambdas", "handlers"],
58
+ }
59
+
60
+ # Entry point patterns by language/framework
61
+ ENTRY_POINT_PATTERNS = {
62
+ "python": [
63
+ "main.py",
64
+ "app.py",
65
+ "server.py",
66
+ "run.py",
67
+ "__main__.py",
68
+ "wsgi.py",
69
+ ],
70
+ "javascript": ["index.js", "main.js", "app.js", "server.js", "index.ts"],
71
+ "java": ["Main.java", "Application.java", "App.java"],
72
+ "go": ["main.go", "cmd/*/main.go"],
73
+ "rust": ["main.rs", "lib.rs"],
74
+ "ruby": ["app.rb", "application.rb", "config.ru"],
75
+ "php": ["index.php", "app.php"],
76
+ "csharp": ["Program.cs", "Startup.cs"],
77
+ }
78
+
79
+ # API pattern indicators
80
+ API_INDICATORS = {
81
+ "rest": ["routes", "endpoints", "resources", "api/v", "/api/"],
82
+ "graphql": ["schema.graphql", "resolvers", "typeDefs", "graphql"],
83
+ "grpc": [".proto", "grpc", "protobuf", "rpc"],
84
+ "websocket": ["ws://", "wss://", "socket.io", "websocket"],
85
+ "soap": [".wsdl", "soap", "xml-rpc"],
86
+ }
87
+
88
+ # Configuration file patterns
89
+ CONFIG_PATTERNS = {
90
+ "yaml": [".yaml", ".yml"],
91
+ "json": [".json", "config.json", "settings.json"],
92
+ "toml": [".toml", "pyproject.toml", "Cargo.toml"],
93
+ "ini": [".ini", ".cfg", "setup.cfg"],
94
+ "env": [".env", ".env.example", ".env.local"],
95
+ "xml": [".xml", "pom.xml", "web.xml"],
96
+ }
97
+
98
+ def __init__(self, working_directory: Path):
99
+ """Initialize the architecture analyzer service.
100
+
101
+ Args:
102
+ working_directory: Project root directory
103
+ """
104
+ self.working_directory = working_directory
105
+ self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
106
+
107
+ def analyze_architecture(self) -> ArchitectureInfo:
108
+ """Analyze project architecture and structure.
109
+
110
+ WHY: Understanding architecture helps maintain consistency
111
+ and work within established patterns.
112
+
113
+ Returns:
114
+ ArchitectureInfo with analysis results
115
+ """
116
+ info = ArchitectureInfo(
117
+ architecture_type="unknown",
118
+ patterns_detected=[],
119
+ main_modules=[],
120
+ key_directories=[],
121
+ entry_points=[],
122
+ api_patterns=[],
123
+ configuration_patterns=[],
124
+ project_terminology=[],
125
+ )
126
+
127
+ # Analyze directory structure
128
+ self._analyze_directory_patterns(info)
129
+
130
+ # Detect entry points
131
+ self._detect_entry_points(info)
132
+
133
+ # Detect API patterns
134
+ self._detect_api_patterns(info)
135
+
136
+ # Detect configuration patterns
137
+ self._detect_config_patterns(info)
138
+
139
+ # Extract project terminology
140
+ self._extract_terminology(info)
141
+
142
+ # Infer architecture type
143
+ self._infer_architecture_type(info)
144
+
145
+ return info
146
+
147
+ def detect_design_patterns(self) -> List[str]:
148
+ """Detect design patterns used in the project.
149
+
150
+ WHY: Understanding design patterns helps maintain
151
+ consistent implementation approaches.
152
+
153
+ Returns:
154
+ List of detected design pattern names
155
+ """
156
+ patterns = []
157
+
158
+ # Sample some source files
159
+ source_files = self._get_sample_source_files()
160
+
161
+ pattern_indicators = {
162
+ "singleton": [r"getInstance\(\)", r"_instance\s*=", r"@singleton"],
163
+ "factory": [r"Factory", r"create\w+\(\)", r"@factory"],
164
+ "observer": [r"subscribe\(", r"notify\(", r"addEventListener"],
165
+ "strategy": [r"Strategy", r"setStrategy\(", r"execute\("],
166
+ "decorator": [r"@\w+", r"Decorator", r"wrapper"],
167
+ "repository": [r"Repository", r"find\w+By", r"save\("],
168
+ "dependency_injection": [r"@inject", r"@autowired", r"container\."],
169
+ "mvc": [r"Controller", r"Model", r"View"],
170
+ "middleware": [r"middleware", r"use\(", r"next\("],
171
+ }
172
+
173
+ for file_path in source_files:
174
+ try:
175
+ content = file_path.read_text(encoding="utf-8", errors="ignore")
176
+
177
+ for pattern_name, indicators in pattern_indicators.items():
178
+ if any(re.search(ind, content) for ind in indicators):
179
+ if pattern_name not in patterns:
180
+ patterns.append(pattern_name)
181
+
182
+ except Exception as e:
183
+ self.logger.debug(f"Error detecting patterns in {file_path}: {e}")
184
+
185
+ return patterns
186
+
187
+ def analyze_module_structure(self) -> Dict[str, List[str]]:
188
+ """Analyze module organization and dependencies.
189
+
190
+ WHY: Module structure reveals how code is organized
191
+ and helps understand component relationships.
192
+
193
+ Returns:
194
+ Dictionary mapping modules to their components
195
+ """
196
+ modules = {}
197
+
198
+ # Look for common module directories
199
+ module_dirs = ["src", "lib", "app", "modules", "packages", "components"]
200
+
201
+ for module_dir in module_dirs:
202
+ module_path = self.working_directory / module_dir
203
+ if module_path.exists() and module_path.is_dir():
204
+ # Get immediate subdirectories as modules
205
+ for subdir in module_path.iterdir():
206
+ if subdir.is_dir() and not subdir.name.startswith("."):
207
+ module_name = subdir.name
208
+
209
+ # Get module components
210
+ components = []
211
+ for item in subdir.iterdir():
212
+ if item.is_file():
213
+ components.append(item.name)
214
+ elif item.is_dir() and not item.name.startswith("."):
215
+ components.append(f"{item.name}/")
216
+
217
+ if components:
218
+ modules[module_name] = components[:10] # Limit to 10
219
+
220
+ return modules
221
+
222
+ def _analyze_directory_patterns(self, info: ArchitectureInfo) -> None:
223
+ """Analyze directory structure for architectural patterns."""
224
+ existing_dirs = set()
225
+
226
+ # Collect all directory names
227
+ for dirpath, dirnames, _ in self.working_directory.walk():
228
+ for dirname in dirnames:
229
+ if not dirname.startswith("."):
230
+ existing_dirs.add(dirname.lower())
231
+
232
+ # Check for architectural patterns
233
+ for arch_type, indicators in self.ARCHITECTURE_INDICATORS.items():
234
+ matches = sum(1 for ind in indicators if ind in existing_dirs)
235
+ if matches >= len(indicators) * 0.6: # 60% match threshold
236
+ info.patterns_detected.append(arch_type)
237
+
238
+ # Identify key directories
239
+ important_dirs = [
240
+ "src",
241
+ "lib",
242
+ "app",
243
+ "components",
244
+ "services",
245
+ "models",
246
+ "views",
247
+ "controllers",
248
+ "routes",
249
+ "api",
250
+ "tests",
251
+ "docs",
252
+ "config",
253
+ "utils",
254
+ "helpers",
255
+ "core",
256
+ "modules",
257
+ "packages",
258
+ ]
259
+
260
+ info.key_directories = [
261
+ d for d in important_dirs if (self.working_directory / d).exists()
262
+ ]
263
+
264
+ # Identify main modules (top-level important directories)
265
+ for key_dir in ["src", "lib", "app"]:
266
+ key_path = self.working_directory / key_dir
267
+ if key_path.exists() and key_path.is_dir():
268
+ subdirs = [
269
+ d.name
270
+ for d in key_path.iterdir()
271
+ if d.is_dir() and not d.name.startswith(".")
272
+ ]
273
+ info.main_modules.extend(subdirs[:5]) # Top 5 modules
274
+
275
+ def _detect_entry_points(self, info: ArchitectureInfo) -> None:
276
+ """Detect project entry points."""
277
+ entry_points = []
278
+
279
+ for patterns in self.ENTRY_POINT_PATTERNS.values():
280
+ for pattern in patterns:
281
+ # Handle glob patterns
282
+ if "*" in pattern:
283
+ for match in self.working_directory.glob(pattern):
284
+ if match.is_file():
285
+ rel_path = str(match.relative_to(self.working_directory))
286
+ entry_points.append(rel_path)
287
+ else:
288
+ # Check direct path and in src/
289
+ for base in ["", "src/", "app/", "lib/"]:
290
+ entry_path = self.working_directory / base / pattern
291
+ if entry_path.exists() and entry_path.is_file():
292
+ rel_path = str(
293
+ entry_path.relative_to(self.working_directory)
294
+ )
295
+ entry_points.append(rel_path)
296
+
297
+ info.entry_points = list(set(entry_points))[:10] # Limit to 10
298
+
299
+ def _detect_api_patterns(self, info: ArchitectureInfo) -> None:
300
+ """Detect API patterns and styles."""
301
+ api_patterns = []
302
+
303
+ # Check directory structure
304
+ for api_type, indicators in self.API_INDICATORS.items():
305
+ for indicator in indicators:
306
+ # Check for directories
307
+ if "/" in indicator:
308
+ if any(
309
+ indicator in str(p)
310
+ for p in self.working_directory.rglob("*")
311
+ if p.is_dir()
312
+ ):
313
+ api_patterns.append(api_type.upper())
314
+ break
315
+
316
+ # Check for files
317
+ elif "." in indicator:
318
+ if list(self.working_directory.rglob(f"*{indicator}")):
319
+ api_patterns.append(api_type.upper())
320
+ break
321
+
322
+ # Check source files for API patterns
323
+ source_files = self._get_sample_source_files(limit=10)
324
+ for file_path in source_files:
325
+ try:
326
+ content = file_path.read_text(encoding="utf-8", errors="ignore")
327
+
328
+ # REST API patterns
329
+ if any(
330
+ p in content for p in ["@app.route", "@router.", "@Get(", "@Post("]
331
+ ):
332
+ api_patterns.append("REST")
333
+
334
+ # GraphQL patterns
335
+ if any(p in content for p in ["graphql", "resolver", "typeDefs"]):
336
+ api_patterns.append("GraphQL")
337
+
338
+ except Exception as e:
339
+ self.logger.debug(f"Error detecting API patterns in {file_path}: {e}")
340
+
341
+ info.api_patterns = list(set(api_patterns))
342
+
343
+ def _detect_config_patterns(self, info: ArchitectureInfo) -> None:
344
+ """Detect configuration file patterns."""
345
+ config_patterns = []
346
+
347
+ for config_type, extensions in self.CONFIG_PATTERNS.items():
348
+ for ext in extensions:
349
+ if list(self.working_directory.rglob(f"*{ext}")):
350
+ config_patterns.append(config_type)
351
+ break
352
+
353
+ info.configuration_patterns = config_patterns
354
+
355
+ def _extract_terminology(self, info: ArchitectureInfo) -> None:
356
+ """Extract project-specific terminology."""
357
+ terminology = set()
358
+
359
+ # Extract from project name
360
+ project_words = re.findall(r"[A-Z][a-z]+|[a-z]+", self.working_directory.name)
361
+ terminology.update(project_words)
362
+
363
+ # Extract from main modules
364
+ for module in info.main_modules:
365
+ words = re.findall(r"[A-Z][a-z]+|[a-z]+", module)
366
+ terminology.update(words)
367
+
368
+ # Extract from key directories
369
+ for directory in info.key_directories:
370
+ words = re.findall(r"[A-Z][a-z]+|[a-z]+", directory)
371
+ terminology.update(words)
372
+
373
+ # Filter out common words
374
+ common_words = {
375
+ "src",
376
+ "lib",
377
+ "app",
378
+ "main",
379
+ "test",
380
+ "tests",
381
+ "docs",
382
+ "config",
383
+ "utils",
384
+ "helpers",
385
+ "core",
386
+ "base",
387
+ "common",
388
+ "shared",
389
+ "public",
390
+ "private",
391
+ "static",
392
+ "assets",
393
+ "build",
394
+ "dist",
395
+ "node",
396
+ "modules",
397
+ "vendor",
398
+ "bin",
399
+ "obj",
400
+ }
401
+
402
+ domain_terms = [
403
+ term
404
+ for term in terminology
405
+ if len(term) > 3 and term.lower() not in common_words
406
+ ]
407
+
408
+ info.project_terminology = sorted(domain_terms)[:15] # Top 15 terms
409
+
410
+ def _infer_architecture_type(self, info: ArchitectureInfo) -> None:
411
+ """Infer the overall architecture type."""
412
+ # Check detected patterns
413
+ if "microservices" in info.patterns_detected:
414
+ info.architecture_type = "Microservices Architecture"
415
+ elif "hexagonal" in info.patterns_detected:
416
+ info.architecture_type = "Hexagonal Architecture"
417
+ elif "clean" in info.patterns_detected:
418
+ info.architecture_type = "Clean Architecture"
419
+ elif "mvc" in info.patterns_detected:
420
+ info.architecture_type = "MVC Architecture"
421
+ elif "layered" in info.patterns_detected:
422
+ info.architecture_type = "Layered Architecture"
423
+ elif "event_driven" in info.patterns_detected:
424
+ info.architecture_type = "Event-Driven Architecture"
425
+ elif "serverless" in info.patterns_detected:
426
+ info.architecture_type = "Serverless Architecture"
427
+ elif "modular" in info.patterns_detected:
428
+ info.architecture_type = "Modular Architecture"
429
+ elif info.api_patterns:
430
+ if "REST" in info.api_patterns:
431
+ info.architecture_type = "REST API Service"
432
+ elif "GraphQL" in info.api_patterns:
433
+ info.architecture_type = "GraphQL Service"
434
+ else:
435
+ info.architecture_type = "API Service"
436
+ elif "api" in info.key_directories:
437
+ info.architecture_type = "API-First Architecture"
438
+ elif any(fw in info.key_directories for fw in ["components", "views"]):
439
+ info.architecture_type = "Component-Based Architecture"
440
+ else:
441
+ info.architecture_type = "Standard Application Architecture"
442
+
443
+ def _get_sample_source_files(self, limit: int = 20) -> List[Path]:
444
+ """Get a sample of source files for analysis."""
445
+ extensions = [".py", ".js", ".ts", ".java", ".go", ".rs", ".rb", ".php", ".cs"]
446
+ source_files = []
447
+
448
+ for ext in extensions:
449
+ files = list(self.working_directory.rglob(f"*{ext}"))
450
+ # Filter out vendor directories
451
+ files = [
452
+ f
453
+ for f in files
454
+ if not any(
455
+ part in [".git", "node_modules", "vendor", "__pycache__"]
456
+ for part in f.parts
457
+ )
458
+ ]
459
+ source_files.extend(files[:3]) # Take up to 3 files per extension
460
+
461
+ return source_files[:limit]