Size: 4.0 KB Modified: 4/07/2026 7:53 PM
using System.Diagnostics;

namespace KaiChat.Services;

public class GitService : IGitService
{
    private readonly string _repoPath;
    private readonly ILogger<GitService> _logger;

    public GitService(IConfiguration config, ILogger<GitService> logger)
    {
        _repoPath = config.GetValue<string>("KaiChat:BaseFolder")
            ?? throw new InvalidOperationException("KaiChat:BaseFolder must be set");
        _logger = logger;
    }

    public string RepoPath => _repoPath;

    public async Task<GitResult> StageAllAsync()
    {
        return await RunGitAsync("add -A");
    }

    public async Task<GitResult> StagePathAsync(string repoRelativePath)
    {
        return await RunGitAsync($"add -A -- \"{repoRelativePath.Replace("\"", "\\\"")}\"");
    }

    public async Task<GitResult> CommitAsync(string message, string? body = null)
    {
        var args = new List<string> { "commit", "-m", message };
        if (!string.IsNullOrWhiteSpace(body))
        {
            args.Add("-m");
            args.Add(body);
        }

        return await RunGitAsync(args);
    }

    public async Task<GitResult> StatusAsync()
    {
        return await RunGitAsync("status --short");
    }

    public async Task<GitResult> AutoCommitAsync(string summary, string? filePath = null)
    {
        var stage = filePath != null
            ? await StagePathAsync(filePath)
            : await StageAllAsync();
        if (!stage.Success)
            return stage;

        var status = await StatusAsync();
        if (string.IsNullOrWhiteSpace(status.Output))
            return new GitResult { Success = true, Output = "Nothing to commit." };

        return await CommitAsync($"KaiChat: {summary}");
    }

    private async Task<GitResult> RunGitAsync(string arguments)
    {
        try
        {
            var psi = new ProcessStartInfo("git")
            {
                Arguments = arguments,
                WorkingDirectory = _repoPath,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            using var process = new Process { StartInfo = psi };
            process.Start();

            var output = await process.StandardOutput.ReadToEndAsync();
            var error = await process.StandardError.ReadToEndAsync();
            await process.WaitForExitAsync();

            return new GitResult
            {
                Success = process.ExitCode == 0,
                Output = output.Trim(),
                Error = error.Trim()
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Git command failed: git {Args}", arguments);
            return new GitResult { Success = false, Error = ex.Message };
        }
    }

    private async Task<GitResult> RunGitAsync(IReadOnlyList<string> arguments)
    {
        try
        {
            var psi = new ProcessStartInfo("git")
            {
                WorkingDirectory = _repoPath,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            foreach (var argument in arguments)
                psi.ArgumentList.Add(argument);

            using var process = new Process { StartInfo = psi };
            process.Start();

            var output = await process.StandardOutput.ReadToEndAsync();
            var error = await process.StandardError.ReadToEndAsync();
            await process.WaitForExitAsync();

            return new GitResult
            {
                Success = process.ExitCode == 0,
                Output = output.Trim(),
                Error = error.Trim()
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Git command failed: git {Args}", string.Join(" ", arguments));
            return new GitResult { Success = false, Error = ex.Message };
        }
    }
}
Offline