MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Refacto.../SolutionRefactoringPlugin.cs

799 lines
26 KiB
C#
Executable File

using MarketAlly.AIPlugin;
using LibGit2Sharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace MarketAlly.AIPlugin.Refactoring.Plugins
{
[AIPlugin("SolutionRefactoring", "Processes entire solutions with Git integration, respecting .gitignore and creating safe refactoring branches")]
public class SolutionRefactoringPlugin : IAIPlugin
{
[AIParameter("Path to solution directory (containing .sln file)", required: true)]
public string SolutionPath { get; set; }
[AIParameter("Refactoring operations to perform", required: true)]
public string Operations { get; set; }
[AIParameter("Create new Git branch for refactoring", required: false)]
public bool CreateBranch { get; set; } = true;
[AIParameter("Branch name for refactoring (auto-generated if empty)", required: false)]
public string BranchName { get; set; }
[AIParameter("Apply changes or preview only", required: false)]
public bool ApplyChanges { get; set; } = false;
[AIParameter("Respect .gitignore file", required: false)]
public bool RespectGitIgnore { get; set; } = true;
[AIParameter("Maximum files to process per project", required: false)]
public int MaxFilesPerProject { get; set; } = 100;
[AIParameter("Skip projects matching patterns (comma-separated)", required: false)]
public string SkipProjects { get; set; } = "*.Test,*.Tests,*.UnitTest";
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
{
["solutionPath"] = typeof(string),
["solutionpath"] = typeof(string),
["operations"] = typeof(string),
["createBranch"] = typeof(bool),
["createbranch"] = typeof(bool),
["branchName"] = typeof(string),
["branchname"] = typeof(string),
["applyChanges"] = typeof(bool),
["applychanges"] = typeof(bool),
["respectGitIgnore"] = typeof(bool),
["respectgitignore"] = typeof(bool),
["maxFilesPerProject"] = typeof(int),
["maxfilesperproject"] = typeof(int),
["skipProjects"] = typeof(string),
["skipprojects"] = typeof(string)
};
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
{
try
{
// Extract parameters
string solutionPath = GetParameterValue(parameters, "solutionPath", "solutionpath")?.ToString();
string operations = GetParameterValue(parameters, "operations")?.ToString();
bool createBranch = GetBoolParameter(parameters, "createBranch", "createbranch", true);
string branchName = GetParameterValue(parameters, "branchName", "branchname")?.ToString();
bool applyChanges = GetBoolParameter(parameters, "applyChanges", "applychanges", false);
bool respectGitIgnore = GetBoolParameter(parameters, "respectGitIgnore", "respectgitignore", true);
int maxFilesPerProject = GetIntParameter(parameters, "maxFilesPerProject", "maxfilesperproject", 100);
string skipProjects = GetParameterValue(parameters, "skipProjects", "skipprojects")?.ToString() ?? "*.Test,*.Tests,*.UnitTest";
// Validate solution path
if (!Directory.Exists(solutionPath))
{
return new AIPluginResult(new DirectoryNotFoundException($"Solution directory not found: {solutionPath}"), "Invalid solution path");
}
var solutionResult = new SolutionRefactoringResult
{
SolutionPath = solutionPath,
StartTime = DateTime.UtcNow
};
// Discover solution structure
await DiscoverSolutionStructure(solutionResult, solutionPath, skipProjects);
// Setup Git integration
var gitManager = new GitRefactoringManager(solutionPath);
if (createBranch && gitManager.IsGitRepository)
{
branchName = branchName ?? GenerateBranchName(operations);
solutionResult.GitInfo = await gitManager.CreateRefactoringBranch(branchName, applyChanges);
}
// Process .gitignore
var gitIgnoreRules = new List<string>();
if (respectGitIgnore)
{
gitIgnoreRules = LoadGitIgnoreRules(solutionPath);
solutionResult.GitIgnoreRules = gitIgnoreRules;
}
// Process each project
foreach (var project in solutionResult.Projects)
{
try
{
await ProcessProject(project, operations, gitIgnoreRules, maxFilesPerProject, applyChanges);
solutionResult.SuccessfulProjects++;
}
catch (Exception ex)
{
project.Error = ex.Message;
solutionResult.FailedProjects++;
}
}
solutionResult.EndTime = DateTime.UtcNow;
solutionResult.TotalDuration = solutionResult.EndTime - solutionResult.StartTime;
// Generate comprehensive summary
var summary = GenerateSolutionSummary(solutionResult);
return new AIPluginResult(new
{
Message = $"Solution refactoring completed: {solutionResult.Projects.Count} projects processed",
SolutionPath = solutionPath,
GitBranch = solutionResult.GitInfo?.NewBranchName,
ChangesApplied = applyChanges,
Summary = summary,
DetailedResult = solutionResult,
GitCommands = GenerateGitCommands(solutionResult),
Timestamp = DateTime.UtcNow
});
}
catch (Exception ex)
{
return new AIPluginResult(ex, $"Solution refactoring failed: {ex.Message}");
}
}
private async Task DiscoverSolutionStructure(SolutionRefactoringResult result, string solutionPath, string skipPatterns)
{
// Find .sln files
var solutionFiles = Directory.GetFiles(solutionPath, "*.sln", SearchOption.TopDirectoryOnly);
if (solutionFiles.Any())
{
result.SolutionFile = solutionFiles.First();
result.SolutionName = Path.GetFileNameWithoutExtension(result.SolutionFile);
// NEW: Parse .sln file to understand logical project structure
await ParseSolutionFileForProjectTypes(result, result.SolutionFile);
}
// Find all .csproj files
var projectFiles = Directory.GetFiles(solutionPath, "*.csproj", SearchOption.AllDirectories);
var skipPatternList = skipPatterns.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Trim()).ToList();
foreach (var projectFile in projectFiles)
{
var projectName = Path.GetFileNameWithoutExtension(projectFile);
// Skip test projects and other excluded patterns
if (ShouldSkipProject(projectName, skipPatternList))
{
result.SkippedProjects.Add(new SkippedProject
{
Name = projectName,
Path = projectFile,
Reason = "Matches skip pattern"
});
continue;
}
var project = new ProjectRefactoringInfo
{
Name = projectName,
ProjectFilePath = projectFile,
ProjectDirectory = Path.GetDirectoryName(projectFile),
CSharpFiles = new List<string>()
};
// NEW: Analyze project file for MAUI and other special properties
await AnalyzeProjectFileProperties(project, projectFile);
// Discover C# files in project with MAUI-aware filtering
var csFiles = Directory.GetFiles(project.ProjectDirectory, "*.cs", SearchOption.AllDirectories);
// NEW: Apply MAUI-aware file filtering
var filteredFiles = csFiles.Where(file => !ShouldExcludeFileForProject(file, project, solutionPath)).ToList();
project.CSharpFiles.AddRange(filteredFiles);
project.TotalFiles = csFiles.Length;
project.ProcessableFiles = filteredFiles.Count; // NEW: Track how many we'll actually process
// NEW: Add MAUI-specific analysis if this is a MAUI project
if (project.IsMauiProject)
{
await AnalyzeMauiProjectStructure(project);
}
result.Projects.Add(project);
}
await Task.CompletedTask;
}
// NEW: Add these supporting methods to your class:
private async Task ParseSolutionFileForProjectTypes(SolutionRefactoringResult result, string solutionFile)
{
try
{
var solutionContent = await File.ReadAllTextAsync(solutionFile);
var lines = solutionContent.Split('\n');
foreach (var line in lines)
{
// Parse project entries from .sln file
var projectMatch = System.Text.RegularExpressions.Regex.Match(line,
@"Project\(""([^""]+)""\)\s*=\s*""([^""]+)"",\s*""([^""]+)"",\s*""([^""]+)""");
if (projectMatch.Success)
{
var projectTypeGuid = projectMatch.Groups[1].Value;
var projectName = projectMatch.Groups[2].Value;
var projectPath = projectMatch.Groups[3].Value;
// Store solution-level project info for later correlation
result.SolutionProjectEntries.Add(new SolutionProjectEntry
{
Name = projectName,
RelativePath = projectPath,
ProjectTypeGuid = projectTypeGuid,
IsVirtualProject = DetermineIfVirtualProject(projectTypeGuid)
});
}
}
}
catch (Exception ex)
{
Console.WriteLine($"[WARNING] Could not parse solution file: {ex.Message}");
}
}
private async Task AnalyzeProjectFileProperties(ProjectRefactoringInfo project, string projectFile)
{
try
{
var projectContent = await File.ReadAllTextAsync(projectFile);
// Check for MAUI project
project.IsMauiProject = projectContent.Contains("<UseMaui>true</UseMaui>", StringComparison.OrdinalIgnoreCase);
// Extract target frameworks
var targetFrameworksMatch = System.Text.RegularExpressions.Regex.Match(projectContent,
@"<TargetFrameworks?>(.*?)</TargetFrameworks?>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if (targetFrameworksMatch.Success)
{
project.TargetFrameworks = targetFrameworksMatch.Groups[1].Value
.Split(';', StringSplitOptions.RemoveEmptyEntries)
.Select(tf => tf.Trim())
.ToList();
}
// Check for other project types
if (projectContent.Contains("<PackageReference Include=\"Microsoft.NET.Test.Sdk\"", StringComparison.OrdinalIgnoreCase))
{
project.IsTestProject = true;
}
if (project.IsMauiProject)
{
Console.WriteLine($"[MAUI] Detected MAUI project: {project.Name}");
Console.WriteLine($" Target Frameworks: {string.Join(", ", project.TargetFrameworks)}");
}
}
catch (Exception ex)
{
Console.WriteLine($"[WARNING] Could not analyze project file {projectFile}: {ex.Message}");
}
}
private bool ShouldExcludeFileForProject(string filePath, ProjectRefactoringInfo project, string solutionBasePath)
{
var fileName = Path.GetFileName(filePath);
var relativePath = Path.GetRelativePath(solutionBasePath, filePath);
// Standard exclusions (same as before)
var standardExclusions = new[]
{
".Designer.cs", ".generated.cs", ".g.cs", "AssemblyInfo.cs",
"GlobalAssemblyInfo.cs", "Reference.cs", "TemporaryGeneratedFile",
".AssemblyAttributes.cs"
};
if (standardExclusions.Any(pattern => fileName.Contains(pattern, StringComparison.OrdinalIgnoreCase)))
return true;
// MAUI-specific exclusions
if (project.IsMauiProject)
{
// Always skip these MAUI infrastructure files
var mauiInfrastructureFiles = new[]
{
"MauiProgram.cs", "App.xaml.cs", "AppShell.xaml.cs"
};
if (mauiInfrastructureFiles.Any(file => fileName.Equals(file, StringComparison.OrdinalIgnoreCase)))
{
return true;
}
// Skip small platform-specific files (but not large ones that might need refactoring)
if (IsInMauiPlatformFolder(filePath))
{
try
{
var content = File.ReadAllText(filePath);
var lineCount = content.Split('\n').Length;
// Skip small platform files (< 50 lines), but process large ones
if (lineCount < 50)
{
return true;
}
else
{
Console.WriteLine($"[MAUI] Large platform file detected: {fileName} ({lineCount} lines) - will analyze carefully");
}
}
catch
{
// If we can't read the file, don't exclude it
}
}
}
return false;
}
private bool IsInMauiPlatformFolder(string filePath)
{
var pathParts = filePath.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
return pathParts.Any(part =>
part.Equals("Platforms", StringComparison.OrdinalIgnoreCase) ||
part.Equals("Platform", StringComparison.OrdinalIgnoreCase) ||
part.Equals("Android", StringComparison.OrdinalIgnoreCase) ||
part.Equals("iOS", StringComparison.OrdinalIgnoreCase) ||
part.Equals("MacCatalyst", StringComparison.OrdinalIgnoreCase) ||
part.Equals("Windows", StringComparison.OrdinalIgnoreCase) ||
part.Equals("Tizen", StringComparison.OrdinalIgnoreCase));
}
private async Task AnalyzeMauiProjectStructure(ProjectRefactoringInfo project)
{
project.MauiAnalysis = new MauiProjectAnalysis();
// Check for platform folders
var projectDir = project.ProjectDirectory;
var platformsDir = Path.Combine(projectDir, "Platforms");
if (Directory.Exists(platformsDir))
{
project.MauiAnalysis.HasPlatformsFolder = true;
var platformDirs = Directory.GetDirectories(platformsDir);
foreach (var platformDir in platformDirs)
{
var platformName = Path.GetFileName(platformDir);
var platformFiles = Directory.GetFiles(platformDir, "*.cs", SearchOption.AllDirectories);
project.MauiAnalysis.PlatformSpecificFiles[platformName] = platformFiles.ToList();
}
}
// Generate MAUI-specific recommendations
var recommendations = new List<string>();
if (project.TargetFrameworks.Count > 4)
{
recommendations.Add("Consider reducing target frameworks - too many platforms increase complexity");
}
var sharedBusinessLogicFiles = project.CSharpFiles
.Where(f => !IsInMauiPlatformFolder(f))
.Where(f => f.Contains("ViewModel") || f.Contains("Service") || f.Contains("Model"))
.ToList();
if (sharedBusinessLogicFiles.Count > 20)
{
recommendations.Add("Large number of shared business logic files - consider organizing into feature folders");
}
project.MauiAnalysis.RefactoringRecommendations = recommendations;
await Task.CompletedTask;
}
private bool DetermineIfVirtualProject(string projectTypeGuid)
{
// Solution folders and other virtual project types
return projectTypeGuid.Equals("{2150E333-8FDC-42A3-9474-1A3956D46DE8}", StringComparison.OrdinalIgnoreCase);
}
private bool ShouldSkipProject(string projectName, List<string> skipPatterns)
{
return skipPatterns.Any(pattern =>
{
var regexPattern = pattern.Replace("*", ".*");
return Regex.IsMatch(projectName, regexPattern, RegexOptions.IgnoreCase);
});
}
private List<string> LoadGitIgnoreRules(string solutionPath)
{
var gitIgnoreFile = Path.Combine(solutionPath, ".gitignore");
var rules = new List<string>();
if (File.Exists(gitIgnoreFile))
{
var lines = File.ReadAllLines(gitIgnoreFile);
foreach (var line in lines)
{
var trimmed = line.Trim();
if (!string.IsNullOrEmpty(trimmed) && !trimmed.StartsWith("#"))
{
rules.Add(trimmed);
}
}
}
// Add common .NET ignore patterns if not present
var commonPatterns = new[] { "bin/", "obj/", "*.user", "*.suo", ".vs/", "packages/" };
foreach (var pattern in commonPatterns)
{
if (!rules.Contains(pattern))
{
rules.Add(pattern);
}
}
return rules;
}
private bool IsFileIgnored(string filePath, string basePath, List<string> gitIgnoreRules)
{
var relativePath = Path.GetRelativePath(basePath, filePath).Replace('\\', '/');
foreach (var rule in gitIgnoreRules)
{
if (string.IsNullOrEmpty(rule)) continue;
var pattern = rule;
if (pattern.EndsWith("/"))
{
// Directory pattern
if (relativePath.StartsWith(pattern.TrimEnd('/'), StringComparison.OrdinalIgnoreCase))
return true;
}
else if (pattern.Contains("*"))
{
// Wildcard pattern
var regexPattern = "^" + Regex.Escape(pattern).Replace("\\*", ".*") + "$";
if (Regex.IsMatch(relativePath, regexPattern, RegexOptions.IgnoreCase))
return true;
}
else
{
// Exact match
if (relativePath.Equals(pattern, StringComparison.OrdinalIgnoreCase) ||
relativePath.EndsWith("/" + pattern, StringComparison.OrdinalIgnoreCase))
return true;
}
}
return false;
}
private async Task ProcessProject(ProjectRefactoringInfo project, string operations, List<string> gitIgnoreRules, int maxFiles, bool applyChanges)
{
project.StartTime = DateTime.UtcNow;
// Filter files based on .gitignore and other exclusions
var filteredFiles = project.CSharpFiles
.Where(file => !IsFileIgnored(file, project.ProjectDirectory, gitIgnoreRules))
.Where(file => !ShouldExcludeFile(file))
.Take(maxFiles)
.ToList();
project.FilesToProcess = filteredFiles.Count;
project.FilesSkipped = project.TotalFiles - project.FilesToProcess;
// Process each file with the refactoring plugins
var codeRefactoringPlugin = new CodeRefactoringPlugin();
var codeAnalysisPlugin = new CodeAnalysisPlugin();
var documentationPlugin = new EnhancedDocumentationGeneratorPlugin();
foreach (var file in filteredFiles)
{
try
{
var fileResult = new FileRefactoringResult
{
FilePath = file,
FileName = Path.GetFileName(file),
StartTime = DateTime.UtcNow
};
// Run analysis first
var analysisParams = new Dictionary<string, object>
{
["path"] = file,
["analysisDepth"] = "detailed",
["includeComplexity"] = true,
["includeCodeSmells"] = true,
["includeSuggestions"] = true
};
var analysisResult = await codeAnalysisPlugin.ExecuteAsync(analysisParams);
fileResult.AnalysisResult = analysisResult;
// Run refactoring if analysis succeeded
if (analysisResult.Success)
{
var refactorParams = new Dictionary<string, object>
{
["filePath"] = file,
["operations"] = operations,
["applyChanges"] = applyChanges,
["maxMethodLength"] = 25,
["maxClassSize"] = 400,
["minComplexityForExtraction"] = 6
};
var refactorResult = await codeRefactoringPlugin.ExecuteAsync(refactorParams);
fileResult.RefactoringResult = refactorResult;
if (refactorResult.Success && applyChanges)
{
project.FilesModified++;
}
}
// Add documentation if requested
if (operations.Contains("documentation"))
{
var docParams = new Dictionary<string, object>
{
["filePath"] = file,
["style"] = "intelligent",
["applyChanges"] = applyChanges
};
var docResult = await documentationPlugin.ExecuteAsync(docParams);
fileResult.DocumentationResult = docResult;
}
fileResult.EndTime = DateTime.UtcNow;
fileResult.Success = analysisResult.Success;
project.FileResults.Add(fileResult);
project.FilesProcessed++;
}
catch (Exception ex)
{
project.FileResults.Add(new FileRefactoringResult
{
FilePath = file,
FileName = Path.GetFileName(file),
Success = false,
Error = ex.Message
});
}
}
project.EndTime = DateTime.UtcNow;
project.Duration = project.EndTime - project.StartTime;
project.Success = project.FileResults.Any() && project.FileResults.All(f => f.Success);
}
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",
"Reference.cs", "References.cs"
};
return excludePatterns.Any(pattern => fileName.Contains(pattern, StringComparison.OrdinalIgnoreCase));
}
private string GenerateBranchName(string operations)
{
var timestamp = DateTime.Now.ToString("yyyyMMdd-HHmm");
var operationSummary = operations.Split(',').FirstOrDefault()?.Trim() ?? "refactor";
return $"refactor/{operationSummary}-{timestamp}";
}
private object GenerateSolutionSummary(SolutionRefactoringResult result)
{
var totalFiles = result.Projects.Sum(p => p.FilesToProcess);
var totalModified = result.Projects.Sum(p => p.FilesModified);
var totalSkipped = result.Projects.Sum(p => p.FilesSkipped);
return new
{
SolutionName = result.SolutionName,
TotalProjects = result.Projects.Count,
SuccessfulProjects = result.SuccessfulProjects,
FailedProjects = result.FailedProjects,
SkippedProjects = result.SkippedProjects.Count,
TotalFiles = totalFiles,
FilesModified = totalModified,
FilesSkipped = totalSkipped,
ProcessingTime = result.TotalDuration,
GitBranch = result.GitInfo?.NewBranchName,
TopIssuesFound = GetTopIssues(result),
ProjectSummaries = result.Projects.Select(p => new
{
p.Name,
p.FilesToProcess,
p.FilesModified,
p.Success,
Duration = p.Duration.TotalSeconds
}).ToList()
};
}
private object GetTopIssues(SolutionRefactoringResult result)
{
// Aggregate issues across all files
var allIssues = new List<string>();
foreach (var project in result.Projects)
{
foreach (var file in project.FileResults)
{
// Extract issues from analysis results
// This would need to parse the actual result data
if (file.AnalysisResult?.Success == true)
{
// Add logic to extract code smells and suggestions
}
}
}
return new
{
TotalIssues = allIssues.Count,
TopIssueTypes = allIssues.GroupBy(i => i).OrderByDescending(g => g.Count()).Take(5)
.Select(g => new { Type = g.Key, Count = g.Count() }).ToList()
};
}
private object GenerateGitCommands(SolutionRefactoringResult result)
{
var commands = new List<string>();
if (result.GitInfo != null)
{
commands.Add($"# Current branch: {result.GitInfo.NewBranchName}");
commands.Add("# To review changes:");
commands.Add("git diff HEAD~1");
commands.Add("git status");
commands.Add("");
commands.Add("# To commit changes:");
commands.Add($"git add .");
commands.Add($"git commit -m \"Automated refactoring: {string.Join(", ", result.GitInfo.OperationsPerformed)}\"");
commands.Add("");
commands.Add("# To merge back to main:");
commands.Add($"git checkout {result.GitInfo.OriginalBranch}");
commands.Add($"git merge {result.GitInfo.NewBranchName}");
commands.Add("");
commands.Add("# To discard changes (if needed):");
commands.Add($"git checkout {result.GitInfo.OriginalBranch}");
commands.Add($"git branch -D {result.GitInfo.NewBranchName}");
}
return commands;
}
// 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 solution processing
public class SolutionRefactoringResult
{
public string SolutionPath { get; set; }
public string SolutionFile { get; set; }
public string SolutionName { get; set; }
public List<ProjectRefactoringInfo> Projects { get; set; } = new List<ProjectRefactoringInfo>();
public List<SkippedProject> SkippedProjects { get; set; } = new List<SkippedProject>();
public List<string> GitIgnoreRules { get; set; } = new List<string>();
public GitRefactoringInfo GitInfo { get; set; }
public int SuccessfulProjects { get; set; }
public int FailedProjects { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public TimeSpan TotalDuration { get; set; }
public List<SolutionProjectEntry> SolutionProjectEntries { get; set; } = new List<SolutionProjectEntry>();
}
public class ProjectRefactoringInfo
{
public string Name { get; set; }
public string ProjectFilePath { get; set; }
public string ProjectDirectory { get; set; }
public List<string> CSharpFiles { get; set; } = new List<string>();
public int TotalFiles { get; set; }
public int FilesToProcess { get; set; }
public int FilesProcessed { get; set; }
public int FilesModified { get; set; }
public int FilesSkipped { get; set; }
public bool Success { get; set; }
public string Error { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public TimeSpan Duration { get; set; }
public List<FileRefactoringResult> FileResults { get; set; } = new List<FileRefactoringResult>();
public bool IsMauiProject { get; set; }
public bool IsTestProject { get; set; }
public List<string> TargetFrameworks { get; set; } = new List<string>();
public int ProcessableFiles { get; set; }
public MauiProjectAnalysis MauiAnalysis { get; set; }
}
public class SolutionProjectEntry
{
public string Name { get; set; } = string.Empty;
public string RelativePath { get; set; } = string.Empty;
public string ProjectTypeGuid { get; set; } = string.Empty;
public bool IsVirtualProject { get; set; }
}
public class MauiProjectAnalysis
{
public bool HasPlatformsFolder { get; set; }
public Dictionary<string, List<string>> PlatformSpecificFiles { get; set; } = new Dictionary<string, List<string>>();
public List<string> RefactoringRecommendations { get; set; } = new List<string>();
}
public class FileRefactoringResult
{
public string FilePath { get; set; }
public string FileName { get; set; }
public bool Success { get; set; }
public string Error { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public AIPluginResult AnalysisResult { get; set; }
public AIPluginResult RefactoringResult { get; set; }
public AIPluginResult DocumentationResult { get; set; }
}
public class SkippedProject
{
public string Name { get; set; }
public string Path { get; set; }
public string Reason { get; set; }
}
public class GitRefactoringInfo
{
public string RepositoryPath { get; set; }
public string OriginalBranch { get; set; }
public string OriginalCommit { get; set; }
public string NewBranchName { get; set; }
public bool BranchCreated { get; set; }
public bool Success { get; set; }
public string Error { get; set; }
public DateTime CreatedAt { get; set; }
public List<string> OperationsPerformed { get; set; } = new List<string>();
}
}