From ea2781333910225678a317233d3808b3108b87c8 Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 12:12:38 +0300 Subject: [PATCH 1/3] Initial commit with task details for issue #217 Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/linksplatform/Bot/issues/217 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..2b9371b6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/linksplatform/Bot/issues/217 +Your prepared branch: issue-217-210d38bc +Your prepared working directory: /tmp/gh-issue-solver-1757581955949 + +Proceed. \ No newline at end of file From f8d84f80d301f3e3cc915379eb0e043d0bbf6f27 Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 12:12:55 +0300 Subject: [PATCH 2/3] Remove CLAUDE.md - PR created successfully --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 2b9371b6..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/linksplatform/Bot/issues/217 -Your prepared branch: issue-217-210d38bc -Your prepared working directory: /tmp/gh-issue-solver-1757581955949 - -Proceed. \ No newline at end of file From e5c7b40b85f7a5efb36bb1e05d941f043ec7b8b1 Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 12:20:20 +0300 Subject: [PATCH 3/3] Implement GitHub Bot code optimizer using ChatGPT/GPT-4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add OpenAI 2.1.0 package dependency for ChatGPT/GPT-4 integration - Create CodeOptimizerTrigger that detects optimization requests in issues - Implement code analysis and optimization using GPT-4 - Support multiple programming languages (.cs, .js, .ts, .py, .java, .cpp, .c, .h) - Create optimized code PRs with detailed descriptions - Add optional OpenAI API key parameter to bot configuration - Integrate trigger into main issue tracking loop The bot now responds to issues containing keywords like 'optimize', 'optimization', 'improve performance' by analyzing repository code files, sending them to GPT-4 for optimization suggestions, and creating pull requests with the optimized code. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- csharp/Platform.Bot/Platform.Bot.csproj | 1 + csharp/Platform.Bot/Program.cs | 32 +- .../Triggers/CodeOptimizerTrigger.cs | 282 ++++++++++++++++++ 3 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 csharp/Platform.Bot/Triggers/CodeOptimizerTrigger.cs diff --git a/csharp/Platform.Bot/Platform.Bot.csproj b/csharp/Platform.Bot/Platform.Bot.csproj index 2828772d..e9fe7451 100644 --- a/csharp/Platform.Bot/Platform.Bot.csproj +++ b/csharp/Platform.Bot/Platform.Bot.csproj @@ -9,6 +9,7 @@ + diff --git a/csharp/Platform.Bot/Program.cs b/csharp/Platform.Bot/Program.cs index 521a6b95..b67ed4fd 100644 --- a/csharp/Platform.Bot/Program.cs +++ b/csharp/Platform.Bot/Program.cs @@ -73,6 +73,10 @@ private static async Task Main(string[] args) description: "Minimum interaction interval in seconds.", getDefaultValue: () => 60); + var openAiApiKeyOption = new Option( + name: "--openai-api-key", + description: "OpenAI API key for code optimization features."); + var rootCommand = new RootCommand("Sample app for System.CommandLine") { githubUserNameOption, @@ -80,10 +84,11 @@ private static async Task Main(string[] args) githubApplicationNameOption, databaseFilePathOption, fileSetNameOption, - minimumInteractionIntervalOption + minimumInteractionIntervalOption, + openAiApiKeyOption }; - rootCommand.SetHandler(async (githubUserName, githubApiToken, githubApplicationName, databaseFilePath, fileSetName, minimumInteractionInterval) => + rootCommand.SetHandler(async (githubUserName, githubApiToken, githubApplicationName, databaseFilePath, fileSetName, minimumInteractionInterval, openAiApiKey) => { Debug.WriteLine($"Nickname: {githubUserName}"); Debug.WriteLine($"GitHub API Token: {githubApiToken}"); @@ -91,11 +96,30 @@ private static async Task Main(string[] args) Debug.WriteLine($"Database File Path: {databaseFilePath?.FullName}"); Debug.WriteLine($"File Set Name: {fileSetName}"); Debug.WriteLine($"Minimum Interaction Interval: {minimumInteractionInterval} seconds"); + Debug.WriteLine($"OpenAI API Key: {(string.IsNullOrEmpty(openAiApiKey) ? "Not provided" : "Provided")}"); var dbContext = new FileStorage(databaseFilePath?.FullName ?? new TemporaryFile().Filename); Console.WriteLine($"Bot has been started. {Environment.NewLine}Press CTRL+C to close"); var githubStorage = new GitHubStorage(githubUserName, githubApiToken, githubApplicationName); - var issueTracker = new IssueTracker(githubStorage, new HelloWorldTrigger(githubStorage, dbContext, fileSetName), new OrganizationLastMonthActivityTrigger(githubStorage), new LastCommitActivityTrigger(githubStorage), new AdminAuthorIssueTriggerDecorator(new ProtectDefaultBranchTrigger(githubStorage), githubStorage), new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationRepositoriesDefaultBranchTrigger(githubStorage, dbContext), githubStorage), new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationPullRequestsBaseBranchTrigger(githubStorage, dbContext), githubStorage)); + + // Create list of triggers + var triggers = new List> + { + new HelloWorldTrigger(githubStorage, dbContext, fileSetName), + new OrganizationLastMonthActivityTrigger(githubStorage), + new LastCommitActivityTrigger(githubStorage), + new AdminAuthorIssueTriggerDecorator(new ProtectDefaultBranchTrigger(githubStorage), githubStorage), + new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationRepositoriesDefaultBranchTrigger(githubStorage, dbContext), githubStorage), + new AdminAuthorIssueTriggerDecorator(new ChangeOrganizationPullRequestsBaseBranchTrigger(githubStorage, dbContext), githubStorage) + }; + + // Add CodeOptimizerTrigger if OpenAI API key is provided + if (!string.IsNullOrEmpty(openAiApiKey)) + { + triggers.Add(new CodeOptimizerTrigger(githubStorage, dbContext, openAiApiKey)); + } + + var issueTracker = new IssueTracker(githubStorage, triggers.ToArray()); var pullRequenstTracker = new PullRequestTracker(githubStorage, new MergeDependabotBumpsTrigger(githubStorage)); var timestampTracker = new DateTimeTracker(githubStorage, new CreateAndSaveOrganizationRepositoriesMigrationTrigger(githubStorage, dbContext, Path.Combine(Directory.GetCurrentDirectory(), "/github-migrations"))); var cancellation = new CancellationTokenSource(); @@ -114,7 +138,7 @@ private static async Task Main(string[] args) } } }, - githubUserNameOption, githubApiTokenOption, githubApplicationNameOption, databaseFilePathOption, fileSetNameOption, minimumInteractionIntervalOption); + githubUserNameOption, githubApiTokenOption, githubApplicationNameOption, databaseFilePathOption, fileSetNameOption, minimumInteractionIntervalOption, openAiApiKeyOption); return await rootCommand.InvokeAsync(args); } diff --git a/csharp/Platform.Bot/Triggers/CodeOptimizerTrigger.cs b/csharp/Platform.Bot/Triggers/CodeOptimizerTrigger.cs new file mode 100644 index 00000000..9b347ba8 --- /dev/null +++ b/csharp/Platform.Bot/Triggers/CodeOptimizerTrigger.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Interfaces; +using Octokit; +using OpenAI; +using OpenAI.Chat; +using Storage.Local; +using Storage.Remote.GitHub; + +namespace Platform.Bot.Triggers +{ + using TContext = Issue; + + /// + /// + /// Represents the code optimizer trigger that uses ChatGPT/GPT-4 to optimize code. + /// + /// + /// + /// + internal class CodeOptimizerTrigger : ITrigger + { + private readonly GitHubStorage _storage; + private readonly FileStorage _fileStorage; + private readonly OpenAIClient _openAiClient; + private readonly string _model; + + /// + /// + /// Initializes a new instance. + /// + /// + /// + /// + /// A GitHub storage. + /// + /// + /// + /// A file storage. + /// + /// + /// + /// OpenAI API key. + /// + /// + /// + /// The model to use (default: gpt-4). + /// + /// + public CodeOptimizerTrigger(GitHubStorage storage, FileStorage fileStorage, string openAiApiKey, string model = "gpt-4") + { + _storage = storage ?? throw new ArgumentNullException(nameof(storage)); + _fileStorage = fileStorage ?? throw new ArgumentNullException(nameof(fileStorage)); + _openAiClient = new OpenAIClient(openAiApiKey); + _model = model; + } + + /// + /// + /// Determines whether this instance condition. + /// + /// + /// + /// + /// The context. + /// + /// + /// + /// The bool + /// + /// + public async Task Condition(TContext context) + { + var title = context.Title.ToLower(); + var body = context.Body?.ToLower() ?? ""; + + return title.Contains("optimize") || title.Contains("optimization") || + body.Contains("optimize code") || body.Contains("code optimization") || + title.Contains("improve performance") || body.Contains("improve performance"); + } + + /// + /// + /// Actions the context. + /// + /// + /// + /// + /// The context. + /// + /// + public async Task Action(TContext context) + { + try + { + var repository = context.Repository; + var optimizedFiles = new List<(string path, string content)>(); + + // Get all code files from the repository + var codeFiles = await GetCodeFilesFromRepository(repository); + + foreach (var file in codeFiles) + { + var optimizedCode = await OptimizeCodeWithGPT(file.content, file.path); + if (!string.IsNullOrWhiteSpace(optimizedCode) && optimizedCode != file.content) + { + optimizedFiles.Add((file.path, optimizedCode)); + } + } + + // Create a new branch for optimizations + var branchName = $"optimize-code-{context.Number}"; + var defaultBranch = repository.DefaultBranch; + + // Create branch and commit optimized files + if (optimizedFiles.Any()) + { + // Create branch reference + var defaultBranchRef = await _storage.GetBranch(repository.Id, defaultBranch); + var newReference = new NewReference($"refs/heads/{branchName}", defaultBranchRef.Commit.Sha); + await _storage.CreateReference(repository.Id, newReference); + + foreach (var file in optimizedFiles) + { + await _storage.CreateOrUpdateFile(file.content, repository, branchName, file.path, + $"Optimize code in {file.path} using GPT-4"); + } + + // Create pull request using Octokit client directly + var newPr = new NewPullRequest( + $"Code optimization for issue #{context.Number}", + branchName, + defaultBranch) + { + Body = $"This PR contains code optimizations generated by GPT-4 for issue #{context.Number}.\n\n" + + $"Files optimized:\n{string.Join("\n", optimizedFiles.Select(f => $"- {f.path}"))}" + }; + + await _storage.Client.PullRequest.Create(repository.Id, newPr); + + // Add comment to the issue + var comment = $"I've analyzed the code and created optimizations using GPT-4. " + + $"Please check the pull request with the optimized code: {branchName}"; + await _storage.CreateIssueComment(repository.Id, context.Number, comment); + } + else + { + await _storage.CreateIssueComment(repository.Id, context.Number, + "I've analyzed the code but couldn't find any significant optimizations to suggest at this time."); + } + } + catch (Exception ex) + { + await _storage.CreateIssueComment(context.Repository.Id, context.Number, + $"An error occurred while optimizing the code: {ex.Message}"); + } + } + + private async Task> GetCodeFilesFromRepository(Repository repository) + { + var files = new List<(string path, string content)>(); + var supportedExtensions = new[] { ".cs", ".js", ".ts", ".py", ".java", ".cpp", ".c", ".h" }; + + try + { + // Get all contents from repository using Octokit client directly + var contents = await _storage.Client.Repository.Content.GetAllContents(repository.Id); + + foreach (var content in contents.Where(c => c.Type == ContentType.File)) + { + var extension = Path.GetExtension(content.Name); + if (supportedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) + { + // Get file content + var fileContents = await _storage.Client.Repository.Content.GetAllContentsByRef(repository.Id, content.Path, repository.DefaultBranch); + var fileContent = fileContents.FirstOrDefault()?.Content; + + if (!string.IsNullOrWhiteSpace(fileContent)) + { + // Decode base64 content + var decodedContent = Encoding.UTF8.GetString(Convert.FromBase64String(fileContent)); + files.Add((content.Path, decodedContent)); + } + } + } + } + catch (Exception ex) + { + // Log error but continue + Console.WriteLine($"Error getting repository contents: {ex.Message}"); + } + + return files; + } + + private async Task OptimizeCodeWithGPT(string code, string filePath) + { + try + { + var fileExtension = Path.GetExtension(filePath); + var language = GetLanguageFromExtension(fileExtension); + + var systemPrompt = $"You are a code optimization expert. Analyze the provided {language} code and suggest optimizations for:" + + "\n1. Performance improvements" + + "\n2. Memory usage optimization" + + "\n3. Code readability and maintainability" + + "\n4. Best practices compliance" + + "\n5. Algorithmic improvements" + + "\n\nProvide only the optimized code without explanations. If no significant optimizations are possible, return the original code."; + + var userPrompt = $"Optimize this {language} code:\n\n```{language}\n{code}\n```"; + + var chatClient = _openAiClient.GetChatClient(_model); + var messages = new List + { + ChatMessage.CreateSystemMessage(systemPrompt), + ChatMessage.CreateUserMessage(userPrompt) + }; + + var response = await chatClient.CompleteChatAsync(messages); + var optimizedCode = response.Value.Content[0].Text; + + // Clean up the response - remove markdown code blocks if present + optimizedCode = CleanCodeResponse(optimizedCode); + + return optimizedCode; + } + catch (Exception ex) + { + Console.WriteLine($"Error optimizing code with GPT: {ex.Message}"); + return code; // Return original code if optimization fails + } + } + + private string GetLanguageFromExtension(string extension) + { + return extension.ToLower() switch + { + ".cs" => "C#", + ".js" => "JavaScript", + ".ts" => "TypeScript", + ".py" => "Python", + ".java" => "Java", + ".cpp" => "C++", + ".c" => "C", + ".h" => "C/C++ Header", + _ => "code" + }; + } + + private string CleanCodeResponse(string response) + { + if (string.IsNullOrWhiteSpace(response)) + return response; + + // Remove markdown code blocks + var lines = response.Split('\n'); + var codeLines = new List(); + bool inCodeBlock = false; + + foreach (var line in lines) + { + if (line.StartsWith("```")) + { + inCodeBlock = !inCodeBlock; + continue; + } + + if (inCodeBlock || !response.Contains("```")) + { + codeLines.Add(line); + } + } + + return string.Join('\n', codeLines).Trim(); + } + } +} \ No newline at end of file