30 KiB
MarketAlly.GitCommitEditor API Reference
Complete API documentation for developers and AI assistants.
Table of Contents
Services
IGitMessageImproverService
The main facade interface that composes all functionality. This is the primary entry point for most use cases.
Implements: IRepositoryManager, ICommitAnalysisService, ISuggestionService, ICommitRewriteService, IGitPushService, IHistoryHealthService, IDisposable
public interface IGitMessageImproverService : IDisposable
{
// State Management
Task LoadStateAsync(CancellationToken ct = default);
string GenerateSummaryReport();
// All methods from composed interfaces (see below)
}
Factory Method
// Create service without DI container
public static Task<GitMessageImproverService> CreateAsync(GitImproverOptions options);
Additional Methods on Implementation
// Branch management
Task<bool> CheckoutBranchAsync(ManagedRepo repo, string branchName);
IEnumerable<BackupBranchInfo> GetBackupBranches(string repoPath);
bool DeleteBranch(string repoPath, string branchName);
int DeleteAllBackupBranches(string repoPath);
// Safety checks
RewriteSafetyInfo GetRewriteSafetyInfo(string repoPath, IEnumerable<CommitAnalysis> commits);
// Batch rewrite with safety
Task<BatchRewriteResult> ExecuteBatchRewriteAsync(
string repoPath,
IEnumerable<CommitAnalysis> commits,
bool createBackup = true,
IProgress<(int Current, int Total, string CommitHash)>? progress = null,
CancellationToken ct = default);
IRepositoryManager
Manages git repository discovery and registration.
public interface IRepositoryManager
{
/// <summary>Gets all registered repositories.</summary>
IReadOnlyList<ManagedRepo> Repos { get; }
/// <summary>Scans WorkspaceRoot for git repos and registers new ones.</summary>
Task<IReadOnlyList<ManagedRepo>> ScanAndRegisterReposAsync(CancellationToken ct = default);
/// <summary>Manually register a repository by path.</summary>
Task<ManagedRepo> RegisterRepoAsync(string repoPath);
/// <summary>Unregister a repository by ID or path.</summary>
Task<bool> UnregisterRepoAsync(string repoIdOrPath);
/// <summary>Get all branches (local and remote) for a repository.</summary>
IEnumerable<BranchInfo> GetBranches(string repoPath);
}
ICommitAnalysisService
Provides commit message quality analysis.
public interface ICommitAnalysisService
{
/// <summary>Analyze commits across all registered repositories.</summary>
/// <param name="onlyNeedsImprovement">If true, only return commits with quality issues.</param>
/// <param name="progress">Reports (RepoName, CommitCount) as repos are processed.</param>
Task<IReadOnlyList<CommitAnalysis>> AnalyzeAllReposAsync(
bool onlyNeedsImprovement = true,
IProgress<(string Repo, int Processed)>? progress = null,
CancellationToken ct = default);
/// <summary>Analyze commits in a single repository.</summary>
IEnumerable<CommitAnalysis> AnalyzeRepo(ManagedRepo repo);
/// <summary>Analyze a specific commit by hash.</summary>
CommitAnalysis AnalyzeCommit(string repoPath, string commitHash);
/// <summary>Update and persist repo analysis statistics.</summary>
Task UpdateRepoAnalysisAsync(
ManagedRepo repo,
int totalCommits,
int commitsNeedingImprovement,
CancellationToken ct = default);
}
ISuggestionService
Generates AI-powered commit message suggestions.
public interface ISuggestionService
{
/// <summary>Generate AI suggestions for multiple commits.</summary>
/// <returns>Result with success/failure counts and individual failures.</returns>
Task<BatchSuggestionResult> GenerateSuggestionsAsync(
IEnumerable<CommitAnalysis> analyses,
IProgress<int>? progress = null,
CancellationToken ct = default);
/// <summary>Generate AI suggestion for a single commit.</summary>
Task<SuggestionResult> GenerateSuggestionAsync(
CommitAnalysis analysis,
CancellationToken ct = default);
}
Throws: ApiKeyNotConfiguredException if AI provider API key is not set.
ICommitRewriteService
Handles commit message rewriting operations.
public interface ICommitRewriteService
{
/// <summary>History of all rewrite operations.</summary>
IReadOnlyList<RewriteOperation> History { get; }
/// <summary>Create RewriteOperation objects for commits with suggestions.</summary>
IReadOnlyList<RewriteOperation> PreviewChanges(IEnumerable<CommitAnalysis> analyses);
/// <summary>Apply commit message changes.</summary>
/// <param name="dryRun">If true, validate but don't modify commits.</param>
Task<BatchResult> ApplyChangesAsync(
IEnumerable<RewriteOperation> operations,
bool dryRun = true,
IProgress<(int Processed, int Total)>? progress = null,
CancellationToken ct = default);
/// <summary>Apply suggested message for a single commit.</summary>
Task<RewriteOperation> ApplyChangeAsync(
CommitAnalysis analysis,
CancellationToken ct = default);
/// <summary>Undo a commit amend by resetting to original commit.</summary>
bool UndoCommitAmend(string repoPath, string originalCommitHash);
}
IGitPushService
Handles git push operations and remote tracking.
public interface IGitPushService
{
/// <summary>Check if a commit exists on the remote tracking branch.</summary>
bool IsCommitPushed(string repoPath, string commitHash);
/// <summary>Get tracking information for current branch.</summary>
TrackingInfo GetTrackingInfo(string repoPath);
/// <summary>Push current branch to remote.</summary>
GitPushResult Push(string repoPath);
/// <summary>Force push current branch to remote (overwrites remote history).</summary>
GitPushResult ForcePush(string repoPath);
}
IHistoryHealthService
Service for analyzing and reporting on git repository history health.
public interface IHistoryHealthService
{
/// <summary>Analyzes repository history health and generates a comprehensive report.</summary>
Task<HistoryHealthReport> AnalyzeHistoryHealthAsync(
string repoPath,
HistoryAnalysisOptions? options = null,
IProgress<AnalysisProgress>? progress = null,
CancellationToken ct = default);
/// <summary>Analyzes history health for a managed repository.</summary>
Task<HistoryHealthReport> AnalyzeHistoryHealthAsync(
ManagedRepo repo,
HistoryAnalysisOptions? options = null,
IProgress<AnalysisProgress>? progress = null,
CancellationToken ct = default);
/// <summary>Exports a health report to the specified format (Json, Markdown, Html, Console).</summary>
Task<string> ExportHealthReportAsync(
HistoryHealthReport report,
ReportFormat format,
CancellationToken ct = default);
/// <summary>Exports a health report to a file.</summary>
Task ExportHealthReportToFileAsync(
HistoryHealthReport report,
ReportFormat format,
string outputPath,
CancellationToken ct = default);
/// <summary>Executes a single cleanup operation.</summary>
Task<CleanupExecutionResult> ExecuteCleanupAsync(
ManagedRepo repo,
CleanupOperation operation,
CleanupExecutionOptions? options = null,
IProgress<CleanupProgress>? progress = null,
CancellationToken ct = default);
/// <summary>Executes all cleanup operations from suggestions.</summary>
Task<BatchCleanupResult> ExecuteAllCleanupsAsync(
ManagedRepo repo,
CleanupSuggestions suggestions,
CleanupExecutionOptions? options = null,
IProgress<CleanupProgress>? progress = null,
CancellationToken ct = default);
/// <summary>Creates a backup branch before cleanup operations.</summary>
Task<string> CreateBackupBranchAsync(
ManagedRepo repo,
string? branchName = null,
CancellationToken ct = default);
}
ICommitMessageAnalyzer
Analyzes commit message quality against configured rules.
public interface ICommitMessageAnalyzer
{
/// <summary>Analyze message without change context.</summary>
MessageQualityScore Analyze(string message);
/// <summary>Analyze message with context about actual changes.</summary>
/// <remarks>Enables detection of vague messages for significant changes.</remarks>
MessageQualityScore Analyze(string message, CommitContext context);
}
ICleanupExecutor
Executes cleanup operations on git repositories.
public interface ICleanupExecutor
{
/// <summary>Executes a single cleanup operation.</summary>
Task<CleanupExecutionResult> ExecuteAsync(
ManagedRepo repo,
CleanupOperation operation,
CleanupExecutionOptions? options = null,
IProgress<CleanupProgress>? progress = null,
CancellationToken ct = default);
/// <summary>Executes multiple cleanup operations in sequence.</summary>
Task<BatchCleanupResult> ExecuteBatchAsync(
ManagedRepo repo,
IEnumerable<CleanupOperation> operations,
CleanupExecutionOptions? options = null,
IProgress<CleanupProgress>? progress = null,
CancellationToken ct = default);
/// <summary>Previews what a cleanup operation will do without executing it.</summary>
Task<CleanupPreview> PreviewAsync(
ManagedRepo repo,
CleanupOperation operation,
CancellationToken ct = default);
/// <summary>Creates a backup branch before cleanup operations.</summary>
Task<string> CreateBackupBranchAsync(
ManagedRepo repo,
string? branchName = null,
CancellationToken ct = default);
}
Models
ManagedRepo
Represents a registered git repository.
public sealed class ManagedRepo
{
public string Id { get; } // GUID
public string Name { get; set; } // Directory name
public string Path { get; set; } // Full path
public string? RemoteUrl { get; set; } // Origin URL
public string? CurrentBranch { get; set; }
public DateTimeOffset? LastScannedAt { get; set; }
public DateTimeOffset? LastAnalyzedAt { get; set; }
public int TotalCommits { get; set; }
public int CommitsNeedingImprovement { get; set; }
}
CommitAnalysis
Complete analysis of a single commit.
public sealed class CommitAnalysis
{
public string RepoId { get; }
public string RepoName { get; }
public string RepoPath { get; }
public string CommitHash { get; }
public string ShortHash { get; } // First 7 characters
public string OriginalMessage { get; }
public string? SuggestedMessage { get; set; } // Populated by AI
public DateTimeOffset CommitDate { get; }
public string Author { get; }
public string AuthorEmail { get; }
public MessageQualityScore Quality { get; }
public IReadOnlyList<string> FilesChanged { get; }
public string? DiffSummary { get; }
public IReadOnlyDictionary<string, string> FileDiffs { get; } // Path -> Diff
public int LinesAdded { get; }
public int LinesDeleted { get; }
public bool IsLatestCommit { get; }
public AnalysisStatus Status { get; set; } // Pending, Analyzed, Applied, Failed, Skipped
}
MessageQualityScore
Quality assessment with detailed issues.
public sealed class MessageQualityScore
{
public int OverallScore { get; } // 0-100
public IReadOnlyList<QualityIssue> Issues { get; }
public bool NeedsImprovement { get; } // Score < 70 or has Error severity
}
QualityIssue
Individual quality problem found in a message.
public sealed class QualityIssue
{
public required string Code { get; init; } // e.g., "SUBJECT_TOO_SHORT"
public required string Message { get; init; } // Human-readable description
public required IssueSeverity Severity { get; init; }
public int ScoreImpact { get; init; } // Points deducted
}
Issue Codes:
SUBJECT_TOO_SHORT- Subject line below minimum lengthSUBJECT_TOO_LONG- Subject line exceeds maximum lengthBODY_TOO_SHORT- Body below minimum length (if required)MISSING_CONVENTIONAL_TYPE- Missing conventional commit type prefixBANNED_PHRASE- Contains banned/vague phraseMISSING_ISSUE_REFERENCE- Missing issue/ticket reference (if required)VAGUE_FOR_CHANGES- Message too vague for significant code changes
CommitContext
Context about commit changes for smarter analysis.
public sealed class CommitContext
{
public int FilesChanged { get; init; }
public int LinesAdded { get; init; }
public int LinesDeleted { get; init; }
public IReadOnlyList<string> FileNames { get; init; }
public int TotalLinesChanged { get; } // Added + Deleted
public bool HasSignificantChanges { get; } // Files > 0 or Lines > 0
public static CommitContext Empty { get; }
}
RewriteOperation
Represents a commit rewrite operation.
public sealed class RewriteOperation
{
public string Id { get; } // GUID
public string RepoId { get; set; }
public string RepoPath { get; set; }
public string CommitHash { get; set; }
public string? NewCommitHash { get; set; } // After rewrite
public string OriginalMessage { get; set; }
public string NewMessage { get; set; }
public bool IsLatestCommit { get; set; }
public OperationStatus Status { get; set; } // Pending, Applied, Failed, Undone
public string? ErrorMessage { get; set; }
public DateTimeOffset CreatedAt { get; }
public DateTimeOffset? AppliedAt { get; set; }
}
RewriteSafetyInfo
Safety information for batch rewrite operations.
public sealed class RewriteSafetyInfo
{
public bool HasUncommittedChanges { get; init; }
public bool HasPushedCommits { get; init; }
public int PushedCommitCount { get; init; }
public int LocalOnlyCommitCount { get; init; }
public int TotalCommitCount { get; init; }
public bool HasRemoteTracking { get; init; }
public string? RemoteTrackingBranch { get; init; }
public int? AheadOfRemote { get; init; }
public int? BehindRemote { get; init; }
public bool BackupBranchCreated { get; init; }
public string? BackupBranchName { get; init; }
// Computed properties
public bool CanProceedSafely { get; } // !HasUncommittedChanges && !HasPushedCommits
public bool CanProceedWithWarnings { get; } // !HasUncommittedChanges
// Methods
public IReadOnlyList<string> GetWarnings();
public string GetSummary();
}
BatchResult
Result of batch operations (ApplyChangesAsync).
public sealed class BatchResult
{
public int TotalProcessed { get; init; }
public int Successful { get; init; }
public int Failed { get; init; }
public int Skipped { get; init; }
public IReadOnlyList<RewriteOperation> Operations { get; init; }
}
BatchRewriteResult
Result of ExecuteBatchRewriteAsync.
public sealed class BatchRewriteResult
{
public bool Success { get; init; }
public int SuccessCount { get; init; }
public int FailedCount { get; init; }
public int SkippedCount { get; init; }
public string? ErrorMessage { get; init; }
public bool RequiresForcePush { get; init; }
public string? BackupBranchName { get; init; }
public IReadOnlyList<RewriteOperation> Operations { get; init; }
public static BatchRewriteResult Failure(string error);
}
BranchInfo
Information about a git branch.
public sealed class BranchInfo
{
public string Name { get; init; } // e.g., "main"
public string FullName { get; init; } // e.g., "refs/heads/main"
public bool IsRemote { get; init; }
public bool IsCurrentHead { get; init; }
public string? LastCommitSha { get; init; }
public DateTimeOffset? LastCommitDate { get; init; }
public string? RemoteName { get; init; } // e.g., "origin"
}
Result Types
// Push operation result
public sealed record GitPushResult(bool Success, string Message)
{
public static GitPushResult Ok(string message = "Push successful");
public static GitPushResult Fail(string message);
}
// Branch tracking information
public sealed record TrackingInfo(string? UpstreamBranch, int? AheadBy, int? BehindBy)
{
public static TrackingInfo None { get; }
public bool HasUpstream { get; }
public bool IsInSync { get; } // AheadBy == 0 && BehindBy == 0
}
// AI suggestion result
public sealed record SuggestionResult(
CommitAnalysis? Analysis,
string? Suggestion,
string? ErrorMessage = null,
string? RawResponse = null,
bool ReturnedOriginal = false,
int InputTokens = 0,
int OutputTokens = 0,
decimal EstimatedCost = 0)
{
public bool Success { get; }
public int TotalTokens { get; }
public static SuggestionResult Ok(CommitAnalysis analysis, string suggestion, ...);
public static SuggestionResult Succeeded(string suggestion, ...);
public static SuggestionResult Fail(CommitAnalysis analysis, string error);
public static SuggestionResult Failed(string error, string? rawResponse = null);
}
// Batch suggestion result
public sealed class BatchSuggestionResult
{
public IReadOnlyList<CommitAnalysis> Analyses { get; init; }
public int SuccessCount { get; init; }
public int FailedCount { get; init; }
public IReadOnlyList<SuggestionFailure> Failures { get; init; }
}
History Health Models
HistoryHealthReport
Complete health report with scoring, issues, and recommendations.
public sealed class HistoryHealthReport
{
public required string RepoId { get; init; }
public required string RepoName { get; init; }
public required string RepoPath { get; init; }
public required string CurrentBranch { get; init; }
public DateTimeOffset GeneratedAt { get; init; }
public int CommitsAnalyzed { get; init; }
// Summary score
public required HealthScore Score { get; init; }
// Detailed metrics
public required DuplicateCommitMetrics DuplicateMetrics { get; init; }
public required MergeCommitMetrics MergeMetrics { get; init; }
public required BranchComplexityMetrics BranchMetrics { get; init; }
public required MessageQualityDistribution MessageDistribution { get; init; }
public required AuthorshipMetrics AuthorshipMetrics { get; init; }
// Issues and recommendations
public IReadOnlyList<HealthIssue> Issues { get; init; }
public IReadOnlyList<HealthRecommendation> Recommendations { get; init; }
// Cleanup opportunities
public CleanupSuggestions? CleanupSuggestions { get; init; }
// Convenience properties
public int CriticalIssueCount { get; }
public int ErrorCount { get; }
public int WarningCount { get; }
}
HealthScore
public sealed class HealthScore
{
public int OverallScore { get; init; } // 0-100
public HealthGrade Grade { get; init; } // Excellent, Good, Fair, Poor, Critical
public int MessageQualityScore { get; init; }
public int DuplicateScore { get; init; }
public int MergeHealthScore { get; init; }
public int BranchComplexityScore { get; init; }
}
HealthIssue
public sealed class HealthIssue
{
public required string Code { get; init; }
public required string Category { get; init; }
public required HealthIssueSeverity Severity { get; init; } // Info, Warning, Error, Critical
public required string Title { get; init; }
public required string Description { get; init; }
public int ImpactScore { get; init; }
public IReadOnlyList<string> AffectedCommits { get; init; }
}
CleanupOperation
public sealed class CleanupOperation
{
public required string Id { get; init; }
public required string Title { get; init; }
public required string Description { get; init; }
public required CleanupType Type { get; init; }
public required CleanupAutomationLevel AutomationLevel { get; init; }
public EstimatedEffort Effort { get; init; }
public RiskLevel Risk { get; init; }
public int ExpectedScoreImprovement { get; init; }
public IReadOnlyList<string> AffectedCommits { get; init; }
public string? GitCommand { get; init; }
public CleanupOperationStatus Status { get; set; }
}
CleanupSuggestions
public sealed class CleanupSuggestions
{
public IReadOnlyList<CleanupOperation> AutomatedOperations { get; init; }
public IReadOnlyList<CleanupOperation> SemiAutomatedOperations { get; init; }
public IReadOnlyList<CleanupOperation> ManualOperations { get; init; }
public int TotalOperations { get; }
public int TotalExpectedImprovement { get; }
}
Options
GitImproverOptions
Main configuration object.
public sealed class GitImproverOptions
{
public string WorkspaceRoot { get; set; } // Required
public string StateFilePath { get; set; } // Default: "git-improver-state.json"
public CommitMessageRules Rules { get; set; }
public AiOptions Ai { get; set; }
public int MaxCommitsPerRepo { get; set; } // Default: 100
public DateTimeOffset? AnalyzeSince { get; set; }
public string[] ExcludedAuthors { get; set; } // Default: []
public void ValidateAndThrow();
}
CommitMessageRules
Quality rules configuration.
public sealed class CommitMessageRules
{
public int MinSubjectLength { get; set; } // Default: 10
public int MaxSubjectLength { get; set; } // Default: 72
public int MinBodyLength { get; set; } // Default: 0
public bool RequireConventionalCommit { get; set; } // Default: false
public bool RequireIssueReference { get; set; } // Default: false
public string[] BannedPhrases { get; set; } // Default: common vague words
public string[] ConventionalTypes { get; set; } // Default: feat, fix, docs, etc.
}
AiOptions
AI provider configuration.
public sealed class AiOptions
{
[JsonIgnore]
public string ApiKey { get; set; } // Not persisted
public string Provider { get; set; } // Default: "Claude"
public string Model { get; set; } // Default: "claude-sonnet-4-20250514"
public bool IncludeDiffContext { get; set; } // Default: true
public int MaxDiffLines { get; set; } // Default: 200
public int MaxTokens { get; set; } // Default: 500
public int RateLimitDelayMs { get; set; } // Default: 500
public static AIProvider ParseProvider(string? provider);
}
HistoryAnalysisOptions
public sealed class HistoryAnalysisOptions
{
public AnalysisDepth Depth { get; set; } // Quick, Standard, Deep, Full
public DateTimeOffset? Since { get; set; }
public string[]? IncludeBranches { get; set; }
public string[]? ExcludeAuthors { get; set; }
public bool AnalyzeMerges { get; set; } // Default: true
public bool DetectDuplicates { get; set; } // Default: true
}
CleanupExecutionOptions
public sealed record CleanupExecutionOptions
{
public bool CreateBackup { get; init; } // Default: true
public string? BackupBranchName { get; init; }
public bool AllowPushedCommits { get; init; } // Default: false
public bool AutoForcePush { get; init; } // Default: false
public bool UseAiForMessages { get; init; } // Default: true
}
Enumerations
Analysis & Quality
public enum AnalysisStatus { Pending, Analyzed, Applied, Failed, Skipped }
public enum IssueSeverity { Info, Warning, Error }
public enum OperationStatus { Pending, Applied, Failed, Undone }
Health Grades
public enum HealthGrade
{
Excellent, // 90-100: Best practices followed
Good, // 70-89: Minor issues, generally healthy
Fair, // 50-69: Noticeable issues, needs attention
Poor, // 30-49: Significant problems, cleanup recommended
Critical // 0-29: Severe issues, immediate action required
}
public enum HealthIssueSeverity { Info, Warning, Error, Critical }
Cleanup
public enum CleanupType
{
SquashDuplicates,
RewordMessages,
SquashMerges,
RebaseLinearize,
ArchiveBranches,
FixAuthorship,
ConsolidateMerges
}
public enum CleanupAutomationLevel
{
FullyAutomated, // Can run with one click
SemiAutomated, // Requires user review/approval
Manual // Requires manual git commands
}
public enum CleanupOperationStatus { Suggested, Approved, InProgress, Completed, Failed, Skipped }
public enum RiskLevel { None, Low, Medium, High, VeryHigh }
public enum EstimatedEffort { Minimal, Low, Medium, High, VeryHigh }
Analysis Depth & Format
public enum AnalysisDepth
{
Quick, // Sample 200 commits, basic metrics
Standard, // 1000 commits, all metrics
Deep, // 5000 commits, comprehensive
Full // All commits (slow for large repos)
}
public enum ReportFormat { Json, Markdown, Html, Console }
Extensions
ServiceCollectionExtensions
public static class ServiceCollectionExtensions
{
/// <summary>
/// Registers all GitMessageImprover services with the DI container.
/// </summary>
public static IServiceCollection AddGitMessageImprover(
this IServiceCollection services,
Action<GitImproverOptions> configureOptions);
}
Registered Services:
GitImproverOptions(Singleton)CommitMessageRules(Singleton)AiOptions(Singleton)IStateRepository(Singleton)ICommitMessageAnalyzer(Singleton)IGitOperationsService(Singleton)ICommitMessageRewriter(Singleton)IGitMessageImproverService(Singleton)
Usage Patterns
Pattern 1: CLI Tool
var options = new GitImproverOptions { WorkspaceRoot = args[0] };
await using var service = await GitMessageImproverService.CreateAsync(options);
var repos = await service.ScanAndRegisterReposAsync();
var analyses = await service.AnalyzeAllReposAsync(onlyNeedsImprovement: true);
foreach (var analysis in analyses)
{
Console.WriteLine($"[{analysis.Quality.OverallScore}] {analysis.ShortHash}: {analysis.OriginalMessage}");
}
Pattern 2: MAUI/WPF App with DI
// In MauiProgram.cs
services.AddGitMessageImprover(opt =>
{
opt.WorkspaceRoot = Preferences.Get("workspace", "");
opt.Ai.ApiKey = SecureStorage.GetAsync("api_key").Result ?? "";
});
// In ViewModel
public class CommitViewModel(IGitMessageImproverService service)
{
public async Task LoadAsync()
{
await service.LoadStateAsync();
Repos = new ObservableCollection<ManagedRepo>(service.Repos);
}
}
Pattern 3: Focused Interface Usage
// Only need push operations - inject the focused interface
public class PushHandler(IGitPushService pushService)
{
public bool SafePush(string repoPath, string commitHash)
{
if (pushService.IsCommitPushed(repoPath, commitHash))
return true;
return pushService.Push(repoPath).Success;
}
}
Pattern 4: Health Analysis Pipeline
// Analyze -> Review -> Cleanup
var report = await service.AnalyzeHistoryHealthAsync(repoPath);
if (report.Score.Grade <= HealthGrade.Fair)
{
// Export for review
var markdown = await service.ExportHealthReportAsync(report, ReportFormat.Markdown);
await File.WriteAllTextAsync("health-report.md", markdown);
// Execute safe automated cleanups
if (report.CleanupSuggestions?.AutomatedOperations.Any() == true)
{
var result = await service.ExecuteAllCleanupsAsync(
repo,
report.CleanupSuggestions,
new CleanupExecutionOptions { CreateBackup = true, AllowPushedCommits = false });
}
}
Error Handling
| Exception | Cause | Resolution |
|---|---|---|
ApiKeyNotConfiguredException |
AI API key not configured | Set AiOptions.ApiKey |
ValidationException |
Invalid GitImproverOptions |
Check required fields |
InvalidOperationException |
No suggested message for commit | Generate suggestion first |
ArgumentException |
Commit or repository not found | Verify paths and hashes |
Thread Safety Notes
- Repository cache uses
ConcurrentDictionarywith LRU eviction - All async methods support
CancellationToken - State operations are serialized through
IStateRepository - Dispose the service when done to release git handles
// Always dispose
await using var service = await GitMessageImproverService.CreateAsync(options);
// or
using var service = ...;
// or explicitly
service.Dispose();