MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Refacto.../ReadmeGeneratorPlugin.cs

1340 lines
44 KiB
C#
Executable File

using MarketAlly.AIPlugin;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace MarketAlly.AIPlugin.Refactoring.Plugins
{
[AIPlugin("ReadmeGenerator", "Generates comprehensive README.md files for projects and solutions with intelligent content analysis")]
public class ReadmeGeneratorPlugin : IAIPlugin
{
[AIParameter("Path to project directory or solution file", required: true)]
public string ProjectPath { get; set; }
[AIParameter("Type of project: auto, library, application, tool, maui", required: false)]
public string ProjectType { get; set; } = "auto";
[AIParameter("Include API documentation section", required: false)]
public bool IncludeApiDocs { get; set; } = true;
[AIParameter("Include architecture diagrams", required: false)]
public bool IncludeArchitecture { get; set; } = true;
[AIParameter("Include setup and installation instructions", required: false)]
public bool IncludeSetup { get; set; } = true;
[AIParameter("Include usage examples", required: false)]
public bool IncludeExamples { get; set; } = true;
[AIParameter("Apply changes and create README.md file", required: false)]
public bool ApplyChanges { get; set; } = false;
[AIParameter("Maximum file size in characters for analysis (default: 50000)", required: false)]
public int MaxFileSize { get; set; } = 50000;
[AIParameter("Use intelligent AI-powered description generation", required: false)]
public bool UseIntelligentDescription { get; set; } = false;
[AIParameter("Intelligent description override (if provided, skips AI generation)", required: false)]
public string IntelligentDescription { get; set; } = "";
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
{
["projectPath"] = typeof(string),
["projectpath"] = typeof(string),
["projectType"] = typeof(string),
["projecttype"] = typeof(string),
["includeApiDocs"] = typeof(bool),
["includeapidocs"] = typeof(bool),
["includeArchitecture"] = typeof(bool),
["includearchitecture"] = typeof(bool),
["includeSetup"] = typeof(bool),
["includesetup"] = typeof(bool),
["includeExamples"] = typeof(bool),
["includeexamples"] = typeof(bool),
["applyChanges"] = typeof(bool),
["applychanges"] = typeof(bool),
["maxFileSize"] = typeof(int),
["maxfilesize"] = typeof(int),
["maxFilesToAnalyze"] = typeof(int),
["maxfilestoanalyze"] = typeof(int),
["useIntelligentDescription"] = typeof(bool),
["useintelligentdescription"] = typeof(bool),
["intelligentDescription"] = typeof(string),
["intelligentdescription"] = typeof(string)
};
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
{
try
{
// Extract parameters
string projectPath = GetParameterValue(parameters, "projectPath", "projectpath")?.ToString();
string projectType = GetParameterValue(parameters, "projectType", "projecttype")?.ToString()?.ToLower() ?? "auto";
bool includeApiDocs = GetBoolParameter(parameters, "includeApiDocs", "includeapidocs", true);
bool includeArchitecture = GetBoolParameter(parameters, "includeArchitecture", "includearchitecture", true);
bool includeSetup = GetBoolParameter(parameters, "includeSetup", "includesetup", true);
bool includeExamples = GetBoolParameter(parameters, "includeExamples", "includeexamples", true);
bool applyChanges = GetBoolParameter(parameters, "applyChanges", "applychanges", false);
int maxFileSize = GetIntParameter(parameters, "maxFileSize", "maxfilesize", 50000);
int maxFilesToAnalyze = GetIntParameter(parameters, "maxFilesToAnalyze", "maxfilestoanalyze", 20);
bool useIntelligentDescription = GetBoolParameter(parameters, "useIntelligentDescription", "useintelligentdescription", false);
string intelligentDescription = GetParameterValue(parameters, "intelligentDescription", "intelligentdescription")?.ToString() ?? "";
if (!Directory.Exists(projectPath) && !File.Exists(projectPath))
{
return new AIPluginResult(new DirectoryNotFoundException($"Path not found: {projectPath}"), "Invalid project path");
}
// Phase 1: Analyze project structure and extract metadata
var analysisResult = await AnalyzeProjectStructure(projectPath, projectType, maxFileSize, maxFilesToAnalyze);
if (!analysisResult.Success)
{
return new AIPluginResult(new Exception(analysisResult.Error), analysisResult.Error);
}
// Phase 2: Generate README content using structured analysis
var readmeContent = await GenerateReadmeContent(
analysisResult,
includeApiDocs,
includeArchitecture,
includeSetup,
includeExamples,
useIntelligentDescription ? intelligentDescription : null
);
// Apply changes if requested
if (applyChanges)
{
var readmePath = Path.Combine(
Directory.Exists(projectPath) ? projectPath : Path.GetDirectoryName(projectPath),
"README.md"
);
// Create backup if file exists
if (File.Exists(readmePath))
{
var backupPath = $"{readmePath}.{DateTime.Now:yyyyMMdd_HHmmss}.bak";
File.Copy(readmePath, backupPath);
}
await File.WriteAllTextAsync(readmePath, readmeContent);
return new AIPluginResult(new
{
Message = "README.md generated successfully",
ProjectPath = projectPath,
ReadmePath = readmePath,
ProjectType = analysisResult.DetectedProjectType,
FilesAnalyzed = analysisResult.FilesAnalyzed,
ContentLength = readmeContent.Length,
Sections = analysisResult.IncludedSections,
ChangesApplied = true,
Timestamp = DateTime.UtcNow
});
}
else
{
return new AIPluginResult(new
{
Message = "README.md content generated (preview mode)",
ProjectPath = projectPath,
ProjectType = analysisResult.DetectedProjectType,
FilesAnalyzed = analysisResult.FilesAnalyzed,
ContentLength = readmeContent.Length,
Sections = analysisResult.IncludedSections,
PreviewContent = readmeContent,
ChangesApplied = false,
Timestamp = DateTime.UtcNow
});
}
}
catch (Exception ex)
{
return new AIPluginResult(ex, $"README generation failed: {ex.Message}");
}
}
private async Task<ProjectAnalysisResult> AnalyzeProjectStructure(string projectPath, string projectType, int maxFileSize, int maxFilesToAnalyze)
{
var result = new ProjectAnalysisResult();
try
{
// Determine if it's a solution or single project
if (File.Exists(projectPath) && projectPath.EndsWith(".sln"))
{
result = await AnalyzeSolution(projectPath, maxFileSize, maxFilesToAnalyze);
}
else if (Directory.Exists(projectPath))
{
result = await AnalyzeDirectory(projectPath, projectType, maxFileSize, maxFilesToAnalyze);
}
else if (File.Exists(projectPath) && projectPath.EndsWith(".csproj"))
{
result = await AnalyzeProject(projectPath, projectType, maxFileSize, maxFilesToAnalyze);
}
result.Success = true;
return result;
}
catch (Exception ex)
{
result.Success = false;
result.Error = ex.Message;
return result;
}
}
private async Task<ProjectAnalysisResult> AnalyzeSolution(string solutionPath, int maxFileSize, int maxFilesToAnalyze)
{
var result = new ProjectAnalysisResult
{
ProjectName = Path.GetFileNameWithoutExtension(solutionPath),
ProjectPath = Path.GetDirectoryName(solutionPath),
IsSolution = true
};
// Parse solution file
var solutionContent = await File.ReadAllTextAsync(solutionPath);
var projectMatches = Regex.Matches(solutionContent, @"Project\(""[^""]+\""\)\s*=\s*""([^""]+)"",\s*""([^""]+)""");
foreach (Match match in projectMatches)
{
var projectName = match.Groups[1].Value;
var projectRelativePath = match.Groups[2].Value;
var projectFullPath = Path.Combine(Path.GetDirectoryName(solutionPath), projectRelativePath);
if (File.Exists(projectFullPath) && projectFullPath.EndsWith(".csproj"))
{
var projectAnalysis = await AnalyzeProject(projectFullPath, "auto", maxFileSize, maxFilesToAnalyze / 2);
result.SubProjects.Add(projectAnalysis);
}
}
// Aggregate analysis
result.DetectedProjectType = DetermineOverallProjectType(result.SubProjects);
result.KeyFiles = result.SubProjects.SelectMany(p => p.KeyFiles).Take(maxFilesToAnalyze).ToList();
result.PublicApis = result.SubProjects.SelectMany(p => p.PublicApis).ToList();
result.Dependencies = result.SubProjects.SelectMany(p => p.Dependencies).Distinct().ToList();
result.FilesAnalyzed = result.SubProjects.Sum(p => p.FilesAnalyzed);
return result;
}
private async Task<ProjectAnalysisResult> AnalyzeDirectory(string directoryPath, string projectType, int maxFileSize, int maxFilesToAnalyze)
{
var result = new ProjectAnalysisResult
{
ProjectPath = directoryPath,
ProjectName = Path.GetFileName(directoryPath)
};
// Look for project files
var projectFiles = Directory.GetFiles(directoryPath, "*.csproj", SearchOption.TopDirectoryOnly);
if (projectFiles.Any())
{
return await AnalyzeProject(projectFiles.First(), projectType, maxFileSize, maxFilesToAnalyze);
}
// Analyze as general directory
result.DetectedProjectType = "application";
await AnalyzeCodeFiles(directoryPath, result, maxFileSize, maxFilesToAnalyze);
return result;
}
private async Task<ProjectAnalysisResult> AnalyzeProject(string projectPath, string projectType, int maxFileSize, int maxFilesToAnalyze)
{
var result = new ProjectAnalysisResult
{
ProjectPath = Path.GetDirectoryName(projectPath),
ProjectName = Path.GetFileNameWithoutExtension(projectPath)
};
// Parse project file
var projectContent = await File.ReadAllTextAsync(projectPath);
result.TargetFramework = ExtractTargetFramework(projectContent);
result.Dependencies = ExtractPackageReferences(projectContent);
// Detect project type
result.DetectedProjectType = projectType == "auto"
? DetectProjectType(projectContent, result.ProjectName)
: projectType;
// Analyze code files
await AnalyzeCodeFiles(result.ProjectPath, result, maxFileSize, maxFilesToAnalyze);
return result;
}
private async Task AnalyzeCodeFiles(string directoryPath, ProjectAnalysisResult result, int maxFileSize, int maxFilesToAnalyze)
{
var csharpFiles = Directory.GetFiles(directoryPath, "*.cs", SearchOption.AllDirectories)
.Where(f => !ShouldExcludeFile(f))
.Take(maxFilesToAnalyze)
.ToList();
var prioritizedFiles = PrioritizeFiles(csharpFiles, result.DetectedProjectType);
foreach (var file in prioritizedFiles)
{
try
{
var fileInfo = new FileInfo(file);
if (fileInfo.Length > maxFileSize) continue;
var content = await File.ReadAllTextAsync(file);
var syntaxTree = CSharpSyntaxTree.ParseText(content);
var root = syntaxTree.GetRoot();
// Extract key information
var fileAnalysis = AnalyzeCodeFile(file, root, content);
result.KeyFiles.Add(fileAnalysis);
result.PublicApis.AddRange(ExtractPublicApi(root));
result.FilesAnalyzed++;
}
catch (Exception)
{
// Skip problematic files
}
}
// Detect additional features
result.HasTests = csharpFiles.Any(f => f.Contains("Test", StringComparison.OrdinalIgnoreCase));
result.HasDocumentation = Directory.GetFiles(directoryPath, "*.md", SearchOption.AllDirectories).Any();
}
private List<string> PrioritizeFiles(List<string> files, string projectType)
{
var prioritized = new List<string>();
// High priority patterns based on project type
var highPriorityPatterns = projectType switch
{
"library" => new[] { "Plugin", "Service", "Manager", "Factory", "Builder", "Repository", "Interface" },
"application" => new[] { "Program", "Main", "Startup", "Controller", "Service", "App" },
"maui" => new[] { "App", "MainPage", "AppShell", "MauiProgram", "Platform" },
"tool" => new[] { "Program", "Main", "Command", "Tool", "Cli" },
_ => new[] { "Program", "Main", "Service", "Controller", "Manager" }
};
// Add high priority files first
foreach (var pattern in highPriorityPatterns)
{
prioritized.AddRange(files.Where(f => Path.GetFileName(f).Contains(pattern, StringComparison.OrdinalIgnoreCase)));
}
// Add remaining files
prioritized.AddRange(files.Except(prioritized));
return prioritized.Distinct().ToList();
}
private CodeFileAnalysis AnalyzeCodeFile(string filePath, SyntaxNode root, string content)
{
var analysis = new CodeFileAnalysis
{
FilePath = filePath,
FileName = Path.GetFileName(filePath),
LineCount = content.Split('\n').Length
};
// Extract classes and interfaces
var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
var interfaces = root.DescendantNodes().OfType<InterfaceDeclarationSyntax>();
analysis.Classes = classes.Select(c => c.Identifier.ValueText).ToList();
analysis.Interfaces = interfaces.Select(i => i.Identifier.ValueText).ToList();
// Extract key patterns
analysis.HasAttributes = root.DescendantNodes().OfType<AttributeSyntax>().Any();
analysis.HasAsyncMethods = root.DescendantNodes().OfType<MethodDeclarationSyntax>()
.Any(m => m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.AsyncKeyword)));
// Extract namespace
var namespaceDecl = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>().FirstOrDefault();
analysis.Namespace = namespaceDecl?.Name.ToString();
return analysis;
}
private List<ApiMethod> ExtractPublicApi(SyntaxNode root)
{
var apis = new List<ApiMethod>();
var publicMethods = root.DescendantNodes().OfType<MethodDeclarationSyntax>()
.Where(m => m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.PublicKeyword)))
.Where(m => !IsConstructor(m) && !IsPropertyAccessor(m)); // Filter out constructors and property accessors
foreach (var method in publicMethods)
{
var className = GetContainingClassName(method);
var methodName = method.Identifier.ValueText;
// Skip duplicate ExecuteAsync methods - only include one per class
if (methodName == "ExecuteAsync" && apis.Any(a => a.Name == "ExecuteAsync" && a.ClassName == className))
continue;
apis.Add(new ApiMethod
{
Name = methodName,
ClassName = className,
ReturnType = method.ReturnType.ToString(),
Parameters = method.ParameterList.Parameters.Select(p =>
$"{p.Type} {p.Identifier.ValueText}").ToList(),
IsAsync = method.Modifiers.Any(m => m.IsKind(SyntaxKind.AsyncKeyword)),
Summary = ExtractDocumentationSummary(method),
IsPluginMethod = methodName == "ExecuteAsync" && className.Contains("Plugin")
});
}
// Group and prioritize meaningful methods
return apis.GroupBy(a => $"{a.ClassName}.{a.Name}")
.Select(g => g.First()) // Remove exact duplicates
.Where(a => !IsBoilerplateMethod(a))
.OrderBy(a => a.IsPluginMethod ? 0 : 1) // Plugin methods first
.ThenBy(a => a.ClassName)
.ThenBy(a => a.Name)
.ToList();
}
private bool IsConstructor(MethodDeclarationSyntax method)
{
return method.Identifier.ValueText == method.Parent?.ChildTokens()
.FirstOrDefault(t => t.IsKind(SyntaxKind.IdentifierToken)).ValueText;
}
private bool IsPropertyAccessor(MethodDeclarationSyntax method)
{
return method.Parent is AccessorDeclarationSyntax;
}
private string GetContainingClassName(MethodDeclarationSyntax method)
{
var classDecl = method.FirstAncestorOrSelf<ClassDeclarationSyntax>();
return classDecl?.Identifier.ValueText ?? "Unknown";
}
private bool IsBoilerplateMethod(ApiMethod method)
{
var boilerplateMethods = new[] { "ToString", "GetHashCode", "Equals", "GetType" };
return boilerplateMethods.Contains(method.Name);
}
private string ExtractDocumentationSummary(SyntaxNode node)
{
var docComment = node.GetLeadingTrivia()
.FirstOrDefault(t => t.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia));
if (docComment.IsKind(SyntaxKind.None)) return null;
var commentText = docComment.ToString();
var summaryMatch = Regex.Match(commentText, @"<summary>\s*(.*?)\s*</summary>", RegexOptions.Singleline);
return summaryMatch.Success
? summaryMatch.Groups[1].Value.Trim().Replace("///", "").Trim()
: null;
}
private string DetectProjectType(string projectContent, string projectName)
{
if (projectContent.Contains("<UseMaui>true</UseMaui>"))
return "maui";
if (projectContent.Contains("<OutputType>Exe</OutputType>") ||
projectName.ToLower().Contains("console") ||
projectName.ToLower().Contains("tool") ||
projectName.ToLower().Contains("cli"))
return "tool";
if (projectContent.Contains("Microsoft.AspNetCore") ||
projectContent.Contains("Microsoft.Extensions.Hosting"))
return "application";
if (projectContent.Contains("<OutputType>Library</OutputType>") ||
projectName.ToLower().Contains("library") ||
projectName.ToLower().Contains("plugin"))
return "library";
return "application";
}
private string DetermineOverallProjectType(List<ProjectAnalysisResult> projects)
{
if (projects.Any(p => p.DetectedProjectType == "maui"))
return "maui";
var types = projects.Select(p => p.DetectedProjectType).ToList();
return types.GroupBy(t => t).OrderByDescending(g => g.Count()).First().Key;
}
private string ExtractTargetFramework(string projectContent)
{
var match = Regex.Match(projectContent, @"<TargetFramework[s]?>(.*?)</TargetFramework[s]?>");
return match.Success ? match.Groups[1].Value : "Unknown";
}
private List<string> ExtractMauiPlatforms(string targetFramework)
{
var platforms = new List<string>();
if (string.IsNullOrEmpty(targetFramework) || targetFramework == "Unknown")
return platforms;
// Split multiple target frameworks
var frameworks = targetFramework.Split(';', ',')
.Select(f => f.Trim())
.Where(f => !string.IsNullOrEmpty(f));
foreach (var framework in frameworks)
{
var platform = framework.ToLower() switch
{
var f when f.Contains("android") => "Android",
var f when f.Contains("ios") => "iOS",
var f when f.Contains("windows") => "Windows",
var f when f.Contains("maccatalyst") => "macOS (Mac Catalyst)",
var f when f.Contains("tizen") => "Tizen",
_ => null
};
if (platform != null && !platforms.Contains(platform))
{
platforms.Add(platform);
}
}
return platforms;
}
private List<string> ExtractPackageReferences(string projectContent)
{
var matches = Regex.Matches(projectContent, @"<PackageReference Include=""([^""]+)""");
return matches.Cast<Match>().Select(m => m.Groups[1].Value).ToList();
}
private bool ShouldExcludeFile(string filePath)
{
var fileName = Path.GetFileName(filePath);
var excludePatterns = new[]
{
".Designer.cs", ".generated.cs", ".g.cs", "AssemblyInfo.cs",
"GlobalAssemblyInfo.cs", "TemporaryGeneratedFile", ".AssemblyAttributes.cs"
};
return excludePatterns.Any(pattern => fileName.Contains(pattern, StringComparison.OrdinalIgnoreCase));
}
private async Task<string> GenerateReadmeContent(
ProjectAnalysisResult analysis,
bool includeApiDocs,
bool includeArchitecture,
bool includeSetup,
bool includeExamples,
string intelligentDescription = null)
{
var template = GetTemplate(analysis.DetectedProjectType);
var content = new StringBuilder();
// Handle MAUI platforms section
var platformsSection = "";
if (analysis.DetectedProjectType == "maui")
{
var platforms = ExtractMauiPlatforms(analysis.TargetFramework);
if (platforms.Any())
{
platformsSection = "## Supported Platforms\n\n" +
string.Join("\n", platforms.Select(p => $"- {p}")) + "\n\n";
}
}
// Replace template variables
var processedTemplate = template
.Replace("{{PROJECT_NAME}}", analysis.ProjectName)
.Replace("{{PROJECT_TYPE}}", FormatProjectType(analysis.DetectedProjectType))
.Replace("{{TARGET_FRAMEWORK}}", analysis.TargetFramework)
.Replace("{{DESCRIPTION}}", intelligentDescription ?? GenerateDescription(analysis))
.Replace("{{PLATFORMS_SECTION}}", platformsSection);
content.AppendLine(processedTemplate);
// Add sections based on parameters
if (includeSetup)
{
content.AppendLine(GenerateSetupSection(analysis));
}
if (includeExamples)
{
content.AppendLine(GenerateExamplesSection(analysis));
}
// Only show API docs for libraries and plugins - not for applications/tools/MAUI apps
if (includeApiDocs && analysis.PublicApis.Any() && ShouldIncludeApiDocs(analysis.DetectedProjectType))
{
content.AppendLine(GenerateApiDocumentation(analysis));
}
if (includeArchitecture)
{
content.AppendLine(GenerateArchitectureSection(analysis));
}
// Add additional sections
content.AppendLine(GenerateDependenciesSection(analysis));
content.AppendLine(GenerateContributingSection(analysis));
return content.ToString();
}
private string GetTemplate(string projectType)
{
return projectType switch
{
"library" => GetLibraryTemplate(),
"maui" => GetMauiTemplate(),
"tool" => GetToolTemplate(),
_ => GetApplicationTemplate()
};
}
private string GetLibraryTemplate()
{
return @"# {{PROJECT_NAME}}
A {{PROJECT_TYPE}} for .NET {{TARGET_FRAMEWORK}}.
## Overview
{{DESCRIPTION}}
## Features
- Modern .NET {{TARGET_FRAMEWORK}} implementation
- Comprehensive API surface
- Well-documented public interfaces
- Unit tested and production ready
";
}
private string GetMauiTemplate()
{
return @"# {{PROJECT_NAME}}
A cross-platform application built with .NET MAUI.
## Overview
{{DESCRIPTION}}
{{PLATFORMS_SECTION}}
## Features
- Cross-platform native UI
- Shared business logic
- Modern .NET implementation
";
}
private string GetToolTemplate()
{
return @"# {{PROJECT_NAME}}
A command-line tool built with .NET {{TARGET_FRAMEWORK}}.
## Overview
{{DESCRIPTION}}
## Features
- Cross-platform CLI tool
- Modern .NET {{TARGET_FRAMEWORK}} implementation
- Comprehensive command set
- Built-in help and documentation
";
}
private string GetApplicationTemplate()
{
return @"# {{PROJECT_NAME}}
A .NET {{TARGET_FRAMEWORK}} application.
## Overview
{{DESCRIPTION}}
## Features
- Modern .NET {{TARGET_FRAMEWORK}} implementation
- Scalable architecture
- Comprehensive functionality
- Production ready
";
}
private string GenerateDescription(ProjectAnalysisResult analysis)
{
var features = new List<string>();
if (analysis.PublicApis.Any())
features.Add($"Provides {analysis.PublicApis.Count} public API methods");
if (analysis.Dependencies.Any())
features.Add($"Integrates with {analysis.Dependencies.Count} external packages");
if (analysis.HasTests)
features.Add("includes comprehensive test coverage");
if (analysis.IsSolution)
features.Add($"multi-project solution with {analysis.SubProjects.Count} projects");
var description = $"This {analysis.DetectedProjectType} ";
if (features.Any())
{
description += string.Join(", ", features) + ".";
}
else
{
description += "provides essential functionality for your .NET applications.";
}
return description;
}
private string GenerateSetupSection(ProjectAnalysisResult analysis)
{
var setup = new StringBuilder();
setup.AppendLine("## Installation");
setup.AppendLine();
if (analysis.DetectedProjectType == "library")
{
setup.AppendLine("### Package Manager");
setup.AppendLine("```");
setup.AppendLine($"Install-Package {analysis.ProjectName}");
setup.AppendLine("```");
setup.AppendLine();
setup.AppendLine("### .NET CLI");
setup.AppendLine("```bash");
setup.AppendLine($"dotnet add package {analysis.ProjectName}");
setup.AppendLine("```");
}
else if (analysis.DetectedProjectType == "tool")
{
setup.AppendLine("### Install as Global Tool");
setup.AppendLine("```bash");
setup.AppendLine($"dotnet tool install -g {analysis.ProjectName}");
setup.AppendLine("```");
setup.AppendLine();
setup.AppendLine("### Local Installation");
setup.AppendLine("```bash");
setup.AppendLine("git clone <repository-url>");
setup.AppendLine($"cd {analysis.ProjectName}");
setup.AppendLine("dotnet build");
setup.AppendLine("dotnet run");
setup.AppendLine("```");
}
else if (analysis.DetectedProjectType == "maui")
{
setup.AppendLine("### Prerequisites");
var platforms = ExtractMauiPlatforms(analysis.TargetFramework);
var netVersion = ExtractNetVersion(analysis.TargetFramework);
setup.AppendLine($"- .NET {netVersion} SDK");
setup.AppendLine("- .NET MAUI workload");
if (platforms.Contains("Android"))
setup.AppendLine("- Android SDK (for Android development)");
if (platforms.Contains("iOS"))
setup.AppendLine("- Xcode (for iOS development)");
if (platforms.Contains("Windows"))
setup.AppendLine("- Windows App SDK (for Windows development)");
setup.AppendLine();
setup.AppendLine("### Build and Run");
setup.AppendLine("```bash");
setup.AppendLine("git clone <repository-url>");
setup.AppendLine($"cd {analysis.ProjectName}");
setup.AppendLine("dotnet restore");
setup.AppendLine("dotnet build");
if (platforms.Count == 1)
{
var platform = platforms.First().ToLower().Replace(" (mac catalyst)", "");
setup.AppendLine($"dotnet run --framework net{netVersion}-{platform}");
}
else
{
setup.AppendLine("# Run on specific platform:");
foreach (var platform in platforms.Take(3)) // Show top 3 platforms
{
var platformCode = platform.ToLower() switch
{
"android" => "android",
"ios" => "ios",
"windows" => "windows10.0.19041.0",
"macos (mac catalyst)" => "maccatalyst",
"tizen" => "tizen",
_ => platform.ToLower()
};
setup.AppendLine($"# dotnet run --framework net{netVersion}-{platformCode}");
}
}
setup.AppendLine("```");
}
else
{
setup.AppendLine("### Prerequisites");
var netVersion = ExtractNetVersion(analysis.TargetFramework);
setup.AppendLine($"- .NET {netVersion} SDK");
setup.AppendLine();
setup.AppendLine("### Build and Run");
setup.AppendLine("```bash");
setup.AppendLine("git clone <repository-url>");
setup.AppendLine($"cd {analysis.ProjectName}");
setup.AppendLine("dotnet restore");
setup.AppendLine("dotnet build");
setup.AppendLine("dotnet run");
setup.AppendLine("```");
}
return setup.ToString();
}
private bool ShouldIncludeApiDocs(string projectType)
{
return projectType switch
{
"library" => true, // Libraries have public APIs for consumers
"application" => false, // Applications are for end users, not developers
"tool" => false, // CLI tools focus on usage, not internal APIs
"maui" => false, // MAUI apps are end-user applications
_ => false // Default to false for unknown types
};
}
private string ExtractNetVersion(string targetFramework)
{
if (string.IsNullOrEmpty(targetFramework)) return "8.0";
// Extract version from frameworks like "net9.0-android;net9.0-ios"
var match = Regex.Match(targetFramework, @"net(\d+\.\d+)");
return match.Success ? match.Groups[1].Value : "8.0";
}
private string GenerateExamplesSection(ProjectAnalysisResult analysis)
{
var examples = new StringBuilder();
examples.AppendLine("## Usage Examples");
examples.AppendLine();
if (analysis.DetectedProjectType == "library" && analysis.PublicApis.Any())
{
// Check if this is a plugin-based architecture
var pluginClasses = analysis.KeyFiles
.SelectMany(f => f.Classes)
.Where(c => c.Contains("Plugin"))
.Take(5)
.ToList();
if (pluginClasses.Any())
{
examples.AppendLine("### Plugin Registration and Usage");
examples.AppendLine();
examples.AppendLine("```csharp");
examples.AppendLine("using " + (analysis.KeyFiles.FirstOrDefault()?.Namespace ?? analysis.ProjectName) + ";");
examples.AppendLine();
examples.AppendLine("// Register plugins");
examples.AppendLine("var registry = new AIPluginRegistry();");
foreach (var pluginClass in pluginClasses)
{
examples.AppendLine($"registry.RegisterPlugin(new {pluginClass}());");
}
examples.AppendLine();
examples.AppendLine("// Execute plugin functionality");
var firstPlugin = pluginClasses.FirstOrDefault()?.Replace("Plugin", "");
if (!string.IsNullOrEmpty(firstPlugin))
{
examples.AppendLine($"var result = await registry.CallFunctionAsync(\"{firstPlugin}\", new Dictionary<string, object>");
examples.AppendLine("{");
// Use actual parameters from the plugin if available
var pluginApi = analysis.PublicApis.FirstOrDefault(a => a.ClassName == pluginClasses.First());
if (pluginApi != null && pluginApi.Parameters.Any())
{
var sampleParam = pluginApi.Parameters.First().Split(' ').Last(); // Get parameter name
var paramType = pluginApi.Parameters.First().Split(' ').First(); // Get parameter type
var sampleValue = paramType.ToLower() switch
{
"string" => "\"example.cs\"",
"bool" => "true",
"int" => "42",
_ => "\"value\""
};
examples.AppendLine($" [\"{sampleParam}\"] = {sampleValue}");
}
else
{
examples.AppendLine(" // Parameters specific to the plugin");
}
examples.AppendLine("});");
}
examples.AppendLine("```");
// Add batch processing example if multiple plugins
if (pluginClasses.Count > 1)
{
examples.AppendLine();
examples.AppendLine("### Batch Processing");
examples.AppendLine();
examples.AppendLine("```csharp");
examples.AppendLine("// Process with multiple plugins");
var pluginNames = pluginClasses.Select(p => p.Replace("Plugin", "")).Take(3);
examples.AppendLine($"var operations = new[] {{ \"{string.Join("\", \"", pluginNames)}\" }};");
examples.AppendLine("foreach (var operation in operations)");
examples.AppendLine("{");
examples.AppendLine(" var result = await registry.CallFunctionAsync(operation, parameters);");
examples.AppendLine(" // Process result");
examples.AppendLine("}");
examples.AppendLine("```");
}
}
else
{
// For non-plugin libraries, only show examples if we have actual meaningful APIs
var meaningfulApis = analysis.PublicApis
.Where(a => !IsBoilerplateMethod(a))
.Where(a => !string.IsNullOrEmpty(a.ClassName))
.GroupBy(a => a.ClassName)
.Take(2)
.ToList();
if (meaningfulApis.Any())
{
examples.AppendLine("### Basic Usage");
examples.AppendLine();
examples.AppendLine("```csharp");
examples.AppendLine("using " + (analysis.KeyFiles.FirstOrDefault()?.Namespace ?? analysis.ProjectName) + ";");
examples.AppendLine();
foreach (var classGroup in meaningfulApis)
{
var className = classGroup.Key;
var firstMethod = classGroup.First();
examples.AppendLine($"// Using {className}");
examples.AppendLine($"var {className.ToLower()} = new {className}();");
if (firstMethod.IsAsync)
{
examples.AppendLine($"var result = await {className.ToLower()}.{firstMethod.Name}({GenerateExampleParams(firstMethod.Parameters)});");
}
else
{
examples.AppendLine($"var result = {className.ToLower()}.{firstMethod.Name}({GenerateExampleParams(firstMethod.Parameters)});");
}
examples.AppendLine();
}
examples.AppendLine("```");
}
// If no meaningful APIs found, don't show a usage section at all
}
}
else if (analysis.DetectedProjectType == "tool")
{
examples.AppendLine("### Command Line Usage");
examples.AppendLine();
examples.AppendLine("```bash");
examples.AppendLine($"# Install the tool");
examples.AppendLine($"dotnet tool install -g {analysis.ProjectName}");
examples.AppendLine();
examples.AppendLine($"# Basic usage");
examples.AppendLine($"{analysis.ProjectName.ToLower()} --help");
examples.AppendLine();
examples.AppendLine($"# Process files");
examples.AppendLine($"{analysis.ProjectName.ToLower()} process --input file.txt --output result.txt");
examples.AppendLine("```");
}
// For other project types with no clear APIs, don't show usage examples at all
return examples.ToString();
}
private string GenerateExampleParams(List<string> parameters)
{
if (!parameters.Any()) return "";
return string.Join(", ", parameters.Select(param =>
{
var parts = param.Split(' ');
var type = parts[0].ToLower();
return type switch
{
"string" => "\"example\"",
"int" => "42",
"bool" => "true",
"double" or "decimal" => "3.14",
_ => "null"
};
}));
}
private string GenerateApiDocumentation(ProjectAnalysisResult analysis)
{
var api = new StringBuilder();
api.AppendLine("## API Reference");
api.AppendLine();
if (!analysis.PublicApis.Any())
{
api.AppendLine("*API documentation will be generated when public methods are detected.*");
return api.ToString();
}
// Group APIs by class, but with better organization
var groupedApis = analysis.PublicApis
.GroupBy(a => a.ClassName ?? "General")
.Where(g => g.Any(m => !string.IsNullOrEmpty(m.Name)))
.Take(8) // Limit to top 8 classes
.ToList();
foreach (var group in groupedApis)
{
var className = group.Key;
var methods = group.Where(m => !string.IsNullOrEmpty(m.Name)).Take(6).ToList(); // Limit methods per class
if (!methods.Any()) continue;
api.AppendLine($"### {className}");
api.AppendLine();
foreach (var method in methods)
{
// Create meaningful descriptions for plugin methods
var description = GenerateMethodDescription(method, className);
if (!string.IsNullOrEmpty(description))
{
api.AppendLine($"#### {method.Name}");
api.AppendLine();
api.AppendLine(description);
api.AppendLine();
}
api.AppendLine("```csharp");
var signature = $"{method.ReturnType} {method.Name}(";
if (method.Parameters.Any())
{
signature += string.Join(", ", method.Parameters);
}
signature += ")";
api.AppendLine(signature);
api.AppendLine("```");
api.AppendLine();
}
}
return api.ToString();
}
private string GenerateMethodDescription(ApiMethod method, string className)
{
// Use existing summary if available
if (!string.IsNullOrEmpty(method.Summary))
{
return method.Summary;
}
// Generate intelligent descriptions based on patterns
if (method.IsPluginMethod && className.Contains("Plugin"))
{
return GeneratePluginDescription(className, method.Name);
}
// Generate description based on method name patterns
return GenerateMethodDescriptionFromName(method.Name, className);
}
private string GeneratePluginDescription(string className, string methodName)
{
if (methodName == "ExecuteAsync" && className.Contains("Plugin"))
{
return className.Replace("Plugin", "") switch
{
"CodeAnalysis" => "Analyzes code structure, complexity metrics, and identifies refactoring opportunities.",
"EnhancedDocumentationGenerator" => "Generates comprehensive XML documentation with AI-powered intelligent descriptions.",
"CodeFormatter" => "Formats code according to specified style guidelines and conventions.",
"NamingConvention" => "Analyzes and suggests improvements for variable, method, and class naming.",
"BatchRefactor" => "Orchestrates multiple refactoring operations across entire projects or solutions.",
"ReadmeGenerator" => "Generates comprehensive README documentation by analyzing project structure and code.",
_ => $"Executes {className.Replace("Plugin", "").ToLower()} operations on the specified code or project."
};
}
return "";
}
private string GenerateMethodDescriptionFromName(string methodName, string className)
{
// Generate descriptions based on common method name patterns
if (methodName.StartsWith("Generate")) return $"Generates content or artifacts based on the provided parameters.";
if (methodName.StartsWith("Analyze")) return $"Performs analysis on the specified input and returns detailed results.";
if (methodName.StartsWith("Process")) return $"Processes the input data and applies transformations or operations.";
if (methodName.StartsWith("Create")) return $"Creates new instances or artifacts based on the specified configuration.";
if (methodName.StartsWith("Execute")) return $"Executes the primary operation of the {className} component.";
if (methodName.StartsWith("Validate")) return $"Validates input parameters and returns validation results.";
if (methodName.StartsWith("Get")) return $"Retrieves information or data from the {className} component.";
if (methodName.StartsWith("Set")) return $"Sets or updates configuration values in the {className} component.";
return ""; // Don't generate description for unclear methods
}
private string GenerateArchitectureSection(ProjectAnalysisResult analysis)
{
var arch = new StringBuilder();
arch.AppendLine("## Architecture");
arch.AppendLine();
if (analysis.IsSolution)
{
arch.AppendLine("### Solution Structure");
arch.AppendLine();
foreach (var project in analysis.SubProjects)
{
arch.AppendLine($"- **{project.ProjectName}**: {project.DetectedProjectType}");
}
arch.AppendLine();
}
arch.AppendLine("### Key Components");
arch.AppendLine();
var componentTypes = analysis.KeyFiles
.SelectMany(f => f.Classes.Concat(f.Interfaces))
.GroupBy(GetComponentType)
.ToList();
foreach (var group in componentTypes)
{
arch.AppendLine($"- **{group.Key}**: {group.Count()} components");
}
if (analysis.DetectedProjectType == "maui")
{
arch.AppendLine();
arch.AppendLine("### MAUI Architecture");
arch.AppendLine();
arch.AppendLine("```");
arch.AppendLine("┌─────────────────┐");
arch.AppendLine("│ Shared UI │");
arch.AppendLine("├─────────────────┤");
arch.AppendLine("│ Business Logic │");
arch.AppendLine("├─────────────────┤");
arch.AppendLine("│ Platform APIs │");
arch.AppendLine("└─────────────────┘");
arch.AppendLine("```");
}
return arch.ToString();
}
private string GetComponentType(string className)
{
var lower = className.ToLower();
if (lower.Contains("service")) return "Services";
if (lower.Contains("controller")) return "Controllers";
if (lower.Contains("repository")) return "Repositories";
if (lower.Contains("manager")) return "Managers";
if (lower.Contains("factory")) return "Factories";
if (lower.Contains("builder")) return "Builders";
if (lower.Contains("plugin")) return "Plugins";
if (lower.Contains("handler")) return "Handlers";
if (lower.Contains("provider")) return "Providers";
if (lower.Contains("helper")) return "Helpers";
if (lower.Contains("util")) return "Utilities";
if (lower.StartsWith("i") && char.IsUpper(className[1])) return "Interfaces";
return "Core Classes";
}
private string GenerateDependenciesSection(ProjectAnalysisResult analysis)
{
if (!analysis.Dependencies.Any()) return "";
var deps = new StringBuilder();
deps.AppendLine("## Dependencies");
deps.AppendLine();
var majorDeps = analysis.Dependencies
.Where(d => !d.StartsWith("System.") && !d.StartsWith("Microsoft.Extensions."))
.Take(10)
.ToList();
if (majorDeps.Any())
{
deps.AppendLine("### Major Dependencies");
deps.AppendLine();
foreach (var dep in majorDeps)
{
deps.AppendLine($"- {dep}");
}
deps.AppendLine();
}
deps.AppendLine($"### Target Framework");
if (analysis.DetectedProjectType == "maui")
{
var netVersion = ExtractNetVersion(analysis.TargetFramework);
var platforms = ExtractMauiPlatforms(analysis.TargetFramework);
deps.AppendLine($"- .NET {netVersion}");
if (platforms.Any())
{
deps.AppendLine($"- Platforms: {string.Join(", ", platforms)}");
}
}
else
{
var netVersion = ExtractNetVersion(analysis.TargetFramework);
deps.AppendLine($"- .NET {netVersion}");
}
deps.AppendLine();
return deps.ToString();
}
private string GenerateContributingSection(ProjectAnalysisResult analysis)
{
var contrib = new StringBuilder();
contrib.AppendLine("## Contributing");
contrib.AppendLine();
contrib.AppendLine("1. Fork the repository");
contrib.AppendLine("2. Create a feature branch");
contrib.AppendLine("3. Make your changes");
contrib.AppendLine("4. Add tests if applicable");
contrib.AppendLine("5. Submit a pull request");
contrib.AppendLine();
if (analysis.HasTests)
{
contrib.AppendLine("### Running Tests");
contrib.AppendLine();
contrib.AppendLine("```bash");
contrib.AppendLine("dotnet test");
contrib.AppendLine("```");
contrib.AppendLine();
}
contrib.AppendLine("## License");
contrib.AppendLine();
contrib.AppendLine("This project is licensed under the MIT License - see the LICENSE file for details.");
contrib.AppendLine();
return contrib.ToString();
}
private string FormatProjectType(string projectType)
{
return projectType switch
{
"library" => ".NET Library",
"application" => ".NET Application",
"tool" => "Command-Line Tool",
"maui" => ".NET MAUI Application",
_ => ".NET Project"
};
}
// Helper methods for parameter extraction
private object GetParameterValue(IReadOnlyDictionary<string, object> parameters, params string[] keys)
{
foreach (var key in keys)
{
if (parameters.TryGetValue(key, out var value))
return value;
}
return null;
}
private bool GetBoolParameter(IReadOnlyDictionary<string, object> parameters, string key1, string key2, bool defaultValue = false)
{
var value = GetParameterValue(parameters, key1, key2);
return value != null ? Convert.ToBoolean(value) : defaultValue;
}
private int GetIntParameter(IReadOnlyDictionary<string, object> parameters, string key1, string key2, int defaultValue = 0)
{
var value = GetParameterValue(parameters, key1, key2);
return value != null ? Convert.ToInt32(value) : defaultValue;
}
}
// Supporting classes for README generation
public class ProjectAnalysisResult
{
public bool Success { get; set; }
public string Error { get; set; }
public string ProjectName { get; set; }
public string ProjectPath { get; set; }
public string DetectedProjectType { get; set; }
public string TargetFramework { get; set; }
public bool IsSolution { get; set; }
public bool HasTests { get; set; }
public bool HasDocumentation { get; set; }
public int FilesAnalyzed { get; set; }
public List<string> Dependencies { get; set; } = new List<string>();
public List<CodeFileAnalysis> KeyFiles { get; set; } = new List<CodeFileAnalysis>();
public List<ApiMethod> PublicApis { get; set; } = new List<ApiMethod>();
public List<ProjectAnalysisResult> SubProjects { get; set; } = new List<ProjectAnalysisResult>();
public List<string> IncludedSections { get; set; } = new List<string>();
}
public class CodeFileAnalysis
{
public string FilePath { get; set; }
public string FileName { get; set; }
public string Namespace { get; set; }
public int LineCount { get; set; }
public List<string> Classes { get; set; } = new List<string>();
public List<string> Interfaces { get; set; } = new List<string>();
public bool HasAttributes { get; set; }
public bool HasAsyncMethods { get; set; }
}
public class ApiMethod
{
public string Name { get; set; }
public string ClassName { get; set; }
public string ReturnType { get; set; }
public List<string> Parameters { get; set; } = new List<string>();
public bool IsAsync { get; set; }
public string Summary { get; set; }
public bool IsPluginMethod { get; set; }
}
}