MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Analysis/ModularMapPlugin.cs

3671 lines
120 KiB
C#
Executable File

using MarketAlly.AIPlugin;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Microsoft.Extensions.Logging;
using System.Text;
namespace MarketAlly.AIPlugin.Analysis.Plugins
{
[AIPlugin("ModularMap", "Generates visual dependency maps and analyzes modular architecture patterns")]
public class ModularMapPlugin : IAIPlugin
{
private readonly ILogger<ModularMapPlugin>? _logger;
public ModularMapPlugin(ILogger<ModularMapPlugin>? logger = null)
{
_logger = logger;
}
[AIParameter("Full path to the project or solution directory", required: true)]
public string ProjectPath { get; set; } = string.Empty;
[AIParameter("Map output format: json, mermaid, cytoscape, graphviz", required: false)]
public string OutputFormat { get; set; } = "json";
[AIParameter("Include external dependencies (NuGet packages)", required: false)]
public bool IncludeExternalDependencies { get; set; } = false;
[AIParameter("Include internal project references only", required: false)]
public bool InternalOnly { get; set; } = true;
[AIParameter("Analyze coupling and cohesion metrics", required: false)]
public bool AnalyzeCoupling { get; set; } = true;
[AIParameter("Generate architectural insights and recommendations", required: false)]
public bool GenerateInsights { get; set; } = true;
[AIParameter("Maximum dependency depth to analyze", required: false)]
public int MaxDepth { get; set; } = 5;
[AIParameter("Filter by namespace pattern (e.g., 'MyCompany.*')", required: false)]
public string NamespaceFilter { get; set; } = string.Empty;
[AIParameter("Include method-level dependencies", required: false)]
public bool IncludeMethodLevel { get; set; } = false;
[AIParameter("Output file path for the map", required: false)]
public string OutputPath { get; set; } = string.Empty;
[AIParameter("Enable advanced module identification and grouping", required: false)]
public bool EnableModuleGrouping { get; set; } = true;
[AIParameter("Module grouping strategy: namespace, folder, feature, auto", required: false)]
public string ModuleGroupingStrategy { get; set; } = "auto";
[AIParameter("Detect platform-specific modules", required: false)]
public bool DetectPlatformModules { get; set; } = true;
[AIParameter("Include module entry points and public interfaces", required: false)]
public bool IncludeEntryPoints { get; set; } = true;
[AIParameter("Generate reusable module definitions", required: false)]
public bool GenerateModuleDefinitions { get; set; } = true;
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
{
["projectPath"] = typeof(string),
["outputFormat"] = typeof(string),
["includeExternalDependencies"] = typeof(bool),
["internalOnly"] = typeof(bool),
["analyzeCoupling"] = typeof(bool),
["generateInsights"] = typeof(bool),
["maxDepth"] = typeof(int),
["namespaceFilter"] = typeof(string),
["includeMethodLevel"] = typeof(bool),
["outputPath"] = typeof(string),
["detectPatterns"] = typeof(bool),
["enableModuleGrouping"] = typeof(bool),
["moduleGroupingStrategy"] = typeof(string),
["detectPlatformModules"] = typeof(bool),
["includeEntryPoints"] = typeof(bool),
["generateModuleDefinitions"] = typeof(bool),
["generateScaffoldingMetadata"] = typeof(bool),
["includeLlmDescriptions"] = typeof(bool),
["analyzeModuleTags"] = typeof(bool),
["includeModuleFlags"] = typeof(bool)
};
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
{
try
{
// Extract parameters
string projectPath = parameters["projectPath"].ToString() ?? string.Empty;
string outputFormat = parameters.GetValueOrDefault("outputFormat", "json")?.ToString() ?? "json";
bool includeExternal = Convert.ToBoolean(parameters.GetValueOrDefault("includeExternalDependencies", false));
bool internalOnly = Convert.ToBoolean(parameters.GetValueOrDefault("internalOnly", true));
bool analyzeCoupling = Convert.ToBoolean(parameters.GetValueOrDefault("analyzeCoupling", true));
bool generateInsights = Convert.ToBoolean(parameters.GetValueOrDefault("generateInsights", true));
int maxDepth = Convert.ToInt32(parameters.GetValueOrDefault("maxDepth", 5));
string? namespaceFilter = parameters.GetValueOrDefault("namespaceFilter", string.Empty)?.ToString();
bool includeMethodLevel = Convert.ToBoolean(parameters.GetValueOrDefault("includeMethodLevel", false));
string? outputPath = parameters.GetValueOrDefault("outputPath", string.Empty)?.ToString();
bool detectPatterns = Convert.ToBoolean(parameters.GetValueOrDefault("detectPatterns", true));
bool enableModuleGrouping = Convert.ToBoolean(parameters.GetValueOrDefault("enableModuleGrouping", true));
string moduleGroupingStrategy = parameters.GetValueOrDefault("moduleGroupingStrategy", "auto")?.ToString() ?? "auto";
bool detectPlatformModules = Convert.ToBoolean(parameters.GetValueOrDefault("detectPlatformModules", true));
bool includeEntryPoints = Convert.ToBoolean(parameters.GetValueOrDefault("includeEntryPoints", true));
bool generateModuleDefinitions = Convert.ToBoolean(parameters.GetValueOrDefault("generateModuleDefinitions", true));
bool generateScaffoldingMetadata = Convert.ToBoolean(parameters.GetValueOrDefault("generateScaffoldingMetadata", true));
bool includeLlmDescriptions = Convert.ToBoolean(parameters.GetValueOrDefault("includeLlmDescriptions", true));
bool analyzeModuleTags = Convert.ToBoolean(parameters.GetValueOrDefault("analyzeModuleTags", true));
bool includeModuleFlags = Convert.ToBoolean(parameters.GetValueOrDefault("includeModuleFlags", true));
// Validate project path
if (!Directory.Exists(projectPath) && !File.Exists(projectPath))
{
return new AIPluginResult(
new DirectoryNotFoundException($"Project path not found: {projectPath}"),
"Invalid project path"
);
}
_logger?.LogInformation("Starting modular map analysis for {ProjectPath}", projectPath);
// Analyze project structure
var analysisResult = await AnalyzeProjectStructureAsync(projectPath, new AnalysisOptions
{
IncludeExternalDependencies = includeExternal,
InternalOnly = internalOnly,
MaxDepth = maxDepth,
NamespaceFilter = namespaceFilter,
IncludeMethodLevel = includeMethodLevel,
EnableModuleGrouping = enableModuleGrouping,
ModuleGroupingStrategy = moduleGroupingStrategy,
DetectPlatformModules = detectPlatformModules,
IncludeEntryPoints = includeEntryPoints,
GenerateModuleDefinitions = generateModuleDefinitions,
GenerateScaffoldingMetadata = generateScaffoldingMetadata,
IncludeLlmDescriptions = includeLlmDescriptions,
AnalyzeModuleTags = analyzeModuleTags,
IncludeModuleFlags = includeModuleFlags
});
// Generate modular structure if requested
var modularStructure = enableModuleGrouping ?
await GenerateModularStructureAsync(analysisResult, options: new ModularOptions
{
GroupingStrategy = moduleGroupingStrategy,
DetectPlatformModules = detectPlatformModules,
IncludeEntryPoints = includeEntryPoints,
GenerateScaffoldingMetadata = generateScaffoldingMetadata,
IncludeLlmDescriptions = includeLlmDescriptions,
AnalyzeModuleTags = analyzeModuleTags,
IncludeModuleFlags = includeModuleFlags
}) : null;
// Generate dependency map (enhanced with modular structure if available)
var dependencyMap = await GenerateDependencyMapAsync(analysisResult, outputFormat, modularStructure);
// Analyze coupling metrics if requested
var couplingMetrics = analyzeCoupling ? await AnalyzeCouplingMetricsAsync(analysisResult) : null;
// Detect architectural patterns if requested
var architecturalPatterns = detectPatterns ? await DetectArchitecturalPatternsAsync(analysisResult) : null;
// Generate insights and recommendations
var insights = generateInsights ? await GenerateArchitecturalInsightsAsync(analysisResult, couplingMetrics, architecturalPatterns) : null;
// Save output file if path provided
string? savedPath = null;
if (!string.IsNullOrEmpty(outputPath))
{
savedPath = await SaveMapToFileAsync(dependencyMap, outputPath, outputFormat);
}
var result = new
{
ProjectPath = projectPath,
OutputFormat = outputFormat,
DependencyMap = dependencyMap,
ModularStructure = modularStructure,
CouplingMetrics = couplingMetrics,
ArchitecturalPatterns = architecturalPatterns,
Insights = insights,
Statistics = new
{
TotalModules = modularStructure?.Modules?.Count ?? analysisResult.Modules.Count,
TotalLogicalModules = modularStructure?.Modules?.Count ?? 0,
TotalDependencies = analysisResult.Dependencies.Count,
MaxDepthReached = analysisResult.MaxDepthReached,
ExternalDependencies = analysisResult.ExternalDependencies.Count,
PlatformSpecificModules = modularStructure?.Modules?.Count(m => m.PlatformSpecific) ?? 0
},
OutputPath = savedPath
};
_logger?.LogInformation("Modular map analysis completed successfully");
return new AIPluginResult(result, "Modular map generation completed successfully");
}
catch (Exception ex)
{
_logger?.LogError(ex, "Failed to generate modular map");
return new AIPluginResult(ex, $"Failed to generate modular map: {ex.Message}");
}
}
private async Task<ProjectAnalysisResult> AnalyzeProjectStructureAsync(string projectPath, AnalysisOptions options)
{
var result = new ProjectAnalysisResult { ProjectPath = projectPath };
var processedProjects = new HashSet<string>();
var projectQueue = new Queue<(string path, int depth)>();
try
{
// Find initial projects to analyze
var initialProjects = await DiscoverProjectsAsync(projectPath);
foreach (var project in initialProjects)
{
projectQueue.Enqueue((project, 0));
}
// Process projects with depth limiting
while (projectQueue.Count > 0)
{
var (currentPath, depth) = projectQueue.Dequeue();
if (depth > options.MaxDepth || processedProjects.Contains(currentPath))
continue;
processedProjects.Add(currentPath);
result.MaxDepthReached = Math.Max(result.MaxDepthReached, depth);
var module = await AnalyzeProjectAsync(currentPath, options);
if (module != null && ShouldIncludeModule(module, options))
{
result.Modules.Add(module);
// Add project references to queue for deeper analysis
var projectRefs = await GetProjectReferencesAsync(currentPath);
foreach (var refPath in projectRefs)
{
if (!processedProjects.Contains(refPath))
{
projectQueue.Enqueue((refPath, depth + 1));
}
}
}
}
// Build dependency relationships
result.Dependencies = await BuildDependencyGraphAsync(result.Modules, options);
// Analyze external dependencies if requested
if (options.IncludeExternalDependencies)
{
result.ExternalDependencies = await AnalyzeExternalDependenciesAsync(result.Modules);
}
_logger?.LogInformation("Analyzed {ModuleCount} modules with {DependencyCount} dependencies",
result.Modules.Count, result.Dependencies.Count);
return result;
}
catch (Exception ex)
{
_logger?.LogError(ex, "Error analyzing project structure");
throw;
}
}
private async Task<List<string>> DiscoverProjectsAsync(string path)
{
var projects = new List<string>();
if (File.Exists(path))
{
var extension = Path.GetExtension(path).ToLower();
if (extension == ".sln")
{
// Parse solution file
projects.AddRange(await ParseSolutionFileAsync(path));
}
else if (extension == ".csproj" || extension == ".vbproj" || extension == ".fsproj")
{
projects.Add(path);
}
}
else if (Directory.Exists(path))
{
// Search for project files
var projectFiles = Directory.GetFiles(path, "*.csproj", SearchOption.AllDirectories)
.Concat(Directory.GetFiles(path, "*.vbproj", SearchOption.AllDirectories))
.Concat(Directory.GetFiles(path, "*.fsproj", SearchOption.AllDirectories));
projects.AddRange(projectFiles);
// Also check for solution files
var solutionFiles = Directory.GetFiles(path, "*.sln", SearchOption.TopDirectoryOnly);
foreach (var sln in solutionFiles)
{
projects.AddRange(await ParseSolutionFileAsync(sln));
}
}
return projects.Distinct().ToList();
}
private async Task<List<string>> ParseSolutionFileAsync(string solutionPath)
{
var projects = new List<string>();
var solutionDir = Path.GetDirectoryName(solutionPath);
var content = await File.ReadAllTextAsync(solutionPath);
var projectRegex = new Regex(@"Project\(""\{[^}]+\}""\)\s*=\s*""[^""]*"",\s*""([^""]+)"",\s*""\{[^}]+\}""");
foreach (Match match in projectRegex.Matches(content))
{
var relativePath = match.Groups[1].Value;
var absolutePath = Path.GetFullPath(Path.Combine(solutionDir ?? string.Empty, relativePath));
if (File.Exists(absolutePath) && IsProjectFile(absolutePath))
{
projects.Add(absolutePath);
}
}
return projects;
}
private bool IsProjectFile(string path)
{
var extension = Path.GetExtension(path).ToLower();
return extension == ".csproj" || extension == ".vbproj" || extension == ".fsproj";
}
private async Task<ModuleInfo?> AnalyzeProjectAsync(string projectPath, AnalysisOptions options)
{
try
{
var projectName = Path.GetFileNameWithoutExtension(projectPath);
var projectDir = Path.GetDirectoryName(projectPath) ?? string.Empty;
// Parse project file
var projectXml = await File.ReadAllTextAsync(projectPath);
var doc = XDocument.Parse(projectXml);
// Get project type and target framework
var projectType = DetermineProjectType(doc);
var targetFramework = doc.Descendants("TargetFramework").FirstOrDefault()?.Value ??
doc.Descendants("TargetFrameworks").FirstOrDefault()?.Value?.Split(';').FirstOrDefault() ??
"unknown";
// Analyze source code
var sourceFiles = Directory.GetFiles(projectDir, "*.cs", SearchOption.AllDirectories)
.Where(f => !f.Contains("bin") && !f.Contains("obj"))
.ToList();
var namespaces = new HashSet<string>();
var classes = new List<string>();
var totalLines = 0;
foreach (var file in sourceFiles)
{
try
{
var sourceCode = await File.ReadAllTextAsync(file);
totalLines += sourceCode.Split('\n').Length;
var tree = CSharpSyntaxTree.ParseText(sourceCode);
var root = await tree.GetRootAsync();
// Extract namespaces
var namespaceDeclarations = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>();
foreach (var ns in namespaceDeclarations)
{
if (ns.Name != null)
namespaces.Add(ns.Name.ToString());
}
// Extract classes
var classDeclarations = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var cls in classDeclarations)
{
classes.Add(cls.Identifier.ValueText);
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to parse file {File}: {Error}", file, ex.Message);
}
}
var primaryNamespace = namespaces.FirstOrDefault() ?? projectName;
return new ModuleInfo
{
Name = projectName,
Namespace = primaryNamespace,
Type = projectType,
LineCount = totalLines,
ClassCount = classes.Count,
TargetFramework = targetFramework,
ProjectPath = projectPath,
SourceFiles = sourceFiles,
Namespaces = namespaces.ToList(),
Classes = classes
};
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to analyze project {Project}: {Error}", projectPath, ex.Message);
return null;
}
}
private string DetermineProjectType(XDocument projectDoc)
{
var outputType = projectDoc.Descendants("OutputType").FirstOrDefault()?.Value?.ToLower();
var useWebSdk = projectDoc.Root?.Attribute("Sdk")?.Value?.Contains("Web") == true;
var hasPackageRefs = projectDoc.Descendants("PackageReference").Any();
return outputType switch
{
"exe" when useWebSdk => "WebAPI",
"exe" => "Console",
"library" when useWebSdk => "WebLibrary",
"library" => "Library",
"winexe" => "WinForms",
_ when useWebSdk => "Web",
_ => "Library"
};
}
private bool ShouldIncludeModule(ModuleInfo module, AnalysisOptions options)
{
if (string.IsNullOrEmpty(options.NamespaceFilter))
return true;
var pattern = options.NamespaceFilter.Replace("*", ".*");
var regex = new Regex(pattern, RegexOptions.IgnoreCase);
return module.Namespaces.Any(ns => regex.IsMatch(ns));
}
private async Task<List<string>> GetProjectReferencesAsync(string projectPath)
{
var references = new List<string>();
try
{
var projectXml = await File.ReadAllTextAsync(projectPath);
var doc = XDocument.Parse(projectXml);
var projectDir = Path.GetDirectoryName(projectPath) ?? string.Empty;
var projectRefs = doc.Descendants("ProjectReference");
foreach (var projRef in projectRefs)
{
var include = projRef.Attribute("Include")?.Value;
if (!string.IsNullOrEmpty(include))
{
var absolutePath = Path.GetFullPath(Path.Combine(projectDir, include));
if (File.Exists(absolutePath))
{
references.Add(absolutePath);
}
}
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to get project references for {Project}: {Error}", projectPath, ex.Message);
}
return references;
}
private async Task<List<DependencyInfo>> BuildDependencyGraphAsync(List<ModuleInfo> modules, AnalysisOptions options)
{
var dependencies = new List<DependencyInfo>();
var modulesByPath = modules.ToDictionary(m => m.ProjectPath, m => m);
foreach (var module in modules)
{
try
{
// Get project references
var projectRefs = await GetProjectReferencesAsync(module.ProjectPath);
foreach (var refPath in projectRefs)
{
if (modulesByPath.TryGetValue(refPath, out var targetModule))
{
var strength = await CalculateDependencyStrength(module, targetModule, options);
dependencies.Add(new DependencyInfo
{
From = module.Name,
To = targetModule.Name,
Type = "ProjectReference",
Strength = strength,
ReferenceCount = await CountReferences(module, targetModule)
});
}
}
// Analyze using statements for namespace dependencies if method-level analysis is enabled
if (options.IncludeMethodLevel)
{
var namespaceDependencies = await AnalyzeNamespaceDependencies(module, modules);
dependencies.AddRange(namespaceDependencies);
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to build dependencies for {Module}: {Error}", module.Name, ex.Message);
}
}
return dependencies;
}
private async Task<string> CalculateDependencyStrength(ModuleInfo from, ModuleInfo to, AnalysisOptions options)
{
try
{
var referenceCount = await CountReferences(from, to);
return referenceCount switch
{
> 50 => "Strong",
> 10 => "Medium",
_ => "Weak"
};
}
catch
{
return "Unknown";
}
}
private async Task<int> CountReferences(ModuleInfo from, ModuleInfo to)
{
int count = 0;
foreach (var sourceFile in from.SourceFiles)
{
try
{
var content = await File.ReadAllTextAsync(sourceFile);
var tree = CSharpSyntaxTree.ParseText(content);
var root = await tree.GetRootAsync();
// Count using directives
var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>();
foreach (var usingDirective in usingDirectives)
{
var namespaceName = usingDirective.Name?.ToString();
if (to.Namespaces.Any(ns => namespaceName?.StartsWith(ns) == true))
{
count++;
}
}
// Count qualified name references
var identifierNames = root.DescendantNodes().OfType<IdentifierNameSyntax>();
foreach (var identifier in identifierNames)
{
if (to.Classes.Contains(identifier.Identifier.ValueText))
{
count++;
}
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to count references in {File}: {Error}", sourceFile, ex.Message);
}
}
return count;
}
private async Task<List<DependencyInfo>> AnalyzeNamespaceDependencies(ModuleInfo module, List<ModuleInfo> allModules)
{
var dependencies = new List<DependencyInfo>();
var targetModulesByNamespace = allModules
.SelectMany(m => m.Namespaces.Select(ns => new { Namespace = ns, Module = m }))
.GroupBy(x => x.Namespace)
.ToDictionary(g => g.Key, g => g.Select(x => x.Module).ToList());
foreach (var sourceFile in module.SourceFiles)
{
try
{
var content = await File.ReadAllTextAsync(sourceFile);
var tree = CSharpSyntaxTree.ParseText(content);
var root = await tree.GetRootAsync();
var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>();
foreach (var usingDirective in usingDirectives)
{
var namespaceName = usingDirective.Name?.ToString();
if (!string.IsNullOrEmpty(namespaceName) && targetModulesByNamespace.ContainsKey(namespaceName))
{
foreach (var targetModule in targetModulesByNamespace[namespaceName])
{
if (targetModule.Name != module.Name)
{
var existing = dependencies.FirstOrDefault(d => d.From == module.Name && d.To == targetModule.Name && d.Type == "NamespaceReference");
if (existing == null)
{
dependencies.Add(new DependencyInfo
{
From = module.Name,
To = targetModule.Name,
Type = "NamespaceReference",
Strength = "Weak",
ReferenceCount = 1
});
}
else
{
existing.ReferenceCount++;
}
}
}
}
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to analyze namespace dependencies in {File}: {Error}", sourceFile, ex.Message);
}
}
return dependencies;
}
private async Task<List<ExternalDependencyInfo>> AnalyzeExternalDependenciesAsync(List<ModuleInfo> modules)
{
var externalDeps = new List<ExternalDependencyInfo>();
var depsByName = new Dictionary<string, ExternalDependencyInfo>();
foreach (var module in modules)
{
try
{
var projectXml = await File.ReadAllTextAsync(module.ProjectPath);
var doc = XDocument.Parse(projectXml);
var packageRefs = doc.Descendants("PackageReference");
foreach (var packageRef in packageRefs)
{
var name = packageRef.Attribute("Include")?.Value;
var version = packageRef.Attribute("Version")?.Value ??
packageRef.Element("Version")?.Value;
if (!string.IsNullOrEmpty(name))
{
if (!depsByName.TryGetValue(name, out var existingDep))
{
existingDep = new ExternalDependencyInfo
{
Name = name,
Version = version ?? "unknown",
Type = "NuGet"
};
depsByName[name] = existingDep;
externalDeps.Add(existingDep);
}
existingDep.UsedBy.Add(module.Name);
}
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to analyze external dependencies for {Module}: {Error}", module.Name, ex.Message);
}
}
return externalDeps;
}
private async Task<CouplingMetrics> AnalyzeCouplingMetricsAsync(ProjectAnalysisResult analysis)
{
var metrics = new CouplingMetrics();
var moduleMetrics = new Dictionary<string, ModuleCouplingMetrics>();
// Calculate coupling metrics for each module
foreach (var module in analysis.Modules)
{
var afferentCoupling = analysis.Dependencies.Count(d => d.To == module.Name);
var efferentCoupling = analysis.Dependencies.Count(d => d.From == module.Name);
var totalCoupling = afferentCoupling + efferentCoupling;
var instability = totalCoupling == 0 ? 0 : (double)efferentCoupling / totalCoupling;
// Calculate abstractness (simplified - ratio of interfaces/abstract classes)
var abstractness = await CalculateAbstractnessAsync(module);
// Distance from main sequence: D = |A + I - 1|
var distance = Math.Abs(abstractness + instability - 1);
var moduleCoupling = new ModuleCouplingMetrics
{
AfferentCoupling = afferentCoupling,
EfferentCoupling = efferentCoupling,
Instability = instability,
Abstractness = abstractness,
Distance = distance
};
moduleMetrics[module.Name] = moduleCoupling;
metrics.InstabilityScores[module.Name] = instability;
}
// Calculate overall metrics
metrics.OverallCouplingScore = moduleMetrics.Values.Average(m => m.Distance);
// Identify highly coupled modules (instability > 0.7)
metrics.HighlyCoupledModules = moduleMetrics
.Where(kvp => kvp.Value.Instability > 0.7)
.Select(kvp => kvp.Key)
.ToList();
// Identify loosely coupled modules (instability < 0.3)
metrics.LooselyCoupledModules = moduleMetrics
.Where(kvp => kvp.Value.Instability < 0.3)
.Select(kvp => kvp.Key)
.ToList();
// Detect circular dependencies
metrics.CircularDependencies = await DetectCircularDependenciesAsync(analysis);
// Generate recommendations
metrics.Recommendations = GenerateCouplingRecommendations(moduleMetrics, metrics);
await Task.CompletedTask;
return metrics;
}
private async Task<double> CalculateAbstractnessAsync(ModuleInfo module)
{
int totalTypes = 0;
int abstractTypes = 0;
foreach (var sourceFile in module.SourceFiles)
{
try
{
var content = await File.ReadAllTextAsync(sourceFile);
var tree = CSharpSyntaxTree.ParseText(content);
var root = await tree.GetRootAsync();
// Count classes and interfaces
var classDeclarations = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
var interfaceDeclarations = root.DescendantNodes().OfType<InterfaceDeclarationSyntax>();
foreach (var classDecl in classDeclarations)
{
totalTypes++;
if (classDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword)))
{
abstractTypes++;
}
}
foreach (var interfaceDecl in interfaceDeclarations)
{
totalTypes++;
abstractTypes++; // Interfaces are considered abstract
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to calculate abstractness for {File}: {Error}", sourceFile, ex.Message);
}
}
return totalTypes == 0 ? 0 : (double)abstractTypes / totalTypes;
}
private async Task<List<string>> DetectCircularDependenciesAsync(ProjectAnalysisResult analysis)
{
var circularDeps = new List<string>();
var graph = new Dictionary<string, List<string>>();
// Build adjacency list
foreach (var module in analysis.Modules)
{
graph[module.Name] = new List<string>();
}
foreach (var dependency in analysis.Dependencies)
{
if (graph.ContainsKey(dependency.From))
{
graph[dependency.From].Add(dependency.To);
}
}
// Use DFS to detect cycles
var visited = new HashSet<string>();
var recursionStack = new HashSet<string>();
foreach (var module in analysis.Modules)
{
if (!visited.Contains(module.Name))
{
var cycles = await DetectCyclesAsync(module.Name, graph, visited, recursionStack, new List<string>());
circularDeps.AddRange(cycles);
}
}
return circularDeps.Distinct().ToList();
}
private async Task<List<string>> DetectCyclesAsync(string node, Dictionary<string, List<string>> graph,
HashSet<string> visited, HashSet<string> recursionStack, List<string> currentPath)
{
var cycles = new List<string>();
visited.Add(node);
recursionStack.Add(node);
currentPath.Add(node);
if (graph.ContainsKey(node))
{
foreach (var neighbor in graph[node])
{
if (!visited.Contains(neighbor))
{
var subCycles = await DetectCyclesAsync(neighbor, graph, visited, recursionStack, new List<string>(currentPath));
cycles.AddRange(subCycles);
}
else if (recursionStack.Contains(neighbor))
{
// Found a cycle
var cycleStart = currentPath.IndexOf(neighbor);
var cycle = currentPath.Skip(cycleStart).Append(neighbor);
cycles.Add(string.Join(" -> ", cycle));
}
}
}
recursionStack.Remove(node);
return cycles;
}
private List<string> GenerateCouplingRecommendations(Dictionary<string, ModuleCouplingMetrics> moduleMetrics, CouplingMetrics metrics)
{
var recommendations = new List<string>();
// High coupling recommendations
foreach (var highlyCouple in metrics.HighlyCoupledModules)
{
if (moduleMetrics.TryGetValue(highlyCouple, out var coupling))
{
if (coupling.EfferentCoupling > coupling.AfferentCoupling)
{
recommendations.Add($"Module '{highlyCouple}' has high efferent coupling ({coupling.EfferentCoupling}). Consider extracting interfaces or breaking into smaller modules.");
}
else
{
recommendations.Add($"Module '{highlyCouple}' has high afferent coupling ({coupling.AfferentCoupling}). This module is heavily depended upon - ensure it has a stable interface.");
}
}
}
// Distance from main sequence recommendations
foreach (var moduleMetric in moduleMetrics)
{
if (moduleMetric.Value.Distance > 0.7)
{
if (moduleMetric.Value.Abstractness < 0.3 && moduleMetric.Value.Instability > 0.7)
{
recommendations.Add($"Module '{moduleMetric.Key}' is in the 'Zone of Pain' (concrete and unstable). Consider increasing abstraction through interfaces.");
}
else if (moduleMetric.Value.Abstractness > 0.7 && moduleMetric.Value.Instability < 0.3)
{
recommendations.Add($"Module '{moduleMetric.Key}' is in the 'Zone of Uselessness' (abstract but not used). Consider removing unused abstractions.");
}
}
}
// Circular dependency recommendations
if (metrics.CircularDependencies.Any())
{
recommendations.Add($"Found {metrics.CircularDependencies.Count} circular dependencies. Consider dependency inversion or extracting common interfaces.");
}
// General recommendations
if (metrics.OverallCouplingScore > 0.6)
{
recommendations.Add("Overall coupling is high. Consider applying SOLID principles and dependency injection patterns.");
}
return recommendations;
}
private async Task<ArchitecturalPatterns> DetectArchitecturalPatternsAsync(ProjectAnalysisResult analysis)
{
var patterns = new ArchitecturalPatterns();
var layerScores = new Dictionary<string, double>();
// Detect layered architecture
var layeredScore = await DetectLayeredArchitectureAsync(analysis);
layerScores["Layered"] = layeredScore;
// Detect clean architecture
var cleanScore = await DetectCleanArchitectureAsync(analysis);
layerScores["Clean"] = cleanScore;
// Detect hexagonal architecture
var hexagonalScore = await DetectHexagonalArchitectureAsync(analysis);
layerScores["Hexagonal"] = hexagonalScore;
// Detect microservices patterns
var microservicesScore = await DetectMicroservicesPatternAsync(analysis);
layerScores["Microservices"] = microservicesScore;
// Find the pattern with highest confidence
var bestPattern = layerScores.OrderByDescending(kvp => kvp.Value).First();
patterns.DetectedPattern = bestPattern.Key;
patterns.Confidence = bestPattern.Value;
// Generate layer definitions based on detected pattern
patterns.LayerDefinitions = await GenerateLayerDefinitionsAsync(analysis, bestPattern.Key);
// Detect pattern violations
patterns.PatternViolations = await DetectPatternViolationsAsync(analysis, bestPattern.Key, patterns.LayerDefinitions);
// Generate suggestions
patterns.Suggestions = GeneratePatternSuggestions(analysis, bestPattern.Key, patterns.PatternViolations);
return patterns;
}
private async Task<double> DetectLayeredArchitectureAsync(ProjectAnalysisResult analysis)
{
double score = 0;
var moduleNames = analysis.Modules.Select(m => m.Name.ToLower()).ToList();
// Check for common layer naming patterns
var layerPatterns = new Dictionary<string, string[]>
{
["presentation"] = new[] { "web", "api", "mvc", "ui", "frontend", "presentation" },
["business"] = new[] { "business", "service", "logic", "application", "app" },
["data"] = new[] { "data", "dal", "repository", "persistence", "infrastructure" }
};
var foundLayers = new HashSet<string>();
foreach (var module in analysis.Modules)
{
var moduleName = module.Name.ToLower();
var namespaces = module.Namespaces.Select(ns => ns.ToLower());
foreach (var layer in layerPatterns)
{
if (layer.Value.Any(pattern => moduleName.Contains(pattern) ||
namespaces.Any(ns => ns.Contains(pattern))))
{
foundLayers.Add(layer.Key);
score += 0.3;
}
}
}
// Check dependency flow (should be top-down in layered architecture)
var dependencyViolations = 0;
var layerOrder = new[] { "presentation", "business", "data" };
foreach (var dependency in analysis.Dependencies)
{
var fromLayer = DetermineLayer(dependency.From, analysis.Modules);
var toLayer = DetermineLayer(dependency.To, analysis.Modules);
var fromIndex = Array.IndexOf(layerOrder, fromLayer);
var toIndex = Array.IndexOf(layerOrder, toLayer);
if (fromIndex >= 0 && toIndex >= 0 && fromIndex < toIndex)
{
dependencyViolations++;
}
}
// Penalize for dependency violations
if (analysis.Dependencies.Count > 0)
{
var violationRatio = (double)dependencyViolations / analysis.Dependencies.Count;
score -= violationRatio * 0.4;
}
// Bonus for having all three layers
if (foundLayers.Count >= 3)
{
score += 0.3;
}
await Task.CompletedTask;
return Math.Max(0, Math.Min(1, score));
}
private async Task<double> DetectCleanArchitectureAsync(ProjectAnalysisResult analysis)
{
double score = 0;
var moduleNames = analysis.Modules.Select(m => m.Name.ToLower()).ToList();
// Check for clean architecture naming patterns
var cleanPatterns = new Dictionary<string, string[]>
{
["domain"] = new[] { "domain", "entities", "core" },
["application"] = new[] { "application", "usecases", "services" },
["infrastructure"] = new[] { "infrastructure", "persistence", "external" },
["presentation"] = new[] { "web", "api", "ui", "controllers" }
};
var foundLayers = new HashSet<string>();
foreach (var module in analysis.Modules)
{
var moduleName = module.Name.ToLower();
var namespaces = module.Namespaces.Select(ns => ns.ToLower());
foreach (var layer in cleanPatterns)
{
if (layer.Value.Any(pattern => moduleName.Contains(pattern) ||
namespaces.Any(ns => ns.Contains(pattern))))
{
foundLayers.Add(layer.Key);
score += 0.25;
}
}
}
// Check for dependency rule compliance (dependencies point inward)
var violationCount = 0;
var layerDependencyRules = new Dictionary<string, string[]>
{
["domain"] = new string[0], // Domain should have no dependencies
["application"] = new[] { "domain" },
["infrastructure"] = new[] { "domain", "application" },
["presentation"] = new[] { "application", "domain" }
};
foreach (var dependency in analysis.Dependencies)
{
var fromLayer = DetermineCleanLayer(dependency.From, analysis.Modules);
var toLayer = DetermineCleanLayer(dependency.To, analysis.Modules);
if (!string.IsNullOrEmpty(fromLayer) && !string.IsNullOrEmpty(toLayer))
{
var allowedDependencies = layerDependencyRules.GetValueOrDefault(fromLayer, new string[0]);
if (!allowedDependencies.Contains(toLayer))
{
violationCount++;
}
}
}
// Penalize violations
if (analysis.Dependencies.Count > 0)
{
var violationRatio = (double)violationCount / analysis.Dependencies.Count;
score -= violationRatio * 0.5;
}
await Task.CompletedTask;
return Math.Max(0, Math.Min(1, score));
}
private async Task<double> DetectHexagonalArchitectureAsync(ProjectAnalysisResult analysis)
{
double score = 0;
// Check for hexagonal architecture patterns
var hexPatterns = new Dictionary<string, string[]>
{
["core"] = new[] { "core", "domain", "business" },
["ports"] = new[] { "ports", "interfaces", "contracts" },
["adapters"] = new[] { "adapters", "infrastructure", "external" }
};
var foundComponents = new HashSet<string>();
foreach (var module in analysis.Modules)
{
var moduleName = module.Name.ToLower();
var namespaces = module.Namespaces.Select(ns => ns.ToLower());
foreach (var component in hexPatterns)
{
if (component.Value.Any(pattern => moduleName.Contains(pattern) ||
namespaces.Any(ns => ns.Contains(pattern))))
{
foundComponents.Add(component.Key);
score += 0.33;
}
}
}
// Look for interface segregation (ports pattern)
var interfaceCount = 0;
var totalClasses = 0;
foreach (var module in analysis.Modules)
{
foreach (var sourceFile in module.SourceFiles)
{
try
{
var content = await File.ReadAllTextAsync(sourceFile);
var tree = CSharpSyntaxTree.ParseText(content);
var root = await tree.GetRootAsync();
interfaceCount += root.DescendantNodes().OfType<InterfaceDeclarationSyntax>().Count();
totalClasses += root.DescendantNodes().OfType<ClassDeclarationSyntax>().Count();
}
catch
{
// Ignore parsing errors
}
}
}
// High interface-to-class ratio suggests ports and adapters
if (totalClasses > 0)
{
var interfaceRatio = (double)interfaceCount / totalClasses;
if (interfaceRatio > 0.3)
{
score += 0.2;
}
}
return Math.Max(0, Math.Min(1, score));
}
private async Task<double> DetectMicroservicesPatternAsync(ProjectAnalysisResult analysis)
{
double score = 0;
// Check for microservices indicators
var servicePatterns = new[] { "service", "api", "microservice", "ms" };
var serviceCount = 0;
foreach (var module in analysis.Modules)
{
var moduleName = module.Name.ToLower();
if (servicePatterns.Any(pattern => moduleName.Contains(pattern)) &&
(module.Type == "WebAPI" || module.Type == "Console"))
{
serviceCount++;
score += 0.2;
}
}
// Multiple independent services suggest microservices
if (serviceCount >= 3)
{
score += 0.3;
}
// Low coupling between services is good for microservices
var serviceDependencies = analysis.Dependencies.Where(d =>
IsService(d.From, analysis.Modules) && IsService(d.To, analysis.Modules)).Count();
if (serviceCount > 0)
{
var serviceCouplingRatio = (double)serviceDependencies / (serviceCount * serviceCount);
if (serviceCouplingRatio < 0.2)
{
score += 0.3;
}
}
await Task.CompletedTask;
return Math.Max(0, Math.Min(1, score));
}
private bool IsService(string moduleName, List<ModuleInfo> modules)
{
var module = modules.FirstOrDefault(m => m.Name == moduleName);
return module != null && (module.Type == "WebAPI" || module.Type == "Console") &&
new[] { "service", "api", "microservice", "ms" }.Any(pattern =>
module.Name.ToLower().Contains(pattern));
}
private string DetermineLayer(string moduleName, List<ModuleInfo> modules)
{
var module = modules.FirstOrDefault(m => m.Name == moduleName);
if (module == null) return "unknown";
var name = module.Name.ToLower();
var namespaces = module.Namespaces.Select(ns => ns.ToLower());
if (new[] { "web", "api", "mvc", "ui", "presentation" }.Any(p => name.Contains(p) || namespaces.Any(ns => ns.Contains(p))))
return "presentation";
if (new[] { "business", "service", "logic", "application" }.Any(p => name.Contains(p) || namespaces.Any(ns => ns.Contains(p))))
return "business";
if (new[] { "data", "dal", "repository", "persistence" }.Any(p => name.Contains(p) || namespaces.Any(ns => ns.Contains(p))))
return "data";
return "unknown";
}
private string? DetermineCleanLayer(string moduleName, List<ModuleInfo> modules)
{
var module = modules.FirstOrDefault(m => m.Name == moduleName);
if (module == null) return null;
var name = module.Name.ToLower();
var namespaces = module.Namespaces.Select(ns => ns.ToLower());
if (new[] { "domain", "entities", "core" }.Any(p => name.Contains(p) || namespaces.Any(ns => ns.Contains(p))))
return "domain";
if (new[] { "application", "usecases", "services" }.Any(p => name.Contains(p) || namespaces.Any(ns => ns.Contains(p))))
return "application";
if (new[] { "infrastructure", "persistence", "external" }.Any(p => name.Contains(p) || namespaces.Any(ns => ns.Contains(p))))
return "infrastructure";
if (new[] { "web", "api", "ui", "controllers" }.Any(p => name.Contains(p) || namespaces.Any(ns => ns.Contains(p))))
return "presentation";
return null;
}
private async Task<Dictionary<string, List<string>>> GenerateLayerDefinitionsAsync(ProjectAnalysisResult analysis, string patternType)
{
var layers = new Dictionary<string, List<string>>();
switch (patternType.ToLower())
{
case "layered":
layers["Presentation"] = analysis.Modules.Where(m => DetermineLayer(m.Name, analysis.Modules) == "presentation").Select(m => m.Name).ToList();
layers["Business"] = analysis.Modules.Where(m => DetermineLayer(m.Name, analysis.Modules) == "business").Select(m => m.Name).ToList();
layers["Data"] = analysis.Modules.Where(m => DetermineLayer(m.Name, analysis.Modules) == "data").Select(m => m.Name).ToList();
break;
case "clean":
layers["Domain"] = analysis.Modules.Where(m => DetermineCleanLayer(m.Name, analysis.Modules) == "domain").Select(m => m.Name).ToList();
layers["Application"] = analysis.Modules.Where(m => DetermineCleanLayer(m.Name, analysis.Modules) == "application").Select(m => m.Name).ToList();
layers["Infrastructure"] = analysis.Modules.Where(m => DetermineCleanLayer(m.Name, analysis.Modules) == "infrastructure").Select(m => m.Name).ToList();
layers["Presentation"] = analysis.Modules.Where(m => DetermineCleanLayer(m.Name, analysis.Modules) == "presentation").Select(m => m.Name).ToList();
break;
case "hexagonal":
layers["Core"] = analysis.Modules.Where(m => m.Name.ToLower().Contains("core") || m.Name.ToLower().Contains("domain")).Select(m => m.Name).ToList();
layers["Ports"] = analysis.Modules.Where(m => m.Name.ToLower().Contains("interface") || m.Name.ToLower().Contains("contract")).Select(m => m.Name).ToList();
layers["Adapters"] = analysis.Modules.Where(m => m.Name.ToLower().Contains("adapter") || m.Name.ToLower().Contains("infrastructure")).Select(m => m.Name).ToList();
break;
case "microservices":
layers["Services"] = analysis.Modules.Where(m => IsService(m.Name, analysis.Modules)).Select(m => m.Name).ToList();
layers["Shared"] = analysis.Modules.Where(m => !IsService(m.Name, analysis.Modules)).Select(m => m.Name).ToList();
break;
}
await Task.CompletedTask;
return layers;
}
private async Task<List<string>> DetectPatternViolationsAsync(ProjectAnalysisResult analysis, string patternType, Dictionary<string, List<string>> layers)
{
var violations = new List<string>();
switch (patternType.ToLower())
{
case "layered":
violations.AddRange(await DetectLayeredViolationsAsync(analysis, layers));
break;
case "clean":
violations.AddRange(await DetectCleanViolationsAsync(analysis, layers));
break;
case "hexagonal":
violations.AddRange(await DetectHexagonalViolationsAsync(analysis, layers));
break;
case "microservices":
violations.AddRange(await DetectMicroservicesViolationsAsync(analysis, layers));
break;
}
return violations;
}
private async Task<List<string>> DetectLayeredViolationsAsync(ProjectAnalysisResult analysis, Dictionary<string, List<string>> layers)
{
var violations = new List<string>();
var layerOrder = new[] { "Presentation", "Business", "Data" };
foreach (var dependency in analysis.Dependencies)
{
var fromLayer = GetModuleLayer(dependency.From, layers);
var toLayer = GetModuleLayer(dependency.To, layers);
if (!string.IsNullOrEmpty(fromLayer) && !string.IsNullOrEmpty(toLayer))
{
var fromIndex = Array.IndexOf(layerOrder, fromLayer);
var toIndex = Array.IndexOf(layerOrder, toLayer);
if (fromIndex >= 0 && toIndex >= 0 && fromIndex < toIndex)
{
violations.Add($"{fromLayer} layer module '{dependency.From}' should not depend on {toLayer} layer module '{dependency.To}'");
}
}
}
await Task.CompletedTask;
return violations;
}
private async Task<List<string>> DetectCleanViolationsAsync(ProjectAnalysisResult analysis, Dictionary<string, List<string>> layers)
{
var violations = new List<string>();
var dependencyRules = new Dictionary<string, string[]>
{
["Domain"] = new string[0],
["Application"] = new[] { "Domain" },
["Infrastructure"] = new[] { "Domain", "Application" },
["Presentation"] = new[] { "Application", "Domain" }
};
foreach (var dependency in analysis.Dependencies)
{
var fromLayer = GetModuleLayer(dependency.From, layers);
var toLayer = GetModuleLayer(dependency.To, layers);
if (!string.IsNullOrEmpty(fromLayer) && !string.IsNullOrEmpty(toLayer))
{
var allowedDependencies = dependencyRules.GetValueOrDefault(fromLayer, new string[0]);
if (!allowedDependencies.Contains(toLayer))
{
violations.Add($"Clean Architecture violation: {fromLayer} module '{dependency.From}' should not depend on {toLayer} module '{dependency.To}'");
}
}
}
await Task.CompletedTask;
return violations;
}
private async Task<List<string>> DetectHexagonalViolationsAsync(ProjectAnalysisResult analysis, Dictionary<string, List<string>> layers)
{
var violations = new List<string>();
// Core should not depend on Adapters
foreach (var dependency in analysis.Dependencies)
{
var fromLayer = GetModuleLayer(dependency.From, layers);
var toLayer = GetModuleLayer(dependency.To, layers);
if (fromLayer == "Core" && toLayer == "Adapters")
{
violations.Add($"Hexagonal Architecture violation: Core module '{dependency.From}' should not depend on Adapter module '{dependency.To}'");
}
}
await Task.CompletedTask;
return violations;
}
private async Task<List<string>> DetectMicroservicesViolationsAsync(ProjectAnalysisResult analysis, Dictionary<string, List<string>> layers)
{
var violations = new List<string>();
// Services should have minimal dependencies on other services
var serviceToServiceDeps = analysis.Dependencies.Where(d =>
layers["Services"].Contains(d.From) && layers["Services"].Contains(d.To)).ToList();
if (serviceToServiceDeps.Count > layers["Services"].Count * 0.3)
{
violations.Add($"High coupling between microservices detected: {serviceToServiceDeps.Count} inter-service dependencies");
}
foreach (var serviceDep in serviceToServiceDeps)
{
violations.Add($"Service coupling: '{serviceDep.From}' depends on '{serviceDep.To}' - consider async messaging or shared data contracts");
}
await Task.CompletedTask;
return violations;
}
private string GetModuleLayer(string moduleName, Dictionary<string, List<string>> layers)
{
return layers.FirstOrDefault(kvp => kvp.Value.Contains(moduleName)).Key;
}
private List<string> GeneratePatternSuggestions(ProjectAnalysisResult analysis, string patternType, List<string> violations)
{
var suggestions = new List<string>();
switch (patternType.ToLower())
{
case "layered":
suggestions.Add("Ensure dependencies flow downward: Presentation -> Business -> Data");
suggestions.Add("Consider using dependency injection to invert control between layers");
if (violations.Any())
suggestions.Add("Extract interfaces to break inappropriate layer dependencies");
break;
case "clean":
suggestions.Add("Keep the Domain layer free of external dependencies");
suggestions.Add("Use dependency inversion principle for Infrastructure dependencies");
suggestions.Add("Consider using MediatR pattern for Application layer orchestration");
break;
case "hexagonal":
suggestions.Add("Define clear port interfaces for external integrations");
suggestions.Add("Keep business logic in the core, isolated from infrastructure concerns");
suggestions.Add("Use adapter pattern for external service integrations");
break;
case "microservices":
suggestions.Add("Minimize direct dependencies between services");
suggestions.Add("Consider using message queues or event-driven communication");
suggestions.Add("Implement proper service boundaries based on business domains");
break;
}
return suggestions;
}
private async Task<ArchitecturalInsights> GenerateArchitecturalInsightsAsync(
ProjectAnalysisResult analysis,
CouplingMetrics? coupling,
ArchitecturalPatterns? patterns)
{
var insights = new ArchitecturalInsights();
// Calculate overall architecture score
var patternScore = patterns?.Confidence ?? 0;
var couplingScore = coupling != null ? (1 - coupling.OverallCouplingScore) : 0;
var complexityScore = CalculateComplexityScore(analysis);
insights.OverallArchitectureScore = (patternScore + couplingScore + complexityScore) / 3 * 10;
// Identify strength areas
insights.StrengthAreas = new List<string>();
if (patternScore > 0.7)
insights.StrengthAreas.Add($"Good adherence to {patterns?.DetectedPattern ?? "unknown"} architectural pattern");
if (coupling?.CircularDependencies?.Count == 0)
insights.StrengthAreas.Add("No circular dependencies detected");
if (coupling?.LooselyCoupledModules?.Count > coupling?.HighlyCoupledModules?.Count)
insights.StrengthAreas.Add("Good overall module decoupling");
if (analysis.Modules.Any(m => m.Type == "Library"))
insights.StrengthAreas.Add("Good separation of concerns with dedicated library modules");
// Identify improvement areas
insights.ImprovementAreas = new List<string>();
if (coupling?.HighlyCoupledModules?.Count > 0)
insights.ImprovementAreas.Add($"High coupling in modules: {string.Join(", ", coupling.HighlyCoupledModules!)}");
if (patterns?.PatternViolations?.Count > 0)
insights.ImprovementAreas.Add($"Architectural pattern violations detected ({patterns.PatternViolations!.Count} issues)");
if (coupling?.CircularDependencies?.Count > 0)
insights.ImprovementAreas.Add($"Circular dependencies need resolution ({coupling.CircularDependencies!.Count} cycles)");
// Generate refactoring opportunities
insights.RefactoringOpportunities = await GenerateRefactoringOpportunitiesAsync(analysis, coupling, patterns);
// Generate design pattern suggestions
insights.DesignPatternSuggestions = GenerateDesignPatternSuggestions(analysis, coupling, patterns);
return insights;
}
private double CalculateComplexityScore(ProjectAnalysisResult analysis)
{
if (analysis.Modules.Count == 0) return 0;
var avgLinesPerModule = analysis.Modules.Average(m => m.LineCount);
var avgClassesPerModule = analysis.Modules.Average(m => m.ClassCount);
var dependencyRatio = analysis.Dependencies.Count / (double)analysis.Modules.Count;
// Normalize scores (lower is better for complexity)
var linesScore = Math.Max(0, 1 - (avgLinesPerModule / 10000)); // Penalize modules > 10k lines
var classesScore = Math.Max(0, 1 - (avgClassesPerModule / 50)); // Penalize modules > 50 classes
var dependencyScore = Math.Max(0, 1 - (dependencyRatio / 5)); // Penalize > 5 deps per module
return (linesScore + classesScore + dependencyScore) / 3;
}
private async Task<List<RefactoringOpportunity>> GenerateRefactoringOpportunitiesAsync(
ProjectAnalysisResult analysis,
CouplingMetrics? coupling,
ArchitecturalPatterns? patterns)
{
var opportunities = new List<RefactoringOpportunity>();
// Large module refactoring
var largeModules = analysis.Modules.Where(m => m.LineCount > 5000 || m.ClassCount > 30).ToList();
foreach (var module in largeModules)
{
opportunities.Add(new RefactoringOpportunity
{
Type = "Split Large Module",
Target = module.Name,
Benefit = "Improved maintainability and reduced complexity",
Effort = module.LineCount > 10000 ? "High" : "Medium",
Priority = "Medium"
});
}
// High coupling refactoring
if (coupling?.HighlyCoupledModules != null)
{
foreach (var coupledModule in coupling.HighlyCoupledModules)
{
opportunities.Add(new RefactoringOpportunity
{
Type = "Reduce Coupling",
Target = coupledModule,
Benefit = "Improved testability and flexibility",
Effort = "Medium",
Priority = "High"
});
}
}
// Circular dependency refactoring
if (coupling?.CircularDependencies?.Count > 0)
{
opportunities.Add(new RefactoringOpportunity
{
Type = "Break Circular Dependencies",
Target = "Multiple modules",
Benefit = "Simplified dependency graph and better modularity",
Effort = "High",
Priority = "High"
});
}
// Pattern-specific refactoring
if (patterns?.PatternViolations?.Count > 0)
{
opportunities.Add(new RefactoringOpportunity
{
Type = $"Fix {patterns?.DetectedPattern ?? "architectural"} Pattern Violations",
Target = "Architecture",
Benefit = "Better architectural consistency and maintainability",
Effort = "Medium",
Priority = "Medium"
});
}
await Task.CompletedTask;
return opportunities;
}
private List<string> GenerateDesignPatternSuggestions(
ProjectAnalysisResult analysis,
CouplingMetrics? coupling,
ArchitecturalPatterns? patterns)
{
var suggestions = new List<string>();
// Repository pattern for data access
var dataModules = analysis.Modules.Where(m =>
m.Name.ToLower().Contains("data") ||
m.Name.ToLower().Contains("repository") ||
m.Name.ToLower().Contains("dal")).ToList();
if (dataModules.Any())
{
suggestions.Add("Consider implementing Repository pattern for data access abstraction");
}
// Factory pattern for high coupling
if (coupling?.HighlyCoupledModules?.Count > 0)
{
suggestions.Add("Consider Factory or Abstract Factory patterns to reduce coupling in object creation");
}
// Observer pattern for event handling
var eventModules = analysis.Modules.Where(m =>
m.Name.ToLower().Contains("event") ||
m.Name.ToLower().Contains("notification") ||
m.Classes.Any(c => c.ToLower().Contains("event"))).ToList();
if (eventModules.Any())
{
suggestions.Add("Consider Observer or Mediator patterns for event-driven communication");
}
// Strategy pattern for business logic
var businessModules = analysis.Modules.Where(m =>
m.Name.ToLower().Contains("business") ||
m.Name.ToLower().Contains("service") ||
m.Name.ToLower().Contains("logic")).ToList();
if (businessModules.Any())
{
suggestions.Add("Consider Strategy pattern for varying business logic implementations");
}
// Dependency Injection for tight coupling
if (coupling?.OverallCouplingScore > 0.6)
{
suggestions.Add("Implement Dependency Injection container to improve testability and flexibility");
}
// Command pattern for API modules
var apiModules = analysis.Modules.Where(m =>
m.Type == "WebAPI" ||
m.Name.ToLower().Contains("api") ||
m.Name.ToLower().Contains("controller")).ToList();
if (apiModules.Any())
{
suggestions.Add("Consider Command/Query pattern (CQRS) for API request handling");
}
// Facade pattern for complex subsystems
var complexModules = analysis.Modules.Where(m => m.ClassCount > 20).ToList();
if (complexModules.Any())
{
suggestions.Add("Consider Facade pattern to simplify interfaces to complex subsystems");
}
return suggestions;
}
private async Task<string> SaveMapToFileAsync(object map, string outputPath, string format)
{
var directory = Path.GetDirectoryName(outputPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
string content = format.ToLower() switch
{
"json" => JsonSerializer.Serialize(map, new JsonSerializerOptions { WriteIndented = true }),
"mermaid" => map.ToString() ?? string.Empty,
"cytoscape" => JsonSerializer.Serialize(map, new JsonSerializerOptions { WriteIndented = true }),
"graphviz" => map.ToString() ?? string.Empty,
_ => JsonSerializer.Serialize(map, new JsonSerializerOptions { WriteIndented = true })
};
await File.WriteAllTextAsync(outputPath, content);
return outputPath;
}
private string GenerateMermaidDiagram(ProjectAnalysisResult analysis)
{
var mermaid = new StringBuilder();
mermaid.AppendLine("graph TD");
// Add nodes with styling
foreach (var module in analysis.Modules)
{
var nodeStyle = module.Type switch
{
"WebAPI" => ":::webapi",
"Library" => ":::library",
"Console" => ":::console",
_ => ""
};
mermaid.AppendLine($" {SanitizeNodeName(module.Name)}[\"{module.Name}<br/>({module.Type})\"] {nodeStyle}");
}
// Add dependencies
foreach (var dependency in analysis.Dependencies)
{
var arrow = dependency.Strength switch
{
"Strong" => "-->",
"Medium" => "-.->",
_ => "-->"
};
var label = dependency.ReferenceCount > 1 ? $"|{dependency.ReferenceCount}|" : "";
mermaid.AppendLine($" {SanitizeNodeName(dependency.From)} {arrow} {SanitizeNodeName(dependency.To)} {label}");
}
// Add styling
mermaid.AppendLine();
mermaid.AppendLine(" classDef webapi fill:#e1f5fe,stroke:#0277bd,stroke-width:2px");
mermaid.AppendLine(" classDef library fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px");
mermaid.AppendLine(" classDef console fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px");
return mermaid.ToString();
}
private string SanitizeNodeName(string name)
{
return Regex.Replace(name ?? string.Empty, @"[^a-zA-Z0-9_]", "_");
}
private object GenerateCytoscapeJson(ProjectAnalysisResult analysis)
{
return new
{
elements = new
{
nodes = analysis.Modules.Select(m => new
{
data = new
{
id = m.Name,
label = m.Name,
type = m.Type,
lineCount = m.LineCount,
classCount = m.ClassCount,
@namespace = m.Namespace
}
}),
edges = analysis.Dependencies.Select(d => new
{
data = new
{
source = d.From,
target = d.To,
type = d.Type,
strength = d.Strength,
weight = d.ReferenceCount
}
})
},
style = new object[]
{
new
{
selector = "node",
style = new
{
content = "data(label)",
width = "mapData(lineCount, 0, 10000, 20, 80)",
height = "mapData(classCount, 0, 50, 20, 60)",
backgroundColor = "#0074D9"
}
},
new
{
selector = "edge",
style = new
{
width = "mapData(weight, 1, 10, 1, 5)",
lineColor = "#333",
targetArrowColor = "#333",
targetArrowShape = "triangle"
}
}
},
layout = new
{
name = "dagre",
directed = true,
rankDir = "TB"
}
};
}
private string GenerateGraphvizDot(ProjectAnalysisResult analysis)
{
var dot = new StringBuilder();
dot.AppendLine("digraph ModularMap {");
dot.AppendLine(" rankdir=TB;");
dot.AppendLine(" node [shape=box, style=filled];");
dot.AppendLine(" edge [fontsize=10];");
// Add nodes with colors based on type
foreach (var module in analysis.Modules)
{
var color = module.Type switch
{
"WebAPI" => "lightblue",
"Library" => "lightgreen",
"Console" => "lightyellow",
_ => "lightgray"
};
var label = $"{module.Name}\\n({module.Type})\\n{module.LineCount} lines";
dot.AppendLine($" \"{module.Name}\" [label=\"{label}\", fillcolor={color}];");
}
// Add edges
foreach (var dependency in analysis.Dependencies)
{
var style = dependency.Strength switch
{
"Strong" => "solid",
"Medium" => "dashed",
_ => "dotted"
};
var label = dependency.ReferenceCount > 1 ? $"[label=\"{dependency.ReferenceCount}\"]" : "";
dot.AppendLine($" \"{dependency.From}\" -> \"{dependency.To}\" [style={style}] {label};");
}
dot.AppendLine("}");
return dot.ToString();
}
private async Task<ModularStructure> GenerateModularStructureAsync(ProjectAnalysisResult analysis, ModularOptions options)
{
var modularStructure = new ModularStructure
{
GeneratedAt = DateTime.UtcNow,
GroupingStrategy = options.GroupingStrategy
};
try
{
// Step 1: Identify logical modules based on grouping strategy
var logicalModules = await IdentifyLogicalModulesAsync(analysis, options);
// Step 2: Analyze dependencies between modules
var moduleDependencies = await AnalyzeModuleDependenciesAsync(logicalModules, analysis);
// Step 3: Detect platform-specific modules
if (options.DetectPlatformModules)
{
await DetectPlatformSpecificModulesAsync(logicalModules, analysis);
}
// Step 4: Identify entry points and public interfaces
if (options.IncludeEntryPoints)
{
await IdentifyModuleEntryPointsAsync(logicalModules, analysis);
}
// Step 5: Generate scaffolding metadata
if (options.GenerateScaffoldingMetadata)
{
await GenerateScaffoldingMetadataAsync(logicalModules, analysis, options);
}
// Step 6: Analyze module tags and capabilities
if (options.AnalyzeModuleTags)
{
await AnalyzeModuleTagsAsync(logicalModules, analysis);
}
// Step 7: Generate LLM descriptions
if (options.IncludeLlmDescriptions)
{
await GenerateLlmDescriptionsAsync(logicalModules, analysis);
}
// Step 8: Set module flags
if (options.IncludeModuleFlags)
{
await SetModuleFlagsAsync(logicalModules, analysis);
}
// Step 9: Build the final modular structure
modularStructure.Modules = logicalModules;
modularStructure.ModuleDependencies = moduleDependencies;
modularStructure.ReusabilityAnalysis = await AnalyzeModuleReusabilityAsync(logicalModules, moduleDependencies);
_logger?.LogInformation("Generated modular structure with {ModuleCount} logical modules", logicalModules.Count);
return modularStructure;
}
catch (Exception ex)
{
_logger?.LogError(ex, "Failed to generate modular structure");
throw;
}
}
private async Task<List<LogicalModule>> IdentifyLogicalModulesAsync(ProjectAnalysisResult analysis, ModularOptions options)
{
var modules = new List<LogicalModule>();
var namespaceGroups = new Dictionary<string, List<string>>();
// Collect all namespaces from all projects
var allNamespaces = analysis.Modules
.SelectMany(m => m.Namespaces)
.Where(ns => !string.IsNullOrEmpty(ns))
.Distinct()
.ToList();
switch (options.GroupingStrategy.ToLower())
{
case "namespace":
modules = await GroupByNamespacePatternAsync(allNamespaces, analysis);
break;
case "folder":
modules = await GroupByFolderStructureAsync(analysis);
break;
case "feature":
modules = await GroupByFeatureDetectionAsync(allNamespaces, analysis);
break;
case "auto":
default:
modules = await AutoDetectModulesAsync(allNamespaces, analysis);
break;
}
await Task.CompletedTask;
return modules;
}
private async Task<List<LogicalModule>> GroupByNamespacePatternAsync(List<string> namespaces, ProjectAnalysisResult analysis)
{
var modules = new List<LogicalModule>();
var moduleGroups = new Dictionary<string, List<string>>();
foreach (var ns in namespaces)
{
// Extract module name from namespace patterns
var moduleName = ExtractModuleNameFromNamespace(ns);
if (!moduleGroups.ContainsKey(moduleName))
{
moduleGroups[moduleName] = new List<string>();
}
moduleGroups[moduleName].Add(ns);
}
foreach (var group in moduleGroups)
{
var module = new LogicalModule
{
Name = group.Key,
Namespaces = group.Value,
ModuleType = DetermineModuleType(group.Value),
PlatformSpecific = false, // Will be detected later
Platforms = new List<string>()
};
// Find related source files
module.SourceFiles = analysis.Modules
.Where(m => m.Namespaces.Any(ns => group.Value.Contains(ns)))
.SelectMany(m => m.SourceFiles)
.Distinct()
.ToList();
modules.Add(module);
}
await Task.CompletedTask;
return modules;
}
private async Task<List<LogicalModule>> GroupByFolderStructureAsync(ProjectAnalysisResult analysis)
{
var modules = new List<LogicalModule>();
var folderGroups = new Dictionary<string, List<string>>();
foreach (var project in analysis.Modules)
{
var projectDir = Path.GetDirectoryName(project.ProjectPath) ?? string.Empty;
var subFolders = project.SourceFiles
.Select(f => Path.GetDirectoryName(f) ?? string.Empty)
.Where(d => !string.IsNullOrEmpty(d) && d != projectDir)
.Select(d => Path.GetRelativePath(projectDir, d).Split(Path.DirectorySeparatorChar)[0])
.Distinct()
.ToList();
foreach (var folder in subFolders)
{
if (!folderGroups.ContainsKey(folder))
{
folderGroups[folder] = new List<string>();
}
var folderNamespaces = project.Namespaces
.Where(ns => ns.Contains(folder, StringComparison.OrdinalIgnoreCase))
.ToList();
folderGroups[folder].AddRange(folderNamespaces);
}
}
foreach (var group in folderGroups.Where(g => g.Value.Any()))
{
var module = new LogicalModule
{
Name = group.Key,
Namespaces = group.Value.Distinct().ToList(),
ModuleType = DetermineModuleType(group.Value),
PlatformSpecific = false
};
module.SourceFiles = analysis.Modules
.SelectMany(m => m.SourceFiles)
.Where(f => Path.GetDirectoryName(f)?.Contains(group.Key, StringComparison.OrdinalIgnoreCase) == true)
.ToList();
modules.Add(module);
}
await Task.CompletedTask;
return modules;
}
private async Task<List<LogicalModule>> GroupByFeatureDetectionAsync(List<string> namespaces, ProjectAnalysisResult analysis)
{
var modules = new List<LogicalModule>();
var featurePatterns = new Dictionary<string, string[]>
{
["Authentication"] = new[] { "auth", "login", "identity", "security", "token" },
["Trading"] = new[] { "trading", "trade", "market", "order", "portfolio", "broker" },
["Charting"] = new[] { "chart", "graph", "plot", "visualization", "technical" },
["Messaging"] = new[] { "message", "notification", "push", "alert", "communication" },
["Data"] = new[] { "data", "repository", "storage", "database", "persistence" },
["UI"] = new[] { "view", "ui", "interface", "controls", "widgets", "pages" },
["API"] = new[] { "api", "service", "client", "endpoint", "request" },
["Utilities"] = new[] { "util", "helper", "common", "shared", "extension" },
["Platform"] = new[] { "platform", "ios", "android", "windows", "macos" },
["Configuration"] = new[] { "config", "setting", "preference", "option" },
["Sync"] = new[] { "sync", "synchronization", "backup", "cloud" },
["Reports"] = new[] { "report", "analytics", "statistics", "metrics" }
};
var featureGroups = new Dictionary<string, List<string>>();
foreach (var ns in namespaces)
{
var nsLower = ns.ToLower();
var matchedFeature = "Utilities"; // Default
foreach (var feature in featurePatterns)
{
if (feature.Value.Any(pattern => nsLower.Contains(pattern)))
{
matchedFeature = feature.Key;
break;
}
}
if (!featureGroups.ContainsKey(matchedFeature))
{
featureGroups[matchedFeature] = new List<string>();
}
featureGroups[matchedFeature].Add(ns);
}
foreach (var group in featureGroups.Where(g => g.Value.Any()))
{
var module = new LogicalModule
{
Name = group.Key,
Namespaces = group.Value,
ModuleType = DetermineModuleType(group.Value),
PlatformSpecific = group.Key == "Platform"
};
module.SourceFiles = analysis.Modules
.Where(m => m.Namespaces.Any(ns => group.Value.Contains(ns)))
.SelectMany(m => m.SourceFiles)
.Distinct()
.ToList();
modules.Add(module);
}
await Task.CompletedTask;
return modules;
}
private async Task<List<LogicalModule>> AutoDetectModulesAsync(List<string> namespaces, ProjectAnalysisResult analysis)
{
// Combine multiple strategies for auto-detection
var namespaceModules = await GroupByNamespacePatternAsync(namespaces, analysis);
var featureModules = await GroupByFeatureDetectionAsync(namespaces, analysis);
// Merge and deduplicate based on namespace overlap
var mergedModules = new List<LogicalModule>();
var processedNamespaces = new HashSet<string>();
// Prioritize feature-based grouping
foreach (var featureModule in featureModules)
{
if (featureModule.Namespaces.Any(ns => !processedNamespaces.Contains(ns)))
{
mergedModules.Add(featureModule);
foreach (var ns in featureModule.Namespaces)
{
processedNamespaces.Add(ns);
}
}
}
// Add remaining namespaces as separate modules
foreach (var nsModule in namespaceModules)
{
var unprocessedNamespaces = nsModule.Namespaces
.Where(ns => !processedNamespaces.Contains(ns))
.ToList();
if (unprocessedNamespaces.Any())
{
var module = new LogicalModule
{
Name = nsModule.Name,
Namespaces = unprocessedNamespaces,
ModuleType = nsModule.ModuleType,
PlatformSpecific = nsModule.PlatformSpecific
};
module.SourceFiles = analysis.Modules
.Where(m => m.Namespaces.Any(ns => unprocessedNamespaces.Contains(ns)))
.SelectMany(m => m.SourceFiles)
.Distinct()
.ToList();
mergedModules.Add(module);
}
}
return mergedModules;
}
private string ExtractModuleNameFromNamespace(string namespaceName)
{
var parts = namespaceName.Split('.');
// Look for common module indicators
var moduleIndicators = new[] { "Services", "Data", "UI", "API", "Core", "Helpers", "Utils", "Models", "Views", "Controllers" };
parts.Reverse();
foreach (var part in parts)
{
if (moduleIndicators.Contains(part, StringComparer.OrdinalIgnoreCase))
{
return part;
}
}
// Look for feature names (third part usually contains feature name)
if (parts.Length >= 3)
{
return parts[2];
}
// Fall back to last meaningful part
return parts.LastOrDefault(p => !string.IsNullOrEmpty(p) && p.Length > 2) ?? "Unknown";
}
private string DetermineModuleType(List<string> namespaces)
{
var allNamespaces = string.Join(" ", namespaces).ToLower();
if (allNamespaces.Contains("service") || allNamespaces.Contains("api"))
return "Service";
if (allNamespaces.Contains("data") || allNamespaces.Contains("repository"))
return "Data";
if (allNamespaces.Contains("ui") || allNamespaces.Contains("view") || allNamespaces.Contains("page"))
return "UI";
if (allNamespaces.Contains("model") || allNamespaces.Contains("entity"))
return "Model";
if (allNamespaces.Contains("helper") || allNamespaces.Contains("util"))
return "Utility";
if (allNamespaces.Contains("platform") || allNamespaces.Contains("ios") || allNamespaces.Contains("android"))
return "Platform";
return "Library";
}
private async Task<List<ModuleDependency>> AnalyzeModuleDependenciesAsync(List<LogicalModule> modules, ProjectAnalysisResult analysis)
{
var dependencies = new List<ModuleDependency>();
foreach (var fromModule in modules)
{
foreach (var toModule in modules)
{
if (fromModule.Name == toModule.Name) continue;
var dependencyCount = await CountModuleDependencies(fromModule, toModule, analysis);
if (dependencyCount > 0)
{
dependencies.Add(new ModuleDependency
{
From = fromModule.Name,
To = toModule.Name,
DependencyType = "Internal",
ReferenceCount = dependencyCount,
DependencyStrength = CalculateDependencyStrength(dependencyCount)
});
}
}
}
return dependencies;
}
private async Task<int> CountModuleDependencies(LogicalModule fromModule, LogicalModule toModule, ProjectAnalysisResult analysis)
{
int count = 0;
foreach (var sourceFile in fromModule.SourceFiles)
{
try
{
var content = await File.ReadAllTextAsync(sourceFile);
var tree = CSharpSyntaxTree.ParseText(content);
var root = await tree.GetRootAsync();
// Count using directives pointing to target module
var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>();
foreach (var usingDirective in usingDirectives)
{
var namespaceName = usingDirective.Name?.ToString();
if (toModule.Namespaces.Any(ns => namespaceName?.StartsWith(ns) == true))
{
count++;
}
}
// Count type references
var identifierNames = root.DescendantNodes().OfType<IdentifierNameSyntax>();
foreach (var identifier in identifierNames)
{
// This is a simplified check - in practice, you'd want semantic analysis
var identifierText = identifier.Identifier.ValueText;
if (toModule.Namespaces.Any(ns => ns.EndsWith(identifierText)))
{
count++;
}
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to analyze dependencies in {File}: {Error}", sourceFile, ex.Message);
}
}
return count;
}
private string CalculateDependencyStrength(int referenceCount)
{
return referenceCount switch
{
> 20 => "Strong",
> 5 => "Medium",
_ => "Weak"
};
}
private async Task DetectPlatformSpecificModulesAsync(List<LogicalModule> modules, ProjectAnalysisResult analysis)
{
var platformPatterns = new Dictionary<string, string[]>
{
["iOS"] = new[] { "ios", "iphone", "ipad", "xamarin.ios", "uikit", "foundation" },
["Android"] = new[] { "android", "xamarin.android", "androidx", "google.android" },
["Windows"] = new[] { "windows", "win32", "uwp", "winui", "wpf" },
["macOS"] = new[] { "macos", "osx", "appkit", "xamarin.mac" },
["Web"] = new[] { "blazor", "web", "browser", "javascript" }
};
foreach (var module in modules)
{
var moduleContent = string.Join(" ", module.Namespaces).ToLower();
foreach (var platform in platformPatterns)
{
if (platform.Value.Any(pattern => moduleContent.Contains(pattern)))
{
module.PlatformSpecific = true;
module.Platforms.Add(platform.Key);
}
}
// Check source files for platform-specific references
foreach (var sourceFile in module.SourceFiles.Take(10)) // Sample to avoid performance issues
{
try
{
var content = await File.ReadAllTextAsync(sourceFile);
var contentLower = content.ToLower();
foreach (var platform in platformPatterns)
{
if (platform.Value.Any(pattern => contentLower.Contains(pattern)) &&
!module.Platforms.Contains(platform.Key))
{
module.PlatformSpecific = true;
module.Platforms.Add(platform.Key);
}
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to analyze platform specificity in {File}: {Error}", sourceFile, ex.Message);
}
}
}
}
private async Task IdentifyModuleEntryPointsAsync(List<LogicalModule> modules, ProjectAnalysisResult analysis)
{
foreach (var module in modules)
{
var entryPoints = new List<string>();
var publicInterfaces = new List<string>();
foreach (var sourceFile in module.SourceFiles)
{
try
{
var content = await File.ReadAllTextAsync(sourceFile);
var tree = CSharpSyntaxTree.ParseText(content);
var root = await tree.GetRootAsync();
// Find public classes that could be entry points
var publicClasses = root.DescendantNodes().OfType<ClassDeclarationSyntax>()
.Where(c => c.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)));
foreach (var publicClass in publicClasses)
{
var className = publicClass.Identifier.ValueText;
// Entry point heuristics
if (className.EndsWith("Service") || className.EndsWith("Manager") ||
className.EndsWith("Controller") || className.EndsWith("Client") ||
className.EndsWith("Facade") || className.EndsWith("Gateway"))
{
entryPoints.Add($"{Path.GetFileName(sourceFile)}:{className}");
}
}
// Find public interfaces
var publicInterfaces_temp = root.DescendantNodes().OfType<InterfaceDeclarationSyntax>()
.Where(i => i.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)));
foreach (var publicInterface in publicInterfaces_temp)
{
var interfaceName = publicInterface.Identifier.ValueText;
publicInterfaces.Add($"{Path.GetFileName(sourceFile)}:{interfaceName}");
}
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to identify entry points in {File}: {Error}", sourceFile, ex.Message);
}
}
module.EntryPoints = entryPoints;
module.PublicInterfaces = publicInterfaces;
}
}
private async Task<ReusabilityAnalysis> AnalyzeModuleReusabilityAsync(List<LogicalModule> modules, List<ModuleDependency> dependencies)
{
var analysis = new ReusabilityAnalysis();
foreach (var module in modules)
{
var incomingDeps = dependencies.Count(d => d.To == module.Name);
var outgoingDeps = dependencies.Count(d => d.From == module.Name);
var totalDeps = incomingDeps + outgoingDeps;
var reusabilityScore = CalculateReusabilityScore(module, incomingDeps, outgoingDeps);
var moduleReusability = new ModuleReusability
{
ModuleName = module.Name,
ReusabilityScore = reusabilityScore,
IncomingDependencies = incomingDeps,
OutgoingDependencies = outgoingDeps,
PlatformSpecific = module.PlatformSpecific,
HasPublicInterfaces = module.PublicInterfaces.Any(),
RecommendedFor = GenerateReusabilityRecommendations(module, reusabilityScore)
};
analysis.ModuleReusability.Add(moduleReusability);
// Categorize modules
if (reusabilityScore > 0.8 && !module.PlatformSpecific)
{
analysis.HighlyReusableModules.Add(module.Name);
}
else if (module.PlatformSpecific)
{
analysis.PlatformSpecificModules.Add(module.Name);
}
else if (outgoingDeps > incomingDeps * 2)
{
analysis.UtilityModules.Add(module.Name);
}
}
await Task.CompletedTask;
return analysis;
}
private double CalculateReusabilityScore(LogicalModule module, int incomingDeps, int outgoingDeps)
{
double score = 0.5; // Base score
// Factors that increase reusability
if (module.PublicInterfaces.Any()) score += 0.2;
if (!module.PlatformSpecific) score += 0.2;
if (incomingDeps > 0) score += Math.Min(0.3, incomingDeps * 0.1);
if (module.ModuleType == "Utility" || module.ModuleType == "Service") score += 0.1;
// Factors that decrease reusability
if (outgoingDeps > 5) score -= Math.Min(0.3, (outgoingDeps - 5) * 0.05);
if (module.PlatformSpecific) score -= 0.2;
return Math.Max(0, Math.Min(1, score));
}
private List<string> GenerateReusabilityRecommendations(LogicalModule module, double reusabilityScore)
{
var recommendations = new List<string>();
if (reusabilityScore > 0.8)
{
recommendations.Add("Highly reusable - excellent candidate for shared libraries");
recommendations.Add("Consider packaging as NuGet package for cross-project use");
}
else if (reusabilityScore > 0.6)
{
recommendations.Add("Good reusability potential with minor improvements");
if (!module.PublicInterfaces.Any())
recommendations.Add("Add public interfaces to improve API design");
}
else
{
recommendations.Add("Limited reusability - consider refactoring if needed elsewhere");
if (module.PlatformSpecific)
recommendations.Add("Platform-specific - abstract platform dependencies for reusability");
}
return recommendations;
}
private Task<object> GenerateDependencyMapAsync(ProjectAnalysisResult analysis, string format)
{
var result = format.ToLower() switch
{
"mermaid" => GenerateMermaidDiagram(analysis),
"cytoscape" => GenerateCytoscapeJson(analysis),
"graphviz" => GenerateGraphvizDot(analysis),
_ => GenerateJsonMap(analysis)
};
return Task.FromResult(result);
}
private object GenerateJsonMap(ProjectAnalysisResult analysis)
{
return new
{
metadata = new
{
generatedAt = DateTime.UtcNow,
projectPath = analysis.ProjectPath,
totalModules = analysis.Modules.Count,
totalDependencies = analysis.Dependencies.Count
},
nodes = analysis.Modules.Select(m => new
{
id = m.Name,
label = m.Name,
@namespace = m.Namespace,
type = m.Type,
lineCount = m.LineCount,
classCount = m.ClassCount,
targetFramework = m.TargetFramework,
namespaces = m.Namespaces,
projectPath = m.ProjectPath
}),
edges = analysis.Dependencies.Select(d => new
{
source = d.From,
target = d.To,
type = d.Type,
strength = d.Strength,
referenceCount = d.ReferenceCount
}),
externalDependencies = analysis.ExternalDependencies.Select(ed => new
{
name = ed.Name,
version = ed.Version,
type = ed.Type,
usedBy = ed.UsedBy
})
};
}
private object GenerateModularJsonMap(ModularStructure modularStructure)
{
return new
{
metadata = new
{
generatedAt = modularStructure.GeneratedAt,
groupingStrategy = modularStructure.GroupingStrategy,
totalLogicalModules = modularStructure.Modules.Count,
totalModuleDependencies = modularStructure.ModuleDependencies.Count,
scaffoldingCompatible = true,
llmOptimized = true
},
modules = modularStructure.Modules.Select(m => new
{
// Core module information
name = m.Name,
namespaces = m.Namespaces,
moduleType = m.ModuleType,
platformSpecific = m.PlatformSpecific,
platforms = m.Platforms,
entryPoints = m.EntryPoints,
publicInterfaces = m.PublicInterfaces,
externalDependencies = m.ExternalDependencies,
// Dependencies and relationships
dependsOn = modularStructure.ModuleDependencies
.Where(d => d.From == m.Name)
.Select(d => d.To)
.ToList(),
usedBy = modularStructure.ModuleDependencies
.Where(d => d.To == m.Name)
.Select(d => d.From)
.ToList(),
// Metrics
sourceFileCount = m.SourceFiles.Count,
reusabilityScore = modularStructure.ReusabilityAnalysis?.ModuleReusability
.FirstOrDefault(r => r.ModuleName == m.Name)?.ReusabilityScore ?? 0,
// New generation-focused properties
tags = m.Tags,
// Module flags for CLI/LLM usage
optional = m.ModuleFlags?.Optional ?? true,
defaultIncluded = m.ModuleFlags?.DefaultIncluded ?? false,
coreModule = m.ModuleFlags?.CoreModule ?? false,
experimentalFeature = m.ModuleFlags?.ExperimentalFeature ?? false,
requiresConfiguration = m.ModuleFlags?.RequiresConfiguration ?? false,
hasBreakingChanges = m.ModuleFlags?.HasBreakingChanges ?? false,
// Scaffolding metadata
scaffoldable = m.ScaffoldingMetadata?.Scaffoldable ?? false,
minimumDependencies = m.ScaffoldingMetadata?.MinimumDependencies ?? new List<string>(),
lastUpdated = m.ScaffoldingMetadata?.LastUpdated ?? DateTime.UtcNow,
isDeprecated = m.ScaffoldingMetadata?.IsDeprecated ?? false,
complexityScore = m.ScaffoldingMetadata?.ComplexityScore ?? 0,
setupInstructions = m.ScaffoldingMetadata?.SetupInstructions ?? new List<string>(),
configurationFiles = m.ScaffoldingMetadata?.ConfigurationFiles ?? new List<string>(),
requiredEnvironmentVariables = m.ScaffoldingMetadata?.RequiredEnvironmentVariables ?? new List<string>(),
// LLM-friendly descriptions
promptDescription = m.LlmMetadata?.PromptDescription ?? $"The {m.Name} module provides {m.ModuleType.ToLower()} functionality.",
usageExample = m.LlmMetadata?.UsageExample ?? "",
integrationNotes = m.LlmMetadata?.IntegrationNotes ?? new List<string>(),
commonUseCases = m.LlmMetadata?.CommonUseCases ?? new List<string>(),
alternativeModules = m.LlmMetadata?.AlternativeModules ?? new List<string>()
}),
moduleDependencies = modularStructure.ModuleDependencies.Select(d => new
{
from = d.From,
to = d.To,
dependencyType = d.DependencyType,
referenceCount = d.ReferenceCount,
dependencyStrength = d.DependencyStrength
}),
reusabilityAnalysis = new
{
highlyReusableModules = modularStructure.ReusabilityAnalysis?.HighlyReusableModules ?? new List<string>(),
platformSpecificModules = modularStructure.ReusabilityAnalysis?.PlatformSpecificModules ?? new List<string>(),
utilityModules = modularStructure.ReusabilityAnalysis?.UtilityModules ?? new List<string>(),
moduleReusability = modularStructure.ReusabilityAnalysis?.ModuleReusability?.Select(r => new
{
moduleName = r.ModuleName,
reusabilityScore = r.ReusabilityScore,
incomingDependencies = r.IncomingDependencies,
outgoingDependencies = r.OutgoingDependencies,
platformSpecific = r.PlatformSpecific,
hasPublicInterfaces = r.HasPublicInterfaces,
recommendedFor = r.RecommendedFor
}).Cast<object>().ToList() ?? new List<object>()
},
// New generation-focused analysis
scaffoldingGuide = new
{
coreModules = modularStructure.Modules
.Where(m => m.ModuleFlags?.CoreModule == true)
.Select(m => m.Name)
.ToList(),
optionalModules = modularStructure.Modules
.Where(m => m.ModuleFlags?.Optional == true)
.Select(m => new { name = m.Name, tags = m.Tags })
.ToList(),
platformModules = modularStructure.Modules
.Where(m => m.PlatformSpecific)
.GroupBy(m => m.Platforms.FirstOrDefault() ?? "Unknown")
.ToDictionary(g => g.Key, g => g.Select(m => m.Name).ToList()),
deprecatedModules = modularStructure.Modules
.Where(m => m.ScaffoldingMetadata?.IsDeprecated == true)
.Select(m => m.Name)
.ToList()
},
llmPromptData = new
{
totalModules = modularStructure.Modules.Count,
modulesByCategory = modularStructure.Modules
.GroupBy(m => m.ModuleType)
.ToDictionary(g => g.Key, g => g.Select(m => new
{
name = m.Name,
description = m.LlmMetadata?.PromptDescription ?? "",
tags = m.Tags,
optional = m.ModuleFlags?.Optional ?? true
}).ToList()),
commonCombinations = GenerateCommonModuleCombinations(modularStructure.Modules),
quickStartModules = modularStructure.Modules
.Where(m => m.ModuleFlags?.DefaultIncluded == true)
.Select(m => m.Name)
.ToList()
}
};
}
private List<object> GenerateCommonModuleCombinations(List<LogicalModule> modules)
{
var combinations = new List<object>();
// Web API combination
var webApiModules = modules.Where(m =>
m.Tags.Contains("api") || m.Tags.Contains("auth") || m.Tags.Contains("database"))
.Select(m => m.Name).ToList();
if (webApiModules.Any())
{
combinations.Add(new { name = "Web API Stack", modules = webApiModules, useCase = "RESTful API development" });
}
// Mobile app combination
var mobileModules = modules.Where(m =>
m.Tags.Contains("ui") || m.Tags.Contains("auth") || m.Tags.Contains("offline") || m.Tags.Contains("notification"))
.Select(m => m.Name).ToList();
if (mobileModules.Any())
{
combinations.Add(new { name = "Mobile App Stack", modules = mobileModules, useCase = "Cross-platform mobile application" });
}
// Analytics combination
var analyticsModules = modules.Where(m =>
m.Tags.Contains("analytics") || m.Tags.Contains("logging") || m.Tags.Contains("database"))
.Select(m => m.Name).ToList();
if (analyticsModules.Any())
{
combinations.Add(new { name = "Analytics Stack", modules = analyticsModules, useCase = "Data tracking and analysis" });
}
return combinations;
}
private async Task GenerateScaffoldingMetadataAsync(List<LogicalModule> modules, ProjectAnalysisResult analysis, ModularOptions options)
{
foreach (var module in modules)
{
try
{
// Analyze module complexity and dependencies
var moduleDependencies = await AnalyzeModuleDependencyComplexity(module, modules, analysis);
// Determine if module is scaffoldable
module.ScaffoldingMetadata = new ScaffoldingMetadata
{
Scaffoldable = DetermineIfScaffoldable(module),
MinimumDependencies = moduleDependencies.Where(d => d.IsRequired).Select(d => d.ModuleName).ToList(),
LastUpdated = await GetModuleLastUpdated(module),
IsDeprecated = await CheckIfDeprecated(module),
ComplexityScore = CalculateModuleComplexity(module),
SetupInstructions = GenerateSetupInstructions(module),
ConfigurationFiles = await IdentifyConfigurationFiles(module),
RequiredEnvironmentVariables = await IdentifyRequiredEnvironmentVariables(module)
};
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to generate scaffolding metadata for module {Module}: {Error}", module.Name, ex.Message);
}
}
}
private async Task AnalyzeModuleTagsAsync(List<LogicalModule> modules, ProjectAnalysisResult analysis)
{
var tagPatterns = new Dictionary<string, string[]>
{
["auth"] = new[] { "authentication", "login", "oauth", "jwt", "identity", "security", "token" },
["navigation"] = new[] { "navigation", "routing", "menu", "page", "route" },
["offline"] = new[] { "offline", "cache", "sync", "storage", "local" },
["firebase"] = new[] { "firebase", "firestore", "messaging", "analytics", "crashlytics" },
["database"] = new[] { "database", "sql", "entity", "repository", "orm" },
["api"] = new[] { "api", "http", "rest", "client", "service", "endpoint" },
["ui"] = new[] { "ui", "view", "component", "control", "widget", "page" },
["notification"] = new[] { "notification", "push", "alert", "message" },
["analytics"] = new[] { "analytics", "tracking", "metrics", "telemetry" },
["payment"] = new[] { "payment", "billing", "stripe", "paypal", "checkout" },
["media"] = new[] { "image", "video", "audio", "camera", "photo" },
["location"] = new[] { "location", "gps", "map", "geolocation" },
["social"] = new[] { "social", "share", "facebook", "twitter", "instagram" },
["testing"] = new[] { "test", "mock", "stub", "unit", "integration" },
["logging"] = new[] { "log", "logger", "diagnostic", "debug", "trace" },
["configuration"] = new[] { "config", "setting", "preference", "option" },
["crypto"] = new[] { "crypto", "encryption", "hash", "cipher", "secure" },
["network"] = new[] { "network", "connectivity", "reachability", "internet" }
};
foreach (var module in modules)
{
var tags = new HashSet<string>();
var moduleContent = string.Join(" ", module.Namespaces).ToLower();
// Add class names and comments for better tag detection
var allContent = moduleContent;
foreach (var sourceFile in module.SourceFiles.Take(10)) // Limit for performance
{
try
{
var content = await File.ReadAllTextAsync(sourceFile);
allContent += " " + content.ToLower();
}
catch
{
// Ignore file read errors
}
}
foreach (var tagPattern in tagPatterns)
{
if (tagPattern.Value.Any(pattern => allContent.Contains(pattern)))
{
tags.Add(tagPattern.Key);
}
}
// Add module type as a tag
tags.Add(module.ModuleType.ToLower());
// Add platform tags if platform-specific
if (module.PlatformSpecific)
{
foreach (var platform in module.Platforms)
{
tags.Add(platform.ToLower());
}
}
module.Tags = tags.ToList();
}
}
private async Task GenerateLlmDescriptionsAsync(List<LogicalModule> modules, ProjectAnalysisResult analysis)
{
foreach (var module in modules)
{
try
{
var description = await GenerateModuleDescription(module, analysis);
module.LlmMetadata = new LlmMetadata
{
PromptDescription = description,
UsageExample = GenerateUsageExample(module),
IntegrationNotes = GenerateIntegrationNotes(module),
CommonUseCases = GenerateCommonUseCases(module),
AlternativeModules = await FindAlternativeModules(module, modules)
};
}
catch (Exception ex)
{
_logger?.LogWarning("Failed to generate LLM description for module {Module}: {Error}", module.Name, ex.Message);
}
}
}
private async Task SetModuleFlagsAsync(List<LogicalModule> modules, ProjectAnalysisResult analysis)
{
foreach (var module in modules)
{
module.ModuleFlags = new ModuleFlags
{
Optional = DetermineIfOptional(module),
DefaultIncluded = DetermineDefaultIncluded(module),
CoreModule = DetermineIfCoreModule(module, modules),
ExperimentalFeature = await CheckIfExperimental(module),
RequiresConfiguration = await CheckIfRequiresConfiguration(module),
HasBreakingChanges = await CheckForBreakingChanges(module)
};
}
}
// Helper methods for scaffolding metadata
private bool DetermineIfScaffoldable(LogicalModule module)
{
// Modules are scaffoldable if they have clear interfaces and aren't too complex
return module.PublicInterfaces.Any() &&
!module.Name.ToLower().Contains("legacy") &&
!module.Name.ToLower().Contains("deprecated");
}
private Task<DateTime> GetModuleLastUpdated(LogicalModule module)
{
try
{
var latestDate = DateTime.MinValue;
foreach (var file in module.SourceFiles.Take(10)) // Sample for performance
{
if (File.Exists(file))
{
var fileInfo = new FileInfo(file);
if (fileInfo.LastWriteTime > latestDate)
{
latestDate = fileInfo.LastWriteTime;
}
}
}
return Task.FromResult(latestDate == DateTime.MinValue ? DateTime.UtcNow : latestDate);
}
catch
{
return Task.FromResult(DateTime.UtcNow);
}
}
private async Task<bool> CheckIfDeprecated(LogicalModule module)
{
try
{
foreach (var file in module.SourceFiles.Take(5))
{
var content = await File.ReadAllTextAsync(file);
if (content.ToLower().Contains("deprecated") ||
content.ToLower().Contains("obsolete") ||
content.Contains("[Obsolete"))
{
return true;
}
}
return false;
}
catch
{
return false;
}
}
private double CalculateModuleComplexity(LogicalModule module)
{
// Simple complexity calculation based on various factors
double complexity = 0;
complexity += module.SourceFiles.Count * 0.1; // File count factor
complexity += module.PublicInterfaces.Count * 0.2; // Interface complexity
complexity += module.EntryPoints.Count * 0.15; // Entry point complexity
complexity += module.Namespaces.Count * 0.05; // Namespace spread
if (module.PlatformSpecific) complexity += 0.3; // Platform complexity
return Math.Min(1.0, complexity); // Cap at 1.0
}
private List<string> GenerateSetupInstructions(LogicalModule module)
{
var instructions = new List<string>();
instructions.Add($"1. Add reference to {module.Name} module");
if (module.PublicInterfaces.Any())
{
instructions.Add($"2. Register services/interfaces in DI container");
}
if (module.PlatformSpecific)
{
instructions.Add($"3. Configure platform-specific settings for {string.Join(", ", module.Platforms)}");
}
if (module.Tags.Contains("configuration"))
{
instructions.Add($"4. Update configuration files with required settings");
}
instructions.Add($"5. Initialize {module.Name} in application startup");
return instructions;
}
private Task<List<string>> IdentifyConfigurationFiles(LogicalModule module)
{
var configFiles = new List<string>();
var configPatterns = new[] { "config", "settings", "appsettings", "web.config", ".json", ".xml", ".yml", ".yaml" };
foreach (var file in module.SourceFiles)
{
var fileName = Path.GetFileName(file).ToLower();
if (configPatterns.Any(pattern => fileName.Contains(pattern)))
{
configFiles.Add(fileName);
}
}
return Task.FromResult(configFiles.Distinct().ToList());
}
private async Task<List<string>> IdentifyRequiredEnvironmentVariables(LogicalModule module)
{
var envVars = new HashSet<string>();
var envPatterns = new[] { "Environment.GetEnvironmentVariable", "Environment.GetVariable", "Environment[", "GetEnvironmentVariable", "env.", "process.env" };
foreach (var file in module.SourceFiles.Take(10))
{
try
{
var content = await File.ReadAllTextAsync(file);
foreach (var pattern in envPatterns)
{
if (content.Contains(pattern))
{
// Simple extraction - could be enhanced with regex
var lines = content.Split('\n').Where(l => l.Contains(pattern));
foreach (var line in lines)
{
var envVarMatch = ExtractEnvironmentVariableName(line);
if (!string.IsNullOrEmpty(envVarMatch))
{
envVars.Add(envVarMatch);
}
}
}
}
}
catch
{
// Ignore file read errors
}
}
return envVars.ToList();
}
private string? ExtractEnvironmentVariableName(string line)
{
// Simple extraction - looks for quoted strings after environment variable calls
var patterns = new[]
{
@"Environment\.GetEnvironmentVariable\(\s*""([^""]+)""",
@"Environment\[\s*""([^""]+)""",
@"process\.env\.([A-Z_]+)",
@"env\.([A-Z_]+)"
};
foreach (var pattern in patterns)
{
var match = System.Text.RegularExpressions.Regex.Match(line, pattern);
if (match.Success)
{
return match.Groups[1].Value;
}
}
return null;
}
private async Task<string> GenerateModuleDescription(LogicalModule module, ProjectAnalysisResult analysis)
{
var description = $"The '{module.Name}' module is a {module.ModuleType.ToLower()} component that ";
// Add functionality description based on tags
var functionDescriptions = new List<string>();
if (module.Tags.Contains("auth"))
functionDescriptions.Add("handles user authentication and authorization");
if (module.Tags.Contains("api"))
functionDescriptions.Add("provides API integration and data communication");
if (module.Tags.Contains("ui"))
functionDescriptions.Add("manages user interface components and interactions");
if (module.Tags.Contains("database"))
functionDescriptions.Add("manages data persistence and database operations");
if (module.Tags.Contains("analytics"))
functionDescriptions.Add("tracks user behavior and application metrics");
if (module.Tags.Contains("notification"))
functionDescriptions.Add("manages push notifications and messaging");
if (functionDescriptions.Any())
{
description += string.Join(", ", functionDescriptions);
}
else
{
description += $"provides {module.ModuleType.ToLower()} functionality";
}
// Add platform information
if (module.PlatformSpecific)
{
description += $" specifically for {string.Join(" and ", module.Platforms)} platforms";
}
// Add dependency information
if (module.EntryPoints.Any())
{
description += $". Main entry points include {string.Join(", ", module.EntryPoints.Take(3))}";
}
description += ".";
await Task.CompletedTask;
return description;
}
private string GenerateUsageExample(LogicalModule module)
{
if (module.EntryPoints.Any())
{
var mainEntry = module.EntryPoints.First();
var serviceName = mainEntry.Split(':').Last();
return $"// Example usage:\nvar {serviceName.ToLower()} = new {serviceName}();\n// Use {serviceName.ToLower()} for {module.ModuleType.ToLower()} operations";
}
return $"// Register {module.Name} module in your DI container\nservices.Add{module.Name}();";
}
private List<string> GenerateIntegrationNotes(LogicalModule module)
{
var notes = new List<string>();
if (module.PlatformSpecific)
{
notes.Add($"Platform-specific module - requires {string.Join(", ", module.Platforms)} runtime");
}
if (module.Tags.Contains("configuration"))
{
notes.Add("Requires configuration setup before use");
}
if (module.Tags.Contains("api"))
{
notes.Add("May require API keys or authentication tokens");
}
if (module.Tags.Contains("database"))
{
notes.Add("Requires database connection configuration");
}
return notes;
}
private List<string> GenerateCommonUseCases(LogicalModule module)
{
var useCases = new List<string>();
foreach (var tag in module.Tags.Take(5))
{
var useCase = tag switch
{
"auth" => "User login and session management",
"api" => "External service integration and data synchronization",
"ui" => "Building responsive user interfaces",
"database" => "Data storage and retrieval operations",
"analytics" => "User behavior tracking and reporting",
"notification" => "Real-time user notifications",
"payment" => "Processing financial transactions",
"media" => "Image and video processing",
_ => $"General {tag} functionality"
};
useCases.Add(useCase);
}
return useCases;
}
private async Task<List<string>> FindAlternativeModules(LogicalModule module, List<LogicalModule> allModules)
{
var alternatives = new List<string>();
// Find modules with similar tags
foreach (var otherModule in allModules)
{
if (otherModule.Name == module.Name) continue;
var commonTags = module.Tags.Intersect(otherModule.Tags).Count();
if (commonTags >= 2) // At least 2 common tags
{
alternatives.Add(otherModule.Name);
}
}
await Task.CompletedTask;
return alternatives.Take(3).ToList(); // Limit to top 3 alternatives
}
private bool DetermineIfOptional(LogicalModule module)
{
// Core functionality modules are not optional
var coreModules = new[] { "core", "data", "api", "auth", "main" };
return !coreModules.Any(core => module.Name.ToLower().Contains(core)) &&
!module.Tags.Contains("auth") &&
module.ModuleType != "Service";
}
private bool DetermineDefaultIncluded(LogicalModule module)
{
// Include core modules and commonly used utilities by default
return module.Tags.Contains("auth") ||
module.Tags.Contains("api") ||
module.Tags.Contains("ui") ||
module.ModuleType == "Service" ||
module.Name.ToLower().Contains("core");
}
private bool DetermineIfCoreModule(LogicalModule module, List<LogicalModule> allModules)
{
// A module is core if many other modules depend on it
var dependents = allModules.Count(m =>
m.Namespaces.Any(ns => module.Namespaces.Any(mns => ns.StartsWith(mns))));
return dependents > allModules.Count * 0.3 || // More than 30% of modules depend on it
module.Name.ToLower().Contains("core") ||
module.Tags.Contains("auth");
}
private async Task<bool> CheckIfExperimental(LogicalModule module)
{
try
{
foreach (var file in module.SourceFiles.Take(5))
{
var content = await File.ReadAllTextAsync(file);
if (content.ToLower().Contains("experimental") ||
content.ToLower().Contains("preview") ||
content.ToLower().Contains("beta"))
{
return true;
}
}
return false;
}
catch
{
return false;
}
}
private async Task<bool> CheckIfRequiresConfiguration(LogicalModule module)
{
return module.Tags.Contains("configuration") ||
module.Tags.Contains("api") ||
module.Tags.Contains("database") ||
module.PlatformSpecific ||
(await IdentifyConfigurationFiles(module)).Any();
}
private async Task<bool> CheckForBreakingChanges(LogicalModule module)
{
try
{
foreach (var file in module.SourceFiles.Take(5))
{
var content = await File.ReadAllTextAsync(file);
if (content.Contains("BREAKING") ||
content.Contains("breaking change") ||
content.Contains("incompatible"))
{
return true;
}
}
return false;
}
catch
{
return false;
}
}
private async Task<List<ModuleDependencyInfo>> AnalyzeModuleDependencyComplexity(LogicalModule module, List<LogicalModule> allModules, ProjectAnalysisResult analysis)
{
var dependencies = new List<ModuleDependencyInfo>();
foreach (var otherModule in allModules)
{
if (otherModule.Name == module.Name) continue;
var dependencyCount = await CountModuleDependencies(module, otherModule, analysis);
if (dependencyCount > 0)
{
dependencies.Add(new ModuleDependencyInfo
{
ModuleName = otherModule.Name,
IsRequired = dependencyCount > 5 || otherModule.Tags.Contains("auth"), // Heuristic
ComplexityImpact = CalculateDependencyComplexity(dependencyCount, otherModule)
});
}
}
return dependencies;
}
private double CalculateDependencyComplexity(int dependencyCount, LogicalModule targetModule)
{
double complexity = dependencyCount * 0.1;
if (targetModule.PlatformSpecific) complexity += 0.3;
if (targetModule.Tags.Contains("api")) complexity += 0.2;
return Math.Min(1.0, complexity);
}
public class AnalysisOptions
{
public bool IncludeExternalDependencies { get; set; }
public bool InternalOnly { get; set; }
public int MaxDepth { get; set; }
public string? NamespaceFilter { get; set; }
public bool IncludeMethodLevel { get; set; }
public bool EnableModuleGrouping { get; set; }
public string ModuleGroupingStrategy { get; set; } = string.Empty;
public bool DetectPlatformModules { get; set; }
public bool IncludeEntryPoints { get; set; }
public bool GenerateModuleDefinitions { get; set; }
public bool GenerateScaffoldingMetadata { get; set; }
public bool IncludeLlmDescriptions { get; set; }
public bool AnalyzeModuleTags { get; set; }
public bool IncludeModuleFlags { get; set; }
}
public class ModularStructure
{
public DateTime GeneratedAt { get; set; }
public string GroupingStrategy { get; set; } = string.Empty;
public List<LogicalModule> Modules { get; set; } = new();
public List<ModuleDependency> ModuleDependencies { get; set; } = new();
public ReusabilityAnalysis ReusabilityAnalysis { get; set; } = new();
}
public class ModuleDependency
{
public string From { get; set; } = string.Empty;
public string To { get; set; } = string.Empty;
public string DependencyType { get; set; } = string.Empty;
public int ReferenceCount { get; set; }
public string DependencyStrength { get; set; } = string.Empty;
}
public class ReusabilityAnalysis
{
public List<string> HighlyReusableModules { get; set; } = new();
public List<string> PlatformSpecificModules { get; set; } = new();
public List<string> UtilityModules { get; set; } = new();
public List<ModuleReusability> ModuleReusability { get; set; } = new();
}
public class ModuleReusability
{
public string ModuleName { get; set; } = string.Empty;
public double ReusabilityScore { get; set; }
public int IncomingDependencies { get; set; }
public int OutgoingDependencies { get; set; }
public bool PlatformSpecific { get; set; }
public bool HasPublicInterfaces { get; set; }
public List<string> RecommendedFor { get; set; } = new();
}
// Update the GenerateDependencyMapAsync method to handle modular structure
private Task<object> GenerateDependencyMapAsync(ProjectAnalysisResult analysis, string format, ModularStructure? modularStructure = null)
{
// If we have modular structure, use it for enhanced visualization
if (modularStructure != null)
{
var result = format.ToLower() switch
{
"mermaid" => GenerateModularMermaidDiagram(modularStructure),
"cytoscape" => GenerateModularCytoscapeJson(modularStructure),
"graphviz" => GenerateModularGraphvizDot(modularStructure),
_ => GenerateModularJsonMap(modularStructure)
};
return Task.FromResult(result);
}
// Fall back to project-level analysis
var fallbackResult = format.ToLower() switch
{
"mermaid" => GenerateMermaidDiagram(analysis),
"cytoscape" => GenerateCytoscapeJson(analysis),
"graphviz" => GenerateGraphvizDot(analysis),
_ => GenerateJsonMap(analysis)
};
return Task.FromResult(fallbackResult);
}
private string GenerateModularMermaidDiagram(ModularStructure modularStructure)
{
var mermaid = new StringBuilder();
mermaid.AppendLine("graph TD");
// Add nodes with enhanced styling based on module properties
foreach (var module in modularStructure.Modules)
{
var nodeStyle = GetModuleStyle(module);
var platformInfo = module.PlatformSpecific ? $"<br/>[{string.Join(",", module.Platforms)}]" : "";
mermaid.AppendLine($" {SanitizeNodeName(module.Name)}[\"{module.Name}<br/>({module.ModuleType}){platformInfo}\"] {nodeStyle}");
}
// Add dependencies with enhanced information
foreach (var dependency in modularStructure.ModuleDependencies)
{
var arrow = dependency.DependencyStrength switch
{
"Strong" => "==>",
"Medium" => "-->",
_ => "-..->"
};
var label = dependency.ReferenceCount > 1 ? $"|{dependency.ReferenceCount}|" : "";
mermaid.AppendLine($" {SanitizeNodeName(dependency.From)} {arrow} {SanitizeNodeName(dependency.To)} {label}");
}
// Add enhanced styling
mermaid.AppendLine();
mermaid.AppendLine(" classDef service fill:#e3f2fd,stroke:#1976d2,stroke-width:2px");
mermaid.AppendLine(" classDef ui fill:#fce4ec,stroke:#c2185b,stroke-width:2px");
mermaid.AppendLine(" classDef data fill:#e8f5e8,stroke:#388e3c,stroke-width:2px");
mermaid.AppendLine(" classDef platform fill:#fff3e0,stroke:#f57c00,stroke-width:2px");
mermaid.AppendLine(" classDef utility fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px");
return mermaid.ToString();
}
private string GetModuleStyle(LogicalModule module)
{
return module.ModuleType.ToLower() switch
{
"service" => ":::service",
"ui" => ":::ui",
"data" => ":::data",
"platform" => ":::platform",
"utility" => ":::utility",
_ => ""
};
}
private object GenerateModularCytoscapeJson(ModularStructure modularStructure)
{
return new
{
elements = new
{
nodes = modularStructure.Modules.Select(m => new
{
data = new
{
id = m.Name,
label = m.Name,
moduleType = m.ModuleType,
platformSpecific = m.PlatformSpecific,
platforms = string.Join(",", m.Platforms),
entryPointCount = m.EntryPoints.Count,
interfaceCount = m.PublicInterfaces.Count,
sourceFileCount = m.SourceFiles.Count,
reusabilityScore = modularStructure.ReusabilityAnalysis?.ModuleReusability
.FirstOrDefault(r => r.ModuleName == m.Name)?.ReusabilityScore ?? 0
}
}),
edges = modularStructure.ModuleDependencies.Select(d => new
{
data = new
{
source = d.From,
target = d.To,
dependencyType = d.DependencyType,
dependencyStrength = d.DependencyStrength,
weight = d.ReferenceCount
}
})
},
style = new object[]
{
new
{
selector = "node",
style = new
{
content = "data(label)",
width = "mapData(sourceFileCount, 1, 50, 30, 100)",
height = "mapData(entryPointCount, 0, 10, 30, 80)",
backgroundColor = "mapData(reusabilityScore, 0, 1, #ff6b6b, #51cf66)",
borderWidth = "mapData(interfaceCount, 0, 5, 1, 5)",
borderColor = "#333"
}
},
new
{
selector = "node[platformSpecific = 'true']",
style = new
{
shape = "diamond"
}
},
new
{
selector = "edge",
style = new
{
width = "mapData(weight, 1, 20, 2, 8)",
lineColor = "mapData(weight, 1, 20, #ddd, #333)",
targetArrowColor = "mapData(weight, 1, 20, #ddd, #333)",
targetArrowShape = "triangle",
curveStyle = "bezier"
}
}
},
layout = new
{
name = "cose",
directed = true,
padding = 20,
nodeRepulsion = 400000,
idealEdgeLength = 100,
edgeElasticity = 100
}
};
}
private string GenerateModularGraphvizDot(ModularStructure modularStructure)
{
var dot = new StringBuilder();
dot.AppendLine("digraph ModularMap {");
dot.AppendLine(" rankdir=TB;");
dot.AppendLine(" node [shape=box, style=filled];");
dot.AppendLine(" edge [fontsize=10];");
dot.AppendLine();
// Group modules by type for better layout
var modulesByType = modularStructure.Modules.GroupBy(m => m.ModuleType);
foreach (var typeGroup in modulesByType)
{
dot.AppendLine($" subgraph cluster_{typeGroup.Key.ToLower()} {{");
dot.AppendLine($" label=\"{typeGroup.Key} Modules\";");
dot.AppendLine(" style=rounded;");
foreach (var module in typeGroup)
{
var color = GetGraphvizColor(module);
var shape = module.PlatformSpecific ? "diamond" : "box";
var platformInfo = module.PlatformSpecific ? $"\\n[{string.Join(",", module.Platforms)}]" : "";
var label = $"{module.Name}\\n({module.ModuleType}){platformInfo}\\n{module.SourceFiles.Count} files";
dot.AppendLine($" \"{module.Name}\" [label=\"{label}\", fillcolor={color}, shape={shape}];");
}
dot.AppendLine(" }");
dot.AppendLine();
}
// Add edges with enhanced styling
foreach (var dependency in modularStructure.ModuleDependencies)
{
var style = dependency.DependencyStrength switch
{
"Strong" => "bold",
"Medium" => "solid",
_ => "dashed"
};
var color = dependency.DependencyStrength switch
{
"Strong" => "red",
"Medium" => "blue",
_ => "gray"
};
var label = dependency.ReferenceCount > 1 ? $"[label=\"{dependency.ReferenceCount}\"]" : "";
dot.AppendLine($" \"{dependency.From}\" -> \"{dependency.To}\" [style={style}, color={color}] {label};");
}
dot.AppendLine("}");
return dot.ToString();
}
private string GetGraphvizColor(LogicalModule module)
{
return module.ModuleType.ToLower() switch
{
"service" => "lightblue",
"ui" => "lightpink",
"data" => "lightgreen",
"platform" => "orange",
"utility" => "lightyellow",
_ => "lightgray"
};
}
// Update the main execution method to use the new GenerateDependencyMapAsync signature
// (This would go in the ExecuteAsync method where GenerateDependencyMapAsync is called)
// var dependencyMap = await GenerateDependencyMapAsync(analysisResult, outputFormat, modularStructure);
}
// Supporting data structures
public class AnalysisOptions
{
public bool IncludeExternalDependencies { get; set; }
public bool InternalOnly { get; set; }
public int MaxDepth { get; set; }
public string? NamespaceFilter { get; set; }
public bool IncludeMethodLevel { get; set; }
}
public class ProjectAnalysisResult
{
public string ProjectPath { get; set; } = string.Empty;
public List<ModuleInfo> Modules { get; set; } = new();
public List<DependencyInfo> Dependencies { get; set; } = new();
public List<ExternalDependencyInfo> ExternalDependencies { get; set; } = new();
public int MaxDepthReached { get; set; }
}
public class ModuleInfo
{
public string Name { get; set; } = string.Empty;
public string Namespace { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty; // Library, WebAPI, Console, etc.
public int LineCount { get; set; }
public int ClassCount { get; set; }
public string TargetFramework { get; set; } = string.Empty;
public string ProjectPath { get; set; } = string.Empty;
public List<string> SourceFiles { get; set; } = new();
public List<string> Namespaces { get; set; } = new();
public List<string> Classes { get; set; } = new();
}
public class DependencyInfo
{
public string From { get; set; } = string.Empty;
public string To { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty; // ProjectReference, PackageReference, NamespaceReference
public string Strength { get; set; } = string.Empty; // Strong, Medium, Weak
public int ReferenceCount { get; set; }
}
public class ExternalDependencyInfo
{
public string Name { get; set; } = string.Empty;
public string Version { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty; // NuGet, Framework
public List<string> UsedBy { get; set; } = new();
}
public class CouplingMetrics
{
public double OverallCouplingScore { get; set; }
public List<string> HighlyCoupledModules { get; set; } = new();
public List<string> LooselyCoupledModules { get; set; } = new();
public Dictionary<string, double> InstabilityScores { get; set; } = new();
public List<string> CircularDependencies { get; set; } = new();
public List<string> Recommendations { get; set; } = new();
}
public class ModuleCouplingMetrics
{
public int AfferentCoupling { get; set; }
public int EfferentCoupling { get; set; }
public double Instability { get; set; }
public double Abstractness { get; set; }
public double Distance { get; set; }
}
public class ArchitecturalPatterns
{
public string DetectedPattern { get; set; } = string.Empty;
public double Confidence { get; set; }
public List<string> PatternViolations { get; set; } = new();
public Dictionary<string, List<string>> LayerDefinitions { get; set; } = new();
public List<string> Suggestions { get; set; } = new();
}
public class ArchitecturalInsights
{
public double OverallArchitectureScore { get; set; }
public List<string> StrengthAreas { get; set; } = new();
public List<string> ImprovementAreas { get; set; } = new();
public List<RefactoringOpportunity> RefactoringOpportunities { get; set; } = new();
public List<string> DesignPatternSuggestions { get; set; } = new();
}
public class RefactoringOpportunity
{
public string Type { get; set; } = string.Empty;
public string Target { get; set; } = string.Empty;
public string Benefit { get; set; } = string.Empty;
public string Effort { get; set; } = string.Empty; // Low, Medium, High
public string Priority { get; set; } = string.Empty; // Low, Medium, High
}
public class LogicalModule
{
public string Name { get; set; } = string.Empty;
public List<string> Namespaces { get; set; } = new();
public string ModuleType { get; set; } = string.Empty;
public bool PlatformSpecific { get; set; }
public List<string> Platforms { get; set; } = new();
public List<string> EntryPoints { get; set; } = new();
public List<string> PublicInterfaces { get; set; } = new();
public List<string> ExternalDependencies { get; set; } = new();
public List<string> SourceFiles { get; set; } = new();
// Add these missing properties
public List<string> Tags { get; set; } = new();
public ScaffoldingMetadata ScaffoldingMetadata { get; set; } = new();
public LlmMetadata LlmMetadata { get; set; } = new();
public ModuleFlags ModuleFlags { get; set; } = new();
}
// Add these missing properties to the existing ModularOptions class
public class ModularOptions
{
public string GroupingStrategy { get; set; } = string.Empty;
public bool DetectPlatformModules { get; set; }
public bool IncludeEntryPoints { get; set; }
// Add these missing properties
public bool GenerateScaffoldingMetadata { get; set; }
public bool IncludeLlmDescriptions { get; set; }
public bool AnalyzeModuleTags { get; set; }
public bool IncludeModuleFlags { get; set; }
}
// Add these missing classes
public class ScaffoldingMetadata
{
public bool Scaffoldable { get; set; }
public List<string> MinimumDependencies { get; set; } = new();
public DateTime LastUpdated { get; set; }
public bool IsDeprecated { get; set; }
public double ComplexityScore { get; set; }
public List<string> SetupInstructions { get; set; } = new();
public List<string> ConfigurationFiles { get; set; } = new();
public List<string> RequiredEnvironmentVariables { get; set; } = new();
}
public class LlmMetadata
{
public string PromptDescription { get; set; } = string.Empty;
public string UsageExample { get; set; } = string.Empty;
public List<string> IntegrationNotes { get; set; } = new();
public List<string> CommonUseCases { get; set; } = new();
public List<string> AlternativeModules { get; set; } = new();
}
public class ModuleFlags
{
public bool Optional { get; set; }
public bool DefaultIncluded { get; set; }
public bool CoreModule { get; set; }
public bool ExperimentalFeature { get; set; }
public bool RequiresConfiguration { get; set; }
public bool HasBreakingChanges { get; set; }
}
public class ModuleDependencyInfo
{
public string ModuleName { get; set; } = string.Empty;
public bool IsRequired { get; set; }
public double ComplexityImpact { get; set; }
}
}