using MarketAlly.AIPlugin; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace MarketAlly.AIPlugin.Analysis.Plugins { [AIPlugin("TechnicalDebt", "Quantifies and tracks technical debt with actionable improvement recommendations")] public class TechnicalDebtPlugin : IAIPlugin { [AIParameter("Full path to the project or directory to analyze", required: true)] public string ProjectPath { get; set; } = string.Empty; [AIParameter("Calculate code complexity debt", required: false)] public bool CalculateComplexityDebt { get; set; } = true; [AIParameter("Analyze documentation debt", required: false)] public bool AnalyzeDocumentationDebt { get; set; } = true; [AIParameter("Check for outdated dependencies", required: false)] public bool CheckDependencyDebt { get; set; } = true; [AIParameter("Analyze test coverage debt", required: false)] public bool AnalyzeTestDebt { get; set; } = true; [AIParameter("Generate prioritized improvement plan", required: false)] public bool GenerateImprovementPlan { get; set; } = true; [AIParameter("Track debt trends over time", required: false)] public bool TrackTrends { get; set; } = false; public IReadOnlyDictionary SupportedParameters => new Dictionary { ["projectPath"] = typeof(string), ["calculateComplexityDebt"] = typeof(bool), ["analyzeDocumentationDebt"] = typeof(bool), ["checkDependencyDebt"] = typeof(bool), ["analyzeTestDebt"] = typeof(bool), ["generateImprovementPlan"] = typeof(bool), ["trackTrends"] = typeof(bool) }; public async Task ExecuteAsync(IReadOnlyDictionary parameters) { try { // Extract parameters string projectPath = parameters["projectPath"]?.ToString() ?? string.Empty; bool calculateComplexityDebt = GetBoolParameter(parameters, "calculateComplexityDebt", true); bool analyzeDocumentationDebt = GetBoolParameter(parameters, "analyzeDocumentationDebt", true); bool checkDependencyDebt = GetBoolParameter(parameters, "checkDependencyDebt", true); bool analyzeTestDebt = GetBoolParameter(parameters, "analyzeTestDebt", true); bool generateImprovementPlan = GetBoolParameter(parameters, "generateImprovementPlan", true); bool trackTrends = GetBoolParameter(parameters, "trackTrends", false); // Validate path if (!Directory.Exists(projectPath) && !File.Exists(projectPath)) { return new AIPluginResult( new DirectoryNotFoundException($"Path not found: {projectPath}"), "Path not found" ); } // Initialize debt analysis var debtAnalysis = new TechnicalDebtAnalysis { ProjectPath = projectPath, AnalysisDate = DateTime.UtcNow, ComplexityDebt = new ComplexityDebtMetrics(), DocumentationDebt = new DocumentationDebtMetrics(), DependencyDebt = new DependencyDebtMetrics(), TestDebt = new TestDebtMetrics(), DebtItems = new List() }; // Get all source files var sourceFiles = GetSourceFiles(projectPath); var projectFiles = GetProjectFiles(projectPath); // Analyze complexity debt if (calculateComplexityDebt) { await AnalyzeComplexityDebt(sourceFiles, debtAnalysis); } // Analyze documentation debt if (analyzeDocumentationDebt) { await AnalyzeDocumentationDebtMethod(sourceFiles, debtAnalysis); } // Analyze dependency debt if (checkDependencyDebt) { await AnalyzeDependencyDebt(projectFiles, debtAnalysis); } // Analyze test debt if (analyzeTestDebt) { await AnalyzeTestDebtMethod(sourceFiles, debtAnalysis); } // Calculate overall debt score var debtScore = CalculateOverallDebtScore(debtAnalysis); // Generate improvement plan var improvementPlan = new List(); if (generateImprovementPlan) { improvementPlan = GenerateImprovementPlanMethod(debtAnalysis); } // Track trends if requested object? debtTrends = null; if (trackTrends) { debtTrends = await TrackDebtTrends(projectPath, debtAnalysis); } var result = new { ProjectPath = projectPath, AnalysisDate = debtAnalysis.AnalysisDate, DebtScore = debtScore, FilesAnalyzed = sourceFiles.Count, ComplexityDebt = calculateComplexityDebt ? new { debtAnalysis.ComplexityDebt.TotalComplexityPoints, debtAnalysis.ComplexityDebt.AverageMethodComplexity, debtAnalysis.ComplexityDebt.HighComplexityMethods, debtAnalysis.ComplexityDebt.EstimatedRefactoringHours, DebtLevel = GetDebtLevel(debtAnalysis.ComplexityDebt.TotalComplexityPoints, "Complexity") } : null, DocumentationDebt = analyzeDocumentationDebt ? new { debtAnalysis.DocumentationDebt.TotalMethods, debtAnalysis.DocumentationDebt.UndocumentedMethods, debtAnalysis.DocumentationDebt.DocumentationCoverage, debtAnalysis.DocumentationDebt.EstimatedDocumentationHours, DebtLevel = GetDebtLevel(debtAnalysis.DocumentationDebt.UndocumentedMethods, "Documentation") } : null, DependencyDebt = checkDependencyDebt ? new { debtAnalysis.DependencyDebt.TotalDependencies, debtAnalysis.DependencyDebt.OutdatedDependencies, debtAnalysis.DependencyDebt.VulnerableDependencies, debtAnalysis.DependencyDebt.MajorVersionsBehind, debtAnalysis.DependencyDebt.EstimatedUpgradeHours, DebtLevel = GetDebtLevel(debtAnalysis.DependencyDebt.OutdatedDependencies, "Dependency") } : null, TestDebt = analyzeTestDebt ? new { debtAnalysis.TestDebt.TotalMethods, debtAnalysis.TestDebt.UntestedMethods, debtAnalysis.TestDebt.TestCoverage, debtAnalysis.TestDebt.EstimatedTestingHours, DebtLevel = GetDebtLevel(debtAnalysis.TestDebt.UntestedMethods, "Test") } : null, DebtItems = debtAnalysis.DebtItems.OrderByDescending(d => d.Priority).Take(20).Select(d => new { d.Type, d.Category, d.Description, d.Location, d.Priority, d.EstimatedEffort, d.Impact, d.RecommendedAction }).ToList(), ImprovementPlan = generateImprovementPlan ? improvementPlan.Select(i => new { i.Phase, i.Priority, i.Title, i.Description, i.EstimatedHours, i.ExpectedBenefit, i.Dependencies }).ToList() : null, DebtTrends = debtTrends, Summary = new { TotalDebtItems = debtAnalysis.DebtItems.Count, HighPriorityItems = debtAnalysis.DebtItems.Count(d => d.Priority >= 8), EstimatedTotalEffort = debtAnalysis.DebtItems.Sum(d => d.EstimatedEffort), DebtCategory = GetOverallDebtCategory(debtScore), RecommendedActions = GetTopRecommendations(debtAnalysis), ImprovementTimeline = generateImprovementPlan ? $"{improvementPlan.Sum(p => p.EstimatedHours)} hours over {improvementPlan.Count} phases" : null } }; return new AIPluginResult(result, $"Technical debt analysis completed. Overall debt score: {debtScore}/100. " + $"Found {debtAnalysis.DebtItems.Count} debt items requiring {debtAnalysis.DebtItems.Sum(d => d.EstimatedEffort)} hours of effort."); } catch (Exception ex) { return new AIPluginResult(ex, "Failed to analyze technical debt"); } } private async Task AnalyzeComplexityDebt(List sourceFiles, TechnicalDebtAnalysis analysis) { var totalComplexityPoints = 0; var methodCount = 0; var highComplexityMethods = 0; foreach (var filePath in sourceFiles) { var sourceCode = await File.ReadAllTextAsync(filePath); var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode, path: filePath); var root = await syntaxTree.GetRootAsync(); var methods = root.DescendantNodes().OfType(); foreach (var method in methods) { var complexity = CalculateCyclomaticComplexity(method); totalComplexityPoints += complexity; methodCount++; if (complexity > 10) { highComplexityMethods++; var className = GetContainingClassName(method); var methodName = method.Identifier.ValueText; var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; analysis.DebtItems.Add(new DebtItem { Type = "Complexity", Category = "Code Quality", Description = $"High complexity method ({complexity} cyclomatic complexity)", Location = $"{Path.GetFileName(filePath)}:{lineNumber} - {className}.{methodName}", Priority = Math.Min(10, complexity / 2), // Scale 1-10 EstimatedEffort = Math.Max(2, complexity / 3), // Hours to refactor Impact = complexity > 20 ? "High" : complexity > 15 ? "Medium" : "Low", RecommendedAction = "Extract methods, reduce branching, simplify logic" }); } } } analysis.ComplexityDebt.TotalComplexityPoints = totalComplexityPoints; analysis.ComplexityDebt.AverageMethodComplexity = methodCount > 0 ? (double)totalComplexityPoints / methodCount : 0; analysis.ComplexityDebt.HighComplexityMethods = highComplexityMethods; analysis.ComplexityDebt.EstimatedRefactoringHours = highComplexityMethods * 4; // Average 4 hours per complex method } private async Task AnalyzeDocumentationDebtMethod(List sourceFiles, TechnicalDebtAnalysis analysis) { var totalMethods = 0; var documentedMethods = 0; foreach (var filePath in sourceFiles) { var sourceCode = await File.ReadAllTextAsync(filePath); var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode, path: filePath); var root = await syntaxTree.GetRootAsync(); var methods = root.DescendantNodes().OfType() .Where(m => m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.PublicKeyword) || mod.IsKind(SyntaxKind.ProtectedKeyword))); foreach (var method in methods) { totalMethods++; var hasDocumentation = HasXmlDocumentation(method); if (hasDocumentation) { documentedMethods++; } else { var className = GetContainingClassName(method); var methodName = method.Identifier.ValueText; var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1; var priority = IsPublicApi(method) ? 8 : 5; // Higher priority for public APIs analysis.DebtItems.Add(new DebtItem { Type = "Documentation", Category = "Maintainability", Description = "Public method lacks XML documentation", Location = $"{Path.GetFileName(filePath)}:{lineNumber} - {className}.{methodName}", Priority = priority, EstimatedEffort = 0.5, // 30 minutes per method Impact = IsPublicApi(method) ? "Medium" : "Low", RecommendedAction = "Add comprehensive XML documentation with examples" }); } } // Check for class-level documentation var classes = root.DescendantNodes().OfType() .Where(c => c.Modifiers.Any(mod => mod.IsKind(SyntaxKind.PublicKeyword))); foreach (var cls in classes) { if (!HasXmlDocumentation(cls)) { var className = cls.Identifier.ValueText; var lineNumber = cls.GetLocation().GetLineSpan().StartLinePosition.Line + 1; analysis.DebtItems.Add(new DebtItem { Type = "Documentation", Category = "Maintainability", Description = "Public class lacks XML documentation", Location = $"{Path.GetFileName(filePath)}:{lineNumber} - {className}", Priority = 7, EstimatedEffort = 1, // 1 hour per class Impact = "Medium", RecommendedAction = "Add class-level documentation explaining purpose and usage" }); } } } analysis.DocumentationDebt.TotalMethods = totalMethods; analysis.DocumentationDebt.UndocumentedMethods = totalMethods - documentedMethods; analysis.DocumentationDebt.DocumentationCoverage = totalMethods > 0 ? (double)documentedMethods / totalMethods * 100 : 100; analysis.DocumentationDebt.EstimatedDocumentationHours = (totalMethods - documentedMethods) * 0.5; } private async Task AnalyzeDependencyDebt(List projectFiles, TechnicalDebtAnalysis analysis) { var totalDependencies = 0; var outdatedDependencies = 0; var vulnerableDependencies = 0; var majorVersionsBehind = 0; foreach (var projectFile in projectFiles) { if (projectFile.EndsWith(".csproj")) { var projectContent = await File.ReadAllTextAsync(projectFile); var dependencies = ExtractPackageReferences(projectContent); foreach (var dependency in dependencies) { totalDependencies++; // Simulate dependency analysis (in real implementation, you'd query NuGet API) var isOutdated = SimulateOutdatedCheck(dependency); var isVulnerable = SimulateVulnerabilityCheck(dependency); var versionsBehind = SimulateMajorVersionCheck(dependency); if (isOutdated) { outdatedDependencies++; analysis.DebtItems.Add(new DebtItem { Type = "Dependency", Category = "Security & Maintenance", Description = $"Outdated package: {dependency.Name} v{dependency.Version}", Location = Path.GetFileName(projectFile), Priority = isVulnerable ? 9 : 6, EstimatedEffort = versionsBehind > 1 ? 4 : 1, // More effort for major version jumps Impact = isVulnerable ? "High" : versionsBehind > 1 ? "Medium" : "Low", RecommendedAction = $"Update to latest version and test compatibility" }); } if (isVulnerable) { vulnerableDependencies++; } if (versionsBehind > 1) { majorVersionsBehind++; } } } } analysis.DependencyDebt.TotalDependencies = totalDependencies; analysis.DependencyDebt.OutdatedDependencies = outdatedDependencies; analysis.DependencyDebt.VulnerableDependencies = vulnerableDependencies; analysis.DependencyDebt.MajorVersionsBehind = majorVersionsBehind; analysis.DependencyDebt.EstimatedUpgradeHours = outdatedDependencies * 2; // Average 2 hours per upgrade } private async Task AnalyzeTestDebtMethod(List sourceFiles, TechnicalDebtAnalysis analysis) { var productionFiles = sourceFiles.Where(f => !IsTestFile(f)).ToList(); var testFiles = sourceFiles.Where(f => IsTestFile(f)).ToList(); var totalMethods = 0; var testedMethods = 0; // Get all public methods from production code var publicMethods = new List(); foreach (var filePath in productionFiles) { var sourceCode = await File.ReadAllTextAsync(filePath); var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode, path: filePath); var root = await syntaxTree.GetRootAsync(); var methods = root.DescendantNodes().OfType() .Where(m => m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.PublicKeyword))); foreach (var method in methods) { totalMethods++; var className = GetContainingClassName(method); var methodName = method.Identifier.ValueText; publicMethods.Add(new MethodDebtInfo { ClassName = className, MethodName = methodName, FilePath = filePath, LineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line + 1 }); } } // Simple heuristic to estimate test coverage var testMethodNames = new HashSet(); foreach (var testFile in testFiles) { var testCode = await File.ReadAllTextAsync(testFile); var testTree = CSharpSyntaxTree.ParseText(testCode); var testRoot = await testTree.GetRootAsync(); var testMethods = testRoot.DescendantNodes().OfType() .Where(m => HasTestAttribute(m)); foreach (var testMethod in testMethods) { testMethodNames.Add(testMethod.Identifier.ValueText.ToLowerInvariant()); } } // Estimate which methods are tested (simple name matching heuristic) foreach (var method in publicMethods) { var hasTest = testMethodNames.Any(t => t.Contains(method.MethodName.ToLowerInvariant()) || t.Contains(method.ClassName.ToLowerInvariant())); if (hasTest) { testedMethods++; } else { var priority = IsBusinessLogic(method.MethodName) ? 8 : 5; analysis.DebtItems.Add(new DebtItem { Type = "Test", Category = "Quality Assurance", Description = "Public method lacks unit tests", Location = $"{Path.GetFileName(method.FilePath)}:{method.LineNumber} - {method.ClassName}.{method.MethodName}", Priority = priority, EstimatedEffort = 2, // 2 hours per test Impact = IsBusinessLogic(method.MethodName) ? "High" : "Medium", RecommendedAction = "Write comprehensive unit tests with edge cases" }); } } analysis.TestDebt.TotalMethods = totalMethods; analysis.TestDebt.UntestedMethods = totalMethods - testedMethods; analysis.TestDebt.TestCoverage = totalMethods > 0 ? (double)testedMethods / totalMethods * 100 : 100; analysis.TestDebt.EstimatedTestingHours = (totalMethods - testedMethods) * 2; } private int CalculateOverallDebtScore(TechnicalDebtAnalysis analysis) { // Calculate weighted debt score (0-100, higher is better) var score = 100; // Complexity debt impact (weight: 30%) var complexityPenalty = Math.Min(30, analysis.ComplexityDebt.HighComplexityMethods * 3); score -= complexityPenalty; // Documentation debt impact (weight: 20%) var docCoveragePenalty = Math.Min(20, (int)((100 - analysis.DocumentationDebt.DocumentationCoverage) / 5)); score -= docCoveragePenalty; // Dependency debt impact (weight: 25%) var depPenalty = Math.Min(25, analysis.DependencyDebt.OutdatedDependencies * 2); score -= depPenalty; // Test debt impact (weight: 25%) var testCoveragePenalty = Math.Min(25, (int)((100 - analysis.TestDebt.TestCoverage) / 4)); score -= testCoveragePenalty; return Math.Max(0, score); } private List GenerateImprovementPlanMethod(TechnicalDebtAnalysis analysis) { var plan = new List(); // Phase 1: Critical Issues (High priority, high impact) var criticalItems = analysis.DebtItems.Where(d => d.Priority >= 8).ToList(); if (criticalItems.Any()) { plan.Add(new ImprovementAction { Phase = 1, Priority = "Critical", Title = "Address Critical Technical Debt", Description = $"Fix {criticalItems.Count} high-priority issues including security vulnerabilities and complex code", EstimatedHours = criticalItems.Sum(i => i.EstimatedEffort), ExpectedBenefit = "Immediate risk reduction and improved maintainability", Dependencies = new List() }); } // Phase 2: Complexity Reduction if (analysis.ComplexityDebt.HighComplexityMethods > 0) { plan.Add(new ImprovementAction { Phase = 2, Priority = "High", Title = "Refactor Complex Methods", Description = $"Simplify {analysis.ComplexityDebt.HighComplexityMethods} high-complexity methods", EstimatedHours = analysis.ComplexityDebt.EstimatedRefactoringHours, ExpectedBenefit = "Improved code readability and reduced bug risk", Dependencies = new List { "Ensure comprehensive test coverage before refactoring" } }); } // Phase 3: Test Coverage if (analysis.TestDebt.TestCoverage < 80) { plan.Add(new ImprovementAction { Phase = 3, Priority = "High", Title = "Improve Test Coverage", Description = $"Add tests for {analysis.TestDebt.UntestedMethods} untested methods", EstimatedHours = analysis.TestDebt.EstimatedTestingHours, ExpectedBenefit = "Increased confidence in deployments and easier refactoring", Dependencies = new List() }); } // Phase 4: Dependency Updates if (analysis.DependencyDebt.OutdatedDependencies > 0) { plan.Add(new ImprovementAction { Phase = 4, Priority = "Medium", Title = "Update Dependencies", Description = $"Update {analysis.DependencyDebt.OutdatedDependencies} outdated packages", EstimatedHours = analysis.DependencyDebt.EstimatedUpgradeHours, ExpectedBenefit = "Security improvements and access to latest features", Dependencies = new List { "Ensure test coverage before upgrades" } }); } // Phase 5: Documentation if (analysis.DocumentationDebt.DocumentationCoverage < 90) { plan.Add(new ImprovementAction { Phase = 5, Priority = "Medium", Title = "Improve Documentation", Description = $"Document {analysis.DocumentationDebt.UndocumentedMethods} public methods and classes", EstimatedHours = analysis.DocumentationDebt.EstimatedDocumentationHours, ExpectedBenefit = "Better developer experience and easier onboarding", Dependencies = new List() }); } return plan; } private async Task TrackDebtTrends(string projectPath, TechnicalDebtAnalysis currentAnalysis) { var trendsFile = Path.Combine(projectPath, ".technical-debt-trends.json"); var trends = new List(); // Load existing trends if available if (File.Exists(trendsFile)) { try { var existingData = await File.ReadAllTextAsync(trendsFile); trends = JsonSerializer.Deserialize>(existingData) ?? new List(); } catch { // Ignore errors loading existing trends } } // Add current snapshot var snapshot = new TechnicalDebtSnapshot { Date = currentAnalysis.AnalysisDate, DebtScore = CalculateOverallDebtScore(currentAnalysis), ComplexityDebt = currentAnalysis.ComplexityDebt.TotalComplexityPoints, DocumentationCoverage = currentAnalysis.DocumentationDebt.DocumentationCoverage, TestCoverage = currentAnalysis.TestDebt.TestCoverage, OutdatedDependencies = currentAnalysis.DependencyDebt.OutdatedDependencies, TotalDebtItems = currentAnalysis.DebtItems.Count }; trends.Add(snapshot); // Keep only last 30 snapshots if (trends.Count > 30) { trends = trends.OrderByDescending(t => t.Date).Take(30).ToList(); } // Save trends try { var trendsJson = JsonSerializer.Serialize(trends, new JsonSerializerOptions { WriteIndented = true }); await File.WriteAllTextAsync(trendsFile, trendsJson); } catch { // Ignore save errors } // Calculate trend analysis if (trends.Count >= 2) { var previous = trends.OrderByDescending(t => t.Date).Skip(1).First(); var current = snapshot; return new { TrendDirection = current.DebtScore > previous.DebtScore ? "Improving" : current.DebtScore < previous.DebtScore ? "Deteriorating" : "Stable", ScoreChange = current.DebtScore - previous.DebtScore, ComplexityTrend = current.ComplexityDebt - previous.ComplexityDebt, DocumentationTrend = current.DocumentationCoverage - previous.DocumentationCoverage, TestCoverageTrend = current.TestCoverage - previous.TestCoverage, DependencyTrend = current.OutdatedDependencies - previous.OutdatedDependencies, HistoricalData = trends.OrderByDescending(t => t.Date).Take(10).ToList() }; } return new { Message = "Insufficient historical data for trend analysis" }; } // Helper methods private List GetSourceFiles(string path) { var files = new List(); if (File.Exists(path) && path.EndsWith(".cs")) { files.Add(path); } else if (Directory.Exists(path)) { files.AddRange(Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories) .Where(f => !f.Contains("\\bin\\") && !f.Contains("\\obj\\") && !f.EndsWith(".Designer.cs") && !f.EndsWith(".g.cs"))); } return files; } private List GetProjectFiles(string path) { var files = new List(); if (Directory.Exists(path)) { files.AddRange(Directory.GetFiles(path, "*.csproj", SearchOption.AllDirectories)); files.AddRange(Directory.GetFiles(path, "*.vbproj", SearchOption.AllDirectories)); files.AddRange(Directory.GetFiles(path, "packages.config", SearchOption.AllDirectories)); } return files; } private int CalculateCyclomaticComplexity(SyntaxNode node) { int complexity = 1; // Base complexity var descendants = node.DescendantNodes(); // Decision points that increase complexity complexity += descendants.OfType().Count(); complexity += descendants.OfType().Count(); complexity += descendants.OfType().Count(); complexity += descendants.OfType().Count(); complexity += descendants.OfType().Count(); complexity += descendants.OfType().Count(); complexity += descendants.OfType().Count(); complexity += descendants.OfType().Count(); // Logical operators (&& and ||) var binaryExpressions = descendants.OfType(); foreach (var expr in binaryExpressions) { if (expr.OperatorToken.IsKind(SyntaxKind.AmpersandAmpersandToken) || expr.OperatorToken.IsKind(SyntaxKind.BarBarToken)) { complexity++; } } return complexity; } private string GetContainingClassName(SyntaxNode node) { var classDeclaration = node.Ancestors().OfType().FirstOrDefault(); if (classDeclaration != null) { return classDeclaration.Identifier.ValueText; } var structDeclaration = node.Ancestors().OfType().FirstOrDefault(); if (structDeclaration != null) { return structDeclaration.Identifier.ValueText; } return "Unknown"; } private bool HasXmlDocumentation(SyntaxNode node) { var documentationComment = node.GetLeadingTrivia() .FirstOrDefault(t => t.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) || t.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia)); return !documentationComment.IsKind(SyntaxKind.None); } private bool IsPublicApi(MethodDeclarationSyntax method) { return method.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)); } private List ExtractPackageReferences(string projectContent) { var packages = new List(); // Simple regex to extract PackageReference elements var packagePattern = @" package.Name.Contains(vp)) && SimulateOutdatedCheck(package); } private int SimulateMajorVersionCheck(PackageReference package) { // Simulate major version difference calculation var random = new Random(package.Name.GetHashCode() + 1); return random.Next(0, 4); // 0-3 major versions behind } private bool IsTestFile(string filePath) { var fileName = Path.GetFileName(filePath).ToLowerInvariant(); var directory = Path.GetDirectoryName(filePath)?.ToLowerInvariant() ?? string.Empty; return fileName.Contains("test") || fileName.Contains("spec") || directory.Contains("test") || directory.Contains("spec") || fileName.EndsWith("tests.cs") || fileName.EndsWith("test.cs"); } private bool HasTestAttribute(MethodDeclarationSyntax method) { var attributes = method.AttributeLists.SelectMany(al => al.Attributes); var testAttributes = new[] { "Test", "TestMethod", "Fact", "Theory" }; return attributes.Any(attr => testAttributes.Any(ta => attr.Name.ToString().Contains(ta))); } private bool IsBusinessLogic(string methodName) { var businessKeywords = new[] { "Calculate", "Process", "Validate", "Execute", "Handle", "Manage" }; return businessKeywords.Any(keyword => methodName.Contains(keyword)); } private string GetDebtLevel(int value, string category) { return category switch { "Complexity" => value > 50 ? "Critical" : value > 20 ? "High" : value > 10 ? "Medium" : "Low", "Documentation" => value > 100 ? "Critical" : value > 50 ? "High" : value > 20 ? "Medium" : "Low", "Dependency" => value > 20 ? "Critical" : value > 10 ? "High" : value > 5 ? "Medium" : "Low", "Test" => value > 100 ? "Critical" : value > 50 ? "High" : value > 20 ? "Medium" : "Low", _ => "Unknown" }; } private string GetOverallDebtCategory(int debtScore) { return debtScore switch { >= 80 => "Excellent - Low technical debt", >= 60 => "Good - Manageable technical debt", >= 40 => "Fair - Moderate technical debt requiring attention", >= 20 => "Poor - High technical debt needs immediate action", _ => "Critical - Severe technical debt blocking progress" }; } private List GetTopRecommendations(TechnicalDebtAnalysis analysis) { var recommendations = new List(); // Get top 5 recommendations based on priority and impact var topItems = analysis.DebtItems .OrderByDescending(d => d.Priority) .ThenByDescending(d => d.Impact == "High" ? 3 : d.Impact == "Medium" ? 2 : 1) .Take(5); foreach (var item in topItems) { recommendations.Add($"{item.Type}: {item.RecommendedAction}"); } if (!recommendations.Any()) { recommendations.Add("Continue maintaining current code quality standards"); } return recommendations; } private bool GetBoolParameter(IReadOnlyDictionary parameters, string key, bool defaultValue) { return parameters.TryGetValue(key, out var value) ? Convert.ToBoolean(value) : defaultValue; } } // Supporting data structures public class TechnicalDebtAnalysis { public string ProjectPath { get; set; } = string.Empty; public DateTime AnalysisDate { get; set; } public ComplexityDebtMetrics ComplexityDebt { get; set; } = new(); public DocumentationDebtMetrics DocumentationDebt { get; set; } = new(); public DependencyDebtMetrics DependencyDebt { get; set; } = new(); public TestDebtMetrics TestDebt { get; set; } = new(); public List DebtItems { get; set; } = new(); } public class ComplexityDebtMetrics { public int TotalComplexityPoints { get; set; } public double AverageMethodComplexity { get; set; } public int HighComplexityMethods { get; set; } public double EstimatedRefactoringHours { get; set; } } public class DocumentationDebtMetrics { public int TotalMethods { get; set; } public int UndocumentedMethods { get; set; } public double DocumentationCoverage { get; set; } public double EstimatedDocumentationHours { get; set; } } public class DependencyDebtMetrics { public int TotalDependencies { get; set; } public int OutdatedDependencies { get; set; } public int VulnerableDependencies { get; set; } public int MajorVersionsBehind { get; set; } public double EstimatedUpgradeHours { get; set; } } public class TestDebtMetrics { public int TotalMethods { get; set; } public int UntestedMethods { get; set; } public double TestCoverage { get; set; } public double EstimatedTestingHours { get; set; } } public class DebtItem { public string Type { get; set; } = string.Empty; public string Category { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string Location { get; set; } = string.Empty; public int Priority { get; set; } // 1-10 scale public double EstimatedEffort { get; set; } // Hours public string Impact { get; set; } = string.Empty; // Low, Medium, High public string RecommendedAction { get; set; } = string.Empty; } public class ImprovementAction { public int Phase { get; set; } public string Priority { get; set; } = string.Empty; public string Title { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public double EstimatedHours { get; set; } public string ExpectedBenefit { get; set; } = string.Empty; public List Dependencies { get; set; } = new(); } public class PackageReference { public string Name { get; set; } = string.Empty; public string Version { get; set; } = string.Empty; } public class MethodDebtInfo { public string ClassName { get; set; } = string.Empty; public string MethodName { get; set; } = string.Empty; public string FilePath { get; set; } = string.Empty; public int LineNumber { get; set; } } public class TechnicalDebtSnapshot { public DateTime Date { get; set; } public int DebtScore { get; set; } public int ComplexityDebt { get; set; } public double DocumentationCoverage { get; set; } public double TestCoverage { get; set; } public int OutdatedDependencies { get; set; } public int TotalDebtItems { get; set; } } }