claude-mpm 4.4.3__py3-none-any.whl → 4.4.4__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 +399 -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 +10 -10
  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.4.dist-info}/METADATA +9 -3
  114. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/RECORD +118 -117
  115. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/WHEEL +0 -0
  116. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/entry_points.txt +0 -0
  117. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/licenses/LICENSE +0 -0
  118. {claude_mpm-4.4.3.dist-info → claude_mpm-4.4.4.dist-info}/top_level.txt +0 -0
@@ -22,9 +22,9 @@ from datetime import datetime, timezone
22
22
  from typing import Any, Dict, Optional
23
23
 
24
24
  from claude_mpm.core.config import Config
25
+ from claude_mpm.core.logging_utils import get_logger
25
26
  from claude_mpm.core.shared.config_loader import ConfigLoader
26
27
 
27
- from claude_mpm.core.logging_utils import get_logger
28
28
  logger = get_logger(__name__)
29
29
 
30
30
 
@@ -10,11 +10,11 @@ WHY this integration module:
10
10
  from datetime import datetime, timezone
11
11
  from typing import Optional
12
12
 
13
+ from claude_mpm.core.logging_utils import get_logger
13
14
  from claude_mpm.services.event_bus import EventBus
14
15
  from claude_mpm.services.event_bus.config import get_config
15
16
  from claude_mpm.services.event_bus.direct_relay import DirectSocketIORelay
16
17
 
17
- from claude_mpm.core.logging_utils import get_logger
18
18
  logger = get_logger(__name__)
19
19
 
20
20
 
@@ -62,4 +62,4 @@ __all__ = [
62
62
  "UnifiedDeploymentService",
63
63
  "UnifiedAnalyzer",
64
64
  "UnifiedConfigManager",
65
- ]
65
+ ]
@@ -29,9 +29,9 @@ from .structure_analyzer import StructureAnalyzerStrategy
29
29
  __all__ = [
30
30
  "CodeAnalyzerStrategy",
31
31
  "DependencyAnalyzerStrategy",
32
- "StructureAnalyzerStrategy",
33
- "SecurityAnalyzerStrategy",
34
32
  "PerformanceAnalyzerStrategy",
33
+ "SecurityAnalyzerStrategy",
34
+ "StructureAnalyzerStrategy",
35
35
  ]
36
36
 
37
37
  # Strategy registry for automatic discovery
@@ -41,4 +41,4 @@ ANALYZER_STRATEGIES = {
41
41
  "structure": StructureAnalyzerStrategy,
42
42
  "security": SecurityAnalyzerStrategy,
43
43
  "performance": PerformanceAnalyzerStrategy,
44
- }
44
+ }
@@ -12,11 +12,16 @@ Created: 2025-01-26
12
12
  import ast
13
13
  import re
14
14
  from pathlib import Path
15
- from typing import Any, Dict, List, Optional, Set, Tuple
15
+ from typing import Any, Dict, List, Optional
16
16
 
17
17
  from claude_mpm.core.logging_utils import get_logger
18
18
 
19
- from ..strategies import AnalyzerStrategy, StrategyContext, StrategyMetadata, StrategyPriority
19
+ from ..strategies import (
20
+ AnalyzerStrategy,
21
+ StrategyContext,
22
+ StrategyMetadata,
23
+ StrategyPriority,
24
+ )
20
25
 
21
26
  logger = get_logger(__name__)
22
27
 
@@ -109,7 +114,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
109
114
  target_path = Path(target)
110
115
  if target_path.is_file():
111
116
  return self._analyze_file(target_path, options)
112
- elif target_path.is_dir():
117
+ if target_path.is_dir():
113
118
  return self._analyze_directory(target_path, options)
114
119
  elif isinstance(target, ast.AST):
115
120
  return self._analyze_ast(target, options)
@@ -143,7 +148,9 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
143
148
  metrics.update(self._analyze_python_code(content, file_path))
144
149
 
145
150
  # Calculate complexity metrics
146
- metrics["complexity"] = self._calculate_complexity_metrics(content, language)
151
+ metrics["complexity"] = self._calculate_complexity_metrics(
152
+ content, language
153
+ )
147
154
 
148
155
  # Detect code smells
149
156
  metrics["code_smells"] = self._detect_code_smells(content, metrics)
@@ -166,7 +173,9 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
166
173
  "error": str(e),
167
174
  }
168
175
 
169
- def _analyze_directory(self, dir_path: Path, options: Dict[str, Any]) -> Dict[str, Any]:
176
+ def _analyze_directory(
177
+ self, dir_path: Path, options: Dict[str, Any]
178
+ ) -> Dict[str, Any]:
170
179
  """Analyze all code files in a directory."""
171
180
  results = {
172
181
  "status": "success",
@@ -198,7 +207,10 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
198
207
  results["summary"] = {
199
208
  "total_files": len(results["files"]),
200
209
  "total_lines": total_metrics.get("lines_of_code", 0),
201
- "average_complexity": total_metrics.get("complexity", {}).get("cyclomatic", 0) / max(len(results["files"]), 1),
210
+ "average_complexity": total_metrics.get("complexity", {}).get(
211
+ "cyclomatic", 0
212
+ )
213
+ / max(len(results["files"]), 1),
202
214
  "code_smells_count": sum(
203
215
  len(f.get("metrics", {}).get("code_smells", []))
204
216
  for f in results["files"]
@@ -219,7 +231,9 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
219
231
 
220
232
  for node in ast.walk(tree):
221
233
  if isinstance(node, ast.FunctionDef):
222
- if any(isinstance(parent, ast.ClassDef) for parent in ast.walk(tree)):
234
+ if any(
235
+ isinstance(parent, ast.ClassDef) for parent in ast.walk(tree)
236
+ ):
223
237
  methods.append(node.name)
224
238
  else:
225
239
  functions.append(node.name)
@@ -248,17 +262,27 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
248
262
 
249
263
  # Analyze specific node types
250
264
  if isinstance(node, ast.FunctionDef):
251
- metrics.update({
252
- "name": node.name,
253
- "parameters": len(node.args.args),
254
- "lines": node.end_lineno - node.lineno + 1 if hasattr(node, "end_lineno") else 0,
255
- })
265
+ metrics.update(
266
+ {
267
+ "name": node.name,
268
+ "parameters": len(node.args.args),
269
+ "lines": (
270
+ node.end_lineno - node.lineno + 1
271
+ if hasattr(node, "end_lineno")
272
+ else 0
273
+ ),
274
+ }
275
+ )
256
276
  elif isinstance(node, ast.ClassDef):
257
- metrics.update({
258
- "name": node.name,
259
- "methods": sum(1 for n in node.body if isinstance(n, ast.FunctionDef)),
260
- "bases": len(node.bases),
261
- })
277
+ metrics.update(
278
+ {
279
+ "name": node.name,
280
+ "methods": sum(
281
+ 1 for n in node.body if isinstance(n, ast.FunctionDef)
282
+ ),
283
+ "bases": len(node.bases),
284
+ }
285
+ )
262
286
 
263
287
  return {
264
288
  "status": "success",
@@ -266,7 +290,9 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
266
290
  "metrics": metrics,
267
291
  }
268
292
 
269
- def _calculate_complexity_metrics(self, content: str, language: str) -> Dict[str, Any]:
293
+ def _calculate_complexity_metrics(
294
+ self, content: str, language: str
295
+ ) -> Dict[str, Any]:
270
296
  """Calculate various complexity metrics."""
271
297
  complexity = {
272
298
  "cyclomatic": 1, # Base complexity
@@ -315,26 +341,35 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
315
341
  """Calculate complexity for a single AST node."""
316
342
  return self._calculate_cyclomatic_complexity(node)
317
343
 
318
- def _detect_code_smells(self, content: str, metrics: Dict[str, Any]) -> List[Dict[str, Any]]:
344
+ def _detect_code_smells(
345
+ self, content: str, metrics: Dict[str, Any]
346
+ ) -> List[Dict[str, Any]]:
319
347
  """Detect common code smells."""
320
348
  smells = []
321
349
 
322
350
  # Long method/function
323
- if metrics.get("lines_of_code", 0) > self.code_smell_patterns["long_method"]["threshold"]:
324
- smells.append({
325
- "type": "long_method",
326
- "severity": "medium",
327
- "message": f"Method/function has {metrics['lines_of_code']} lines (threshold: {self.code_smell_patterns['long_method']['threshold']})",
328
- })
351
+ if (
352
+ metrics.get("lines_of_code", 0)
353
+ > self.code_smell_patterns["long_method"]["threshold"]
354
+ ):
355
+ smells.append(
356
+ {
357
+ "type": "long_method",
358
+ "severity": "medium",
359
+ "message": f"Method/function has {metrics['lines_of_code']} lines (threshold: {self.code_smell_patterns['long_method']['threshold']})",
360
+ }
361
+ )
329
362
 
330
363
  # High complexity
331
364
  complexity = metrics.get("complexity", {}).get("cyclomatic", 0)
332
365
  if complexity > 10:
333
- smells.append({
334
- "type": "high_complexity",
335
- "severity": "high",
336
- "message": f"High cyclomatic complexity: {complexity}",
337
- })
366
+ smells.append(
367
+ {
368
+ "type": "high_complexity",
369
+ "severity": "high",
370
+ "message": f"High cyclomatic complexity: {complexity}",
371
+ }
372
+ )
338
373
 
339
374
  return smells
340
375
 
@@ -398,25 +433,35 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
398
433
  if "metrics" in analysis_result:
399
434
  raw_metrics = analysis_result["metrics"]
400
435
 
401
- metrics.update({
402
- "lines_of_code": raw_metrics.get("lines_of_code", 0),
403
- "cyclomatic_complexity": raw_metrics.get("complexity", {}).get("cyclomatic", 0),
404
- "cognitive_complexity": raw_metrics.get("complexity", {}).get("cognitive", 0),
405
- "maintainability_index": raw_metrics.get("maintainability_index", 0),
406
- "code_smells": len(raw_metrics.get("code_smells", [])),
407
- "function_count": raw_metrics.get("function_count", 0),
408
- "class_count": raw_metrics.get("class_count", 0),
409
- })
436
+ metrics.update(
437
+ {
438
+ "lines_of_code": raw_metrics.get("lines_of_code", 0),
439
+ "cyclomatic_complexity": raw_metrics.get("complexity", {}).get(
440
+ "cyclomatic", 0
441
+ ),
442
+ "cognitive_complexity": raw_metrics.get("complexity", {}).get(
443
+ "cognitive", 0
444
+ ),
445
+ "maintainability_index": raw_metrics.get(
446
+ "maintainability_index", 0
447
+ ),
448
+ "code_smells": len(raw_metrics.get("code_smells", [])),
449
+ "function_count": raw_metrics.get("function_count", 0),
450
+ "class_count": raw_metrics.get("class_count", 0),
451
+ }
452
+ )
410
453
 
411
454
  # Extract summary metrics for directory analysis
412
455
  if "summary" in analysis_result:
413
456
  summary = analysis_result["summary"]
414
- metrics.update({
415
- "total_files": summary.get("total_files", 0),
416
- "total_lines": summary.get("total_lines", 0),
417
- "average_complexity": summary.get("average_complexity", 0),
418
- "total_code_smells": summary.get("code_smells_count", 0),
419
- })
457
+ metrics.update(
458
+ {
459
+ "total_files": summary.get("total_files", 0),
460
+ "total_lines": summary.get("total_lines", 0),
461
+ "average_complexity": summary.get("average_complexity", 0),
462
+ "total_code_smells": summary.get("code_smells_count", 0),
463
+ }
464
+ )
420
465
 
421
466
  return metrics
422
467
 
@@ -461,13 +506,12 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
461
506
  comparison["degraded"].append(result)
462
507
  else:
463
508
  comparison["unchanged"].append(result)
509
+ # Lower is better (complexity, code smells, etc.)
510
+ elif diff < 0:
511
+ comparison["improved"].append(result)
512
+ elif diff > 0:
513
+ comparison["degraded"].append(result)
464
514
  else:
465
- # Lower is better (complexity, code smells, etc.)
466
- if diff < 0:
467
- comparison["improved"].append(result)
468
- elif diff > 0:
469
- comparison["degraded"].append(result)
470
- else:
471
- comparison["unchanged"].append(result)
515
+ comparison["unchanged"].append(result)
472
516
 
473
- return comparison
517
+ return comparison
@@ -11,13 +11,17 @@ Created: 2025-01-26
11
11
 
12
12
  import json
13
13
  import re
14
- import subprocess
15
14
  from pathlib import Path
16
- from typing import Any, Dict, List, Optional, Set, Tuple
15
+ from typing import Any, Dict, List, Optional
17
16
 
18
17
  from claude_mpm.core.logging_utils import get_logger
19
18
 
20
- from ..strategies import AnalyzerStrategy, StrategyContext, StrategyMetadata, StrategyPriority
19
+ from ..strategies import (
20
+ AnalyzerStrategy,
21
+ StrategyContext,
22
+ StrategyMetadata,
23
+ StrategyPriority,
24
+ )
21
25
 
22
26
  logger = get_logger(__name__)
23
27
 
@@ -69,7 +73,15 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
69
73
  # Testing framework packages
70
74
  TESTING_PACKAGES = {
71
75
  "python": ["pytest", "unittest", "nose", "nose2", "tox", "coverage"],
72
- "javascript": ["jest", "mocha", "chai", "jasmine", "cypress", "playwright", "vitest"],
76
+ "javascript": [
77
+ "jest",
78
+ "mocha",
79
+ "chai",
80
+ "jasmine",
81
+ "cypress",
82
+ "playwright",
83
+ "vitest",
84
+ ],
73
85
  "java": ["junit", "testng", "mockito", "assertj"],
74
86
  "ruby": ["rspec", "minitest", "cucumber"],
75
87
  "go": ["testify", "ginkgo", "gomega"],
@@ -79,7 +91,16 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
79
91
  # Web framework packages
80
92
  FRAMEWORK_PACKAGES = {
81
93
  "python": ["django", "flask", "fastapi", "pyramid", "tornado", "aiohttp"],
82
- "javascript": ["express", "koa", "fastify", "hapi", "nestjs", "next", "nuxt", "gatsby"],
94
+ "javascript": [
95
+ "express",
96
+ "koa",
97
+ "fastify",
98
+ "hapi",
99
+ "nestjs",
100
+ "next",
101
+ "nuxt",
102
+ "gatsby",
103
+ ],
83
104
  "ruby": ["rails", "sinatra", "hanami"],
84
105
  "java": ["spring", "spring-boot", "struts", "play"],
85
106
  "php": ["laravel", "symfony", "slim", "lumen"],
@@ -141,7 +162,7 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
141
162
 
142
163
  if target_path.is_dir():
143
164
  return self._analyze_project(target_path, options)
144
- elif target_path.is_file():
165
+ if target_path.is_file():
145
166
  return self._analyze_manifest(target_path, options)
146
167
 
147
168
  return {
@@ -149,7 +170,9 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
149
170
  "message": f"Unsupported target type: {type(target).__name__}",
150
171
  }
151
172
 
152
- def _analyze_project(self, project_path: Path, options: Dict[str, Any]) -> Dict[str, Any]:
173
+ def _analyze_project(
174
+ self, project_path: Path, options: Dict[str, Any]
175
+ ) -> Dict[str, Any]:
153
176
  """Analyze dependencies in a project directory."""
154
177
  results = {
155
178
  "status": "success",
@@ -172,7 +195,9 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
172
195
  manager_deps = self._analyze_package_manager(project_path, manager, options)
173
196
  if manager_deps:
174
197
  results["dependencies"][manager] = manager_deps.get("dependencies", {})
175
- results["dev_dependencies"][manager] = manager_deps.get("dev_dependencies", {})
198
+ results["dev_dependencies"][manager] = manager_deps.get(
199
+ "dev_dependencies", {}
200
+ )
176
201
 
177
202
  # Detect frameworks, databases, and testing tools
178
203
  all_deps = self._flatten_dependencies(results["dependencies"])
@@ -191,7 +216,9 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
191
216
 
192
217
  return results
193
218
 
194
- def _analyze_manifest(self, manifest_path: Path, options: Dict[str, Any]) -> Dict[str, Any]:
219
+ def _analyze_manifest(
220
+ self, manifest_path: Path, options: Dict[str, Any]
221
+ ) -> Dict[str, Any]:
195
222
  """Analyze a specific package manifest file."""
196
223
  results = {
197
224
  "status": "success",
@@ -277,20 +304,21 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
277
304
  try:
278
305
  if manager in ["npm", "yarn", "pnpm"]:
279
306
  return self._analyze_node_dependencies(project_path, manager)
280
- elif manager in ["pip", "pipenv", "poetry"]:
307
+ if manager in ["pip", "pipenv", "poetry"]:
281
308
  return self._analyze_python_dependencies(project_path, manager)
282
- elif manager == "cargo":
309
+ if manager == "cargo":
283
310
  return self._analyze_cargo_dependencies(project_path)
284
- elif manager == "go":
311
+ if manager == "go":
285
312
  return self._analyze_go_dependencies(project_path)
286
- else:
287
- logger.debug(f"Unsupported package manager for analysis: {manager}")
288
- return None
313
+ logger.debug(f"Unsupported package manager for analysis: {manager}")
314
+ return None
289
315
  except Exception as e:
290
316
  logger.error(f"Error analyzing {manager} dependencies: {e}")
291
317
  return None
292
318
 
293
- def _analyze_node_dependencies(self, project_path: Path, manager: str) -> Dict[str, Any]:
319
+ def _analyze_node_dependencies(
320
+ self, project_path: Path, manager: str
321
+ ) -> Dict[str, Any]:
294
322
  """Analyze Node.js dependencies."""
295
323
  package_json_path = project_path / "package.json"
296
324
  if not package_json_path.exists():
@@ -298,7 +326,9 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
298
326
 
299
327
  return self._parse_package_json(package_json_path)
300
328
 
301
- def _analyze_python_dependencies(self, project_path: Path, manager: str) -> Dict[str, Any]:
329
+ def _analyze_python_dependencies(
330
+ self, project_path: Path, manager: str
331
+ ) -> Dict[str, Any]:
302
332
  """Analyze Python dependencies."""
303
333
  results = {"dependencies": {}, "dev_dependencies": {}}
304
334
 
@@ -345,7 +375,7 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
345
375
  def _parse_package_json(self, path: Path) -> Dict[str, Any]:
346
376
  """Parse package.json file."""
347
377
  try:
348
- with open(path, "r") as f:
378
+ with open(path) as f:
349
379
  data = json.load(f)
350
380
 
351
381
  return {
@@ -479,7 +509,7 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
479
509
  if line.startswith("require ("):
480
510
  in_require = True
481
511
  continue
482
- elif line == ")":
512
+ if line == ")":
483
513
  in_require = False
484
514
  continue
485
515
 
@@ -494,7 +524,9 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
494
524
 
495
525
  return {"dependencies": dependencies}
496
526
 
497
- def _flatten_dependencies(self, deps_dict: Dict[str, Dict[str, str]]) -> Dict[str, str]:
527
+ def _flatten_dependencies(
528
+ self, deps_dict: Dict[str, Dict[str, str]]
529
+ ) -> Dict[str, str]:
498
530
  """Flatten nested dependency dictionaries."""
499
531
  flattened = {}
500
532
  for manager_deps in deps_dict.values():
@@ -581,11 +613,13 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
581
613
  # Extract vulnerability metrics
582
614
  if "vulnerabilities" in analysis_result:
583
615
  vuln = analysis_result["vulnerabilities"]
584
- metrics.update({
585
- "vulnerability_total": vuln.get("total", 0),
586
- "vulnerability_critical": vuln.get("critical", 0),
587
- "vulnerability_high": vuln.get("high", 0),
588
- })
616
+ metrics.update(
617
+ {
618
+ "vulnerability_total": vuln.get("total", 0),
619
+ "vulnerability_critical": vuln.get("critical", 0),
620
+ "vulnerability_high": vuln.get("high", 0),
621
+ }
622
+ )
589
623
 
590
624
  return metrics
591
625
 
@@ -607,27 +641,33 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
607
641
  # Find added dependencies
608
642
  for dep, version in current_deps.items():
609
643
  if dep not in baseline_deps:
610
- comparison["added_dependencies"].append({
611
- "name": dep,
612
- "version": version,
613
- })
644
+ comparison["added_dependencies"].append(
645
+ {
646
+ "name": dep,
647
+ "version": version,
648
+ }
649
+ )
614
650
 
615
651
  # Find removed dependencies
616
652
  for dep, version in baseline_deps.items():
617
653
  if dep not in current_deps:
618
- comparison["removed_dependencies"].append({
619
- "name": dep,
620
- "version": version,
621
- })
654
+ comparison["removed_dependencies"].append(
655
+ {
656
+ "name": dep,
657
+ "version": version,
658
+ }
659
+ )
622
660
 
623
661
  # Find updated dependencies
624
662
  for dep in baseline_deps:
625
663
  if dep in current_deps and baseline_deps[dep] != current_deps[dep]:
626
- comparison["updated_dependencies"].append({
627
- "name": dep,
628
- "old_version": baseline_deps[dep],
629
- "new_version": current_deps[dep],
630
- })
664
+ comparison["updated_dependencies"].append(
665
+ {
666
+ "name": dep,
667
+ "old_version": baseline_deps[dep],
668
+ "new_version": current_deps[dep],
669
+ }
670
+ )
631
671
 
632
672
  # Compare vulnerability counts
633
673
  if "vulnerabilities" in baseline and "vulnerabilities" in current:
@@ -636,8 +676,9 @@ class DependencyAnalyzerStrategy(AnalyzerStrategy):
636
676
 
637
677
  comparison["vulnerability_changes"] = {
638
678
  "total": current_vuln.get("total", 0) - baseline_vuln.get("total", 0),
639
- "critical": current_vuln.get("critical", 0) - baseline_vuln.get("critical", 0),
679
+ "critical": current_vuln.get("critical", 0)
680
+ - baseline_vuln.get("critical", 0),
640
681
  "high": current_vuln.get("high", 0) - baseline_vuln.get("high", 0),
641
682
  }
642
683
 
643
- return comparison
684
+ return comparison