MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Learning/SelfLearningRefactorPlugin.cs

727 lines
25 KiB
C#
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Fixed SelfLearningRefactorPlugin.cs
using MarketAlly.AIPlugin;
using MarketAlly.AIPlugin.Analysis.Plugins;
using MarketAlly.AIPlugin.Refactoring.Plugins;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
namespace MarketAlly.AIPlugin.Learning
{
[AIPlugin("SelfLearningRefactor", "AI-powered self-learning refactoring system that learns from compilation results")]
public class SelfLearningRefactorPlugin : IAIPlugin
{
[AIParameter("Solution path to analyze and improve", required: true)]
public string SolutionPath { get; set; }
[AIParameter("RefactorIQ database path", required: true)]
public string DatabasePath { get; set; }
[AIParameter("ModularMap analysis JSON path", required: false)]
public string ModularMapPath { get; set; }
[AIParameter("Maximum learning iterations", required: false)]
public int MaxIterations { get; set; } = 5;
[AIParameter("Apply changes automatically", required: false)]
public bool AutoApply { get; set; } = false;
[AIParameter("Backup before changes", required: false)]
public bool CreateBackup { get; set; } = true;
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
{
["solutionPath"] = typeof(string),
["databasePath"] = typeof(string),
["modularMapPath"] = typeof(string),
["maxIterations"] = typeof(int),
["autoApply"] = typeof(bool),
["createBackup"] = typeof(bool)
};
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
{
try
{
var solutionPath = parameters["solutionPath"].ToString();
var databasePath = parameters["databasePath"].ToString();
var modularMapPath = parameters.TryGetValue("modularMapPath", out var mapPath) ? mapPath?.ToString() : null;
var maxIterations = parameters.TryGetValue("maxIterations", out var maxIter) ? Convert.ToInt32(maxIter) : 5;
var autoApply = parameters.TryGetValue("autoApply", out var autoApplyVal) ? Convert.ToBoolean(autoApplyVal) : false;
var createBackup = parameters.TryGetValue("createBackup", out var backupVal) ? Convert.ToBoolean(backupVal) : true;
var learningSession = new LearningSession
{
SolutionPath = solutionPath,
DatabasePath = databasePath,
ModularMapPath = modularMapPath,
MaxIterations = maxIterations,
AutoApply = autoApply,
CreateBackup = createBackup,
StartTime = DateTime.UtcNow
};
// Initialize learning system
var learningEngine = new SelfLearningEngine(learningSession);
// Execute learning workflow
var result = await learningEngine.ExecuteLearningCycleAsync();
return new AIPluginResult(result, "Self-learning refactoring completed");
}
catch (Exception ex)
{
return new AIPluginResult(ex, $"Self-learning refactoring failed: {ex.Message}");
}
}
}
public class SelfLearningEngine
{
private readonly LearningSession _session;
private readonly RefactorIQRepository _repository;
private readonly CompilationValidator _validator;
private readonly ModularAnalyzer _modularAnalyzer;
private readonly ILogger<AIPluginRegistry> _logger;
public SelfLearningEngine(LearningSession session, ILogger<AIPluginRegistry> logger = null)
{
_session = session;
_repository = new RefactorIQRepository(session.DatabasePath);
_validator = new CompilationValidator();
_modularAnalyzer = new ModularAnalyzer();
_logger = logger ?? CreateNullLogger();
}
private static ILogger<AIPluginRegistry> CreateNullLogger()
{
using var loggerFactory = LoggerFactory.Create(builder => { });
return loggerFactory.CreateLogger<AIPluginRegistry>();
}
public async Task<LearningResult> ExecuteLearningCycleAsync()
{
var result = new LearningResult
{
SessionId = Guid.NewGuid(),
StartTime = _session.StartTime,
Iterations = new List<LearningIteration>()
};
Console.WriteLine($"🚀 Starting self-learning cycle for: {Path.GetFileName(_session.SolutionPath)}");
// Load modular analysis if provided
ModularMapData modularMap = null;
if (!string.IsNullOrEmpty(_session.ModularMapPath) && File.Exists(_session.ModularMapPath))
{
modularMap = await _modularAnalyzer.LoadModularMapAsync(_session.ModularMapPath);
Console.WriteLine($"📊 Loaded modular analysis: {modularMap.Statistics.TotalModules} modules");
}
// Create backup if requested
if (_session.CreateBackup)
{
await CreateBackupAsync(result);
}
// Get baseline compilation status
var baselineCompilation = await _validator.ValidateCompilationAsync(_session.SolutionPath);
result.BaselineCompilation = baselineCompilation;
Console.WriteLine($"📋 Baseline: {baselineCompilation.Status} ({baselineCompilation.ErrorCount} errors, {baselineCompilation.WarningCount} warnings)");
// Learning iterations
for (int iteration = 1; iteration <= _session.MaxIterations; iteration++)
{
Console.WriteLine($"\n🔄 Learning Iteration {iteration}/{_session.MaxIterations}");
var iterationResult = await ExecuteLearningIterationAsync(iteration, modularMap, result);
result.Iterations.Add(iterationResult);
// Stop if we achieved perfect compilation or hit critical errors
if (iterationResult.PostChangeCompilation?.Status == CompilationStatus.Success ||
iterationResult.CriticalError)
{
Console.WriteLine($"🎯 Stopping early: {(iterationResult.PostChangeCompilation?.Status == CompilationStatus.Success ? "Perfect compilation achieved" : "Critical error encountered")}");
break;
}
// Apply learning from this iteration
await ApplyLearningFromIterationAsync(iterationResult);
}
result.EndTime = DateTime.UtcNow;
result.TotalDuration = result.EndTime - result.StartTime;
// Store learning session in database
await _repository.StoreLearningSessionAsync(result);
Console.WriteLine($"✅ Learning complete. Duration: {result.TotalDuration.TotalMinutes:F1} minutes");
return result;
}
private async Task<LearningIteration> ExecuteLearningIterationAsync(int iterationNumber, ModularMapData modularMap, LearningResult sessionResult)
{
var iteration = new LearningIteration
{
IterationNumber = iterationNumber,
StartTime = DateTime.UtcNow,
Suggestions = new List<AISuggestion>(),
AppliedChanges = new List<AppliedChange>()
};
try
{
// 1. Analyze current code state
Console.WriteLine("🔍 Analyzing current code state...");
var codeAnalysis = await AnalyzeCurrentCodeStateAsync(modularMap);
iteration.CodeAnalysis = codeAnalysis;
// 2. Generate AI suggestions based on patterns and database
Console.WriteLine("🧠 Generating AI suggestions...");
var suggestions = await GenerateAISuggestionsAsync(codeAnalysis, sessionResult);
iteration.Suggestions = suggestions;
if (!suggestions.Any())
{
Console.WriteLine(" No suggestions generated for this iteration");
return iteration;
}
// 3. Select best suggestion based on learning history
var selectedSuggestion = await SelectBestSuggestionAsync(suggestions, sessionResult);
Console.WriteLine($"💡 Selected suggestion: {selectedSuggestion.Type} (confidence: {selectedSuggestion.Confidence:F2})");
// 4. Apply suggestion and validate
if (_session.AutoApply || await PromptForApplicationAsync(selectedSuggestion))
{
var change = await ApplySuggestionAsync(selectedSuggestion);
iteration.AppliedChanges.Add(change);
// 5. Validate compilation after change
Console.WriteLine("🔨 Validating compilation...");
var postCompilation = await _validator.ValidateCompilationAsync(_session.SolutionPath);
iteration.PostChangeCompilation = postCompilation;
// 6. Update learning metrics
await UpdateLearningMetricsAsync(selectedSuggestion, change, postCompilation);
Console.WriteLine($"📊 Post-change: {postCompilation.Status} ({postCompilation.ErrorCount} errors, {postCompilation.WarningCount} warnings)");
}
}
catch (Exception ex)
{
iteration.CriticalError = true;
iteration.ErrorMessage = ex.Message;
Console.WriteLine($"❌ Critical error in iteration {iterationNumber}: {ex.Message}");
}
finally
{
iteration.EndTime = DateTime.UtcNow;
iteration.Duration = iteration.EndTime - iteration.StartTime;
}
return iteration;
}
private async Task<CodeAnalysisSnapshot> AnalyzeCurrentCodeStateAsync(ModularMapData modularMap)
{
var snapshot = new CodeAnalysisSnapshot
{
Timestamp = DateTime.UtcNow,
Issues = new List<CodeIssue>(),
Metrics = new CodeMetrics()
};
// Analyze using Roslyn
using var workspace = MSBuildWorkspace.Create();
var solution = await workspace.OpenSolutionAsync(_session.SolutionPath);
// Create a simple plugin registry for analysis
var logger = CreateNullLogger();
var pluginRegistry = new AIPluginRegistry(logger);
pluginRegistry.RegisterPlugin(new CodeAnalysisPlugin());
foreach (var project in solution.Projects)
{
var compilation = await project.GetCompilationAsync();
foreach (var document in project.Documents)
{
if (document.FilePath?.EndsWith(".cs") != true) continue;
var syntaxTree = await document.GetSyntaxTreeAsync();
var root = await syntaxTree.GetRootAsync();
// Extract metrics and issues
try
{
var analysisResult = await pluginRegistry.CallFunctionAsync("CodeAnalysis", new Dictionary<string, object>
{
["path"] = document.FilePath,
["analysisDepth"] = "detailed"
});
if (analysisResult.Success && analysisResult.Data != null)
{
// Process analysis results
await ProcessAnalysisResultsAsync(snapshot, analysisResult.Data, document.FilePath);
}
}
catch (Exception ex)
{
_logger.LogWarning($"Failed to analyze {document.FilePath}: {ex.Message}");
}
}
}
// Integrate modular map insights
if (modularMap != null)
{
snapshot.ModularInsights = ExtractModularInsights(modularMap);
}
return snapshot;
}
private async Task<List<AISuggestion>> GenerateAISuggestionsAsync(CodeAnalysisSnapshot analysis, LearningResult sessionResult)
{
var suggestions = new List<AISuggestion>();
// Query database for historical patterns
var historicalPatterns = await _repository.GetSuccessfulPatternsAsync();
var previousFailures = await _repository.GetFailurePatternsAsync();
// Generate suggestions based on code issues
foreach (var issue in analysis.Issues.Take(5)) // Limit to top 5 issues
{
var suggestion = await GenerateSuggestionForIssueAsync(issue, historicalPatterns, previousFailures);
if (suggestion != null)
{
suggestions.Add(suggestion);
}
}
// Generate modular improvement suggestions
if (analysis.ModularInsights?.Any() == true)
{
var modularSuggestions = await GenerateModularSuggestionsAsync(analysis.ModularInsights);
suggestions.AddRange(modularSuggestions);
}
// Rank suggestions by confidence and learning history
return suggestions.OrderByDescending(s => s.Confidence).ToList();
}
private async Task<AISuggestion> GenerateSuggestionForIssueAsync(CodeIssue issue, List<SuccessPattern> patterns, List<FailurePattern> failures)
{
// Find matching patterns
var matchingPattern = patterns.FirstOrDefault(p => p.IssueType == issue.Type);
var avoidPatterns = failures.Where(f => f.IssueType == issue.Type).ToList();
if (matchingPattern == null) return null;
var suggestion = new AISuggestion
{
Id = Guid.NewGuid(),
Type = DetermineSuggestionType(issue.Type),
Description = GenerateDescriptionFromPattern(matchingPattern, issue),
TargetFile = issue.FilePath,
TargetLine = issue.LineNumber,
Confidence = CalculateConfidence(matchingPattern, avoidPatterns),
GeneratedAt = DateTime.UtcNow,
ExpectedImprovement = matchingPattern.AverageImprovement,
RiskLevel = CalculateRiskLevel(matchingPattern, avoidPatterns)
};
// Generate specific code change suggestion (simplified)
suggestion.ProposedChange = await GenerateCodeChangeSuggestionAsync(issue, matchingPattern);
return suggestion;
}
// Fixed: Add the missing GenerateCodeChangeSuggestionAsync method
private async Task<string> GenerateCodeChangeSuggestionAsync(CodeIssue issue, SuccessPattern pattern)
{
// Generate a text-based suggestion for the code change
var suggestionText = issue.Type.ToLower() switch
{
"long method" => $"Extract method to reduce complexity in {Path.GetFileName(issue.FilePath)} at line {issue.LineNumber}",
"meaningless name" => $"Rename variable to use meaningful name in {Path.GetFileName(issue.FilePath)} at line {issue.LineNumber}",
"missing documentation" => $"Add XML documentation to method in {Path.GetFileName(issue.FilePath)} at line {issue.LineNumber}",
"complex expression" => $"Simplify expression in {Path.GetFileName(issue.FilePath)} at line {issue.LineNumber}",
"unused code" => $"Remove unused code in {Path.GetFileName(issue.FilePath)} at line {issue.LineNumber}",
_ => $"Apply {pattern.PatternName} improvement to {Path.GetFileName(issue.FilePath)} at line {issue.LineNumber}"
};
return await Task.FromResult(suggestionText);
}
private async Task<AISuggestion> SelectBestSuggestionAsync(List<AISuggestion> suggestions, LearningResult sessionResult)
{
// Weight suggestions by:
// 1. Confidence score
// 2. Expected improvement
// 3. Risk level (lower is better)
// 4. Learning from previous iterations
var weighted = suggestions.Select(s => new
{
Suggestion = s,
Score = (s.Confidence * 0.4) +
(s.ExpectedImprovement * 0.3) +
((1.0 - s.RiskLevel) * 0.2) +
(GetLearningBonus(s, sessionResult) * 0.1)
}).OrderByDescending(w => w.Score);
return weighted.First().Suggestion;
}
private async Task<AppliedChange> ApplySuggestionAsync(AISuggestion suggestion)
{
var change = new AppliedChange
{
SuggestionId = suggestion.Id,
FilePath = suggestion.TargetFile,
StartTime = DateTime.UtcNow,
Success = false
};
try
{
// Read original file
var originalContent = await File.ReadAllTextAsync(suggestion.TargetFile);
change.OriginalContent = originalContent;
// Apply the suggested change using appropriate plugin
var modifiedContent = await ApplyCodeChangeAsync(originalContent, suggestion);
change.ModifiedContent = modifiedContent;
// Write modified content
await File.WriteAllTextAsync(suggestion.TargetFile, modifiedContent);
change.Success = true;
Console.WriteLine($"✅ Applied change to {Path.GetFileName(suggestion.TargetFile)}");
}
catch (Exception ex)
{
change.ErrorMessage = ex.Message;
Console.WriteLine($"❌ Failed to apply change: {ex.Message}");
}
finally
{
change.EndTime = DateTime.UtcNow;
}
return change;
}
private async Task<string> ApplyCodeChangeAsync(string originalContent, AISuggestion suggestion)
{
// Create a simple plugin registry for applying changes
var logger = CreateNullLogger();
var pluginRegistry = new AIPluginRegistry(logger);
// Register required plugins
pluginRegistry.RegisterPlugin(new EnhancedDocumentationGeneratorPlugin());
pluginRegistry.RegisterPlugin(new CodeFormatterPlugin());
pluginRegistry.RegisterPlugin(new NamingConventionPlugin());
try
{
// Apply changes based on suggestion type
var result = suggestion.Type switch
{
SuggestionType.AddDocumentation => await pluginRegistry.CallFunctionAsync("EnhancedDocumentationGenerator", new Dictionary<string, object>
{
["filePath"] = suggestion.TargetFile,
["style"] = "intelligent",
["applyChanges"] = false
}),
SuggestionType.RenameVariable => await pluginRegistry.CallFunctionAsync("NamingConvention", new Dictionary<string, object>
{
["filePath"] = suggestion.TargetFile,
["convention"] = "pascal",
["applyChanges"] = false
}),
_ => await pluginRegistry.CallFunctionAsync("CodeFormatter", new Dictionary<string, object>
{
["path"] = suggestion.TargetFile,
["formattingStyle"] = "microsoft",
["applyChanges"] = false
})
};
if (result.Success && result.Data != null)
{
// Extract modified content from plugin result
var data = JsonSerializer.Deserialize<JsonElement>(JsonSerializer.Serialize(result.Data));
if (data.TryGetProperty("ModifiedContent", out var contentElement))
{
return contentElement.GetString() ?? originalContent;
}
}
}
catch (Exception ex)
{
_logger.LogWarning($"Failed to apply code change: {ex.Message}");
}
// Fallback: return original content with minimal formatting
return await FormatCodeBasicAsync(originalContent);
}
private async Task<string> FormatCodeBasicAsync(string code)
{
try
{
// Parse and reformat the code
var syntaxTree = CSharpSyntaxTree.ParseText(code);
var root = await syntaxTree.GetRootAsync();
return root.NormalizeWhitespace().ToFullString();
}
catch
{
// If parsing fails, return original
return code;
}
}
private async Task UpdateLearningMetricsAsync(AISuggestion suggestion, AppliedChange change, CompilationResult compilation)
{
var learningRecord = new LearningRecord
{
SuggestionId = suggestion.Id,
SuggestionType = suggestion.Type,
Confidence = suggestion.Confidence,
ExpectedImprovement = suggestion.ExpectedImprovement,
ActualImprovement = CalculateActualImprovement(compilation),
Success = change.Success && compilation.Status != CompilationStatus.Failed,
CompilationStatus = compilation.Status,
ErrorCountBefore = compilation.PreviousErrorCount ?? 0,
ErrorCountAfter = compilation.ErrorCount,
Timestamp = DateTime.UtcNow
};
await _repository.StoreLearningRecordAsync(learningRecord);
}
private async Task ApplyLearningFromIterationAsync(LearningIteration iteration)
{
// Update confidence models based on results
foreach (var change in iteration.AppliedChanges.Where(c => c.Success))
{
var suggestion = iteration.Suggestions.First(s => s.Id == change.SuggestionId);
// If compilation improved, boost confidence for this pattern
if (iteration.PostChangeCompilation?.ErrorCount < iteration.PostChangeCompilation?.PreviousErrorCount)
{
await _repository.BoostPatternConfidenceAsync(suggestion.Type, 0.1);
}
else if (iteration.PostChangeCompilation?.Status == CompilationStatus.Failed)
{
// If compilation failed, reduce confidence
await _repository.ReducePatternConfidenceAsync(suggestion.Type, 0.2);
}
}
}
// Helper methods
private double CalculateConfidence(SuccessPattern pattern, List<FailurePattern> failures)
{
var baseConfidence = pattern.SuccessRate;
var failurePenalty = failures.Sum(f => f.FailureRate) / Math.Max(failures.Count, 1);
return Math.Max(0.1, Math.Min(1.0, baseConfidence - (failurePenalty * 0.5)));
}
private double CalculateRiskLevel(SuccessPattern pattern, List<FailurePattern> failures)
{
return failures.Any() ? failures.Average(f => f.FailureRate) : 0.1;
}
private double GetLearningBonus(AISuggestion suggestion, LearningResult sessionResult)
{
// Boost suggestions that worked well in previous iterations
var previousSuccesses = sessionResult.Iterations
.SelectMany(i => i.AppliedChanges)
.Where(c => c.Success)
.Count();
return previousSuccesses > 0 ? 0.1 : 0.0;
}
private async Task CreateBackupAsync(LearningResult result)
{
var backupDir = Path.Combine(Path.GetDirectoryName(_session.SolutionPath), $"backup_{DateTime.Now:yyyyMMdd_HHmmss}");
Directory.CreateDirectory(backupDir);
var solutionDir = Path.GetDirectoryName(_session.SolutionPath);
await CopyDirectoryAsync(solutionDir, backupDir);
result.BackupPath = backupDir;
Console.WriteLine($"💾 Backup created: {backupDir}");
}
private async Task CopyDirectoryAsync(string sourceDir, string destDir)
{
Directory.CreateDirectory(destDir);
foreach (var file in Directory.GetFiles(sourceDir))
{
var destFile = Path.Combine(destDir, Path.GetFileName(file));
File.Copy(file, destFile, true);
}
foreach (var subDir in Directory.GetDirectories(sourceDir))
{
if (Path.GetFileName(subDir).StartsWith(".") ||
Path.GetFileName(subDir) == "bin" ||
Path.GetFileName(subDir) == "obj") continue;
var destSubDir = Path.Combine(destDir, Path.GetFileName(subDir));
await CopyDirectoryAsync(subDir, destSubDir);
}
}
private async Task<bool> PromptForApplicationAsync(AISuggestion suggestion)
{
Console.WriteLine($"\n💡 Suggested Change:");
Console.WriteLine($" Type: {suggestion.Type}");
Console.WriteLine($" File: {Path.GetFileName(suggestion.TargetFile)}");
Console.WriteLine($" Line: {suggestion.TargetLine}");
Console.WriteLine($" Description: {suggestion.Description}");
Console.WriteLine($" Confidence: {suggestion.Confidence:F2}");
Console.WriteLine($" Risk: {suggestion.RiskLevel:F2}");
Console.Write("Apply this change? (y/n): ");
var response = Console.ReadLine();
return response?.ToLower().StartsWith("y") == true;
}
private double CalculateActualImprovement(CompilationResult compilation)
{
if (compilation.PreviousErrorCount.HasValue)
{
var errorReduction = compilation.PreviousErrorCount.Value - compilation.ErrorCount;
return errorReduction / Math.Max(compilation.PreviousErrorCount.Value, 1.0);
}
return 0.0;
}
private async Task ProcessAnalysisResultsAsync(CodeAnalysisSnapshot snapshot, object analysisData, string filePath)
{
// Convert analysis results to code issues
var json = JsonSerializer.Serialize(analysisData);
var data = JsonSerializer.Deserialize<JsonElement>(json);
if (data.TryGetProperty("DetailedResults", out var results) && results.ValueKind == JsonValueKind.Array)
{
foreach (var result in results.EnumerateArray())
{
if (result.TryGetProperty("CodeSmells", out var smells) && smells.ValueKind == JsonValueKind.Array)
{
foreach (var smell in smells.EnumerateArray())
{
var issue = new CodeIssue
{
Type = smell.TryGetProperty("Type", out var type) ? type.GetString() : "Unknown",
Description = smell.TryGetProperty("Description", out var desc) ? desc.GetString() : "",
FilePath = filePath,
Severity = smell.TryGetProperty("Severity", out var sev) ? sev.GetString() : "Medium"
};
// Extract line number from location if available
if (smell.TryGetProperty("Location", out var location))
{
var locationStr = location.GetString();
if (locationStr.StartsWith("Line "))
{
if (int.TryParse(locationStr.Substring(5), out var lineNum))
{
issue.LineNumber = lineNum;
}
}
}
snapshot.Issues.Add(issue);
}
}
}
}
}
private List<ModularInsight> ExtractModularInsights(ModularMapData modularMap)
{
var insights = new List<ModularInsight>();
// Extract insights from coupling metrics
if (modularMap.CouplingMetrics?.HighlyCoupledModules?.Any() == true)
{
foreach (var module in modularMap.CouplingMetrics.HighlyCoupledModules)
{
insights.Add(new ModularInsight
{
Type = "HighCoupling",
Module = module,
Description = $"Module {module} has high coupling",
Severity = "High"
});
}
}
return insights;
}
private SuggestionType DetermineSuggestionType(string issueType)
{
return issueType.ToLower() switch
{
"long method" => SuggestionType.ExtractMethod,
"meaningless name" => SuggestionType.RenameVariable,
"missing documentation" => SuggestionType.AddDocumentation,
"complex expression" => SuggestionType.SimplifyExpression,
"unused code" => SuggestionType.RemoveDeadCode,
_ => SuggestionType.Other
};
}
private string GenerateDescriptionFromPattern(SuccessPattern pattern, CodeIssue issue)
{
return $"Apply {pattern.PatternName} to resolve {issue.Type} in {Path.GetFileName(issue.FilePath)}";
}
private async Task<List<AISuggestion>> GenerateModularSuggestionsAsync(List<ModularInsight> insights)
{
var suggestions = new List<AISuggestion>();
foreach (var insight in insights.Take(3)) // Limit modular suggestions
{
if (insight.Type == "HighCoupling")
{
suggestions.Add(new AISuggestion
{
Id = Guid.NewGuid(),
Type = SuggestionType.ReduceCoupling,
Description = $"Extract interface to reduce coupling in {insight.Module}",
Confidence = 0.7,
ExpectedImprovement = 0.3,
RiskLevel = 0.2,
GeneratedAt = DateTime.UtcNow
});
}
}
return suggestions;
}
}
}