Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Models/AppConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ public class AppConfiguration

[JsonPropertyName("default_topp")]
public int? DefaultTopP { get; set; }

[JsonPropertyName("use_azure_openai")]
public bool UseAzureOpenAI { get; set; }
}
7 changes: 4 additions & 3 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ static async Task<int> Main(string[] args)

var setupOption = new Option<bool>(
"--setup",
"Configure OpenAI API key"
"Configure OpenAI or Azure OpenAI settings"
);

var rootCommand = new RootCommand("Generate AI-powered commit messages using OpenAI")
var rootCommand = new RootCommand("Generate AI-powered commit messages using OpenAI or Azure OpenAI")
{
dryRunOption,
verboseOption,
Expand Down Expand Up @@ -129,7 +129,8 @@ static async Task GenerateCommitMessage(
var endpoint = await configService.GetOpenAiEndpointAsync() ?? "https://api.openai.com/v1";
var defaultModel = await configService.GetDefaultModelAsync() ?? "gpt-4o-mini";

var openAiService = new OpenAIService(apiKey, endpoint);
var useAzure = await configService.UseAzureOpenAIAsync();
var openAiService = new OpenAIService(apiKey, endpoint, useAzure);

// Check if we're in a git repository
if (!Directory.Exists(".git") && !await gitService.IsInGitRepositoryAsync())
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# WriteCommit

A cross-platform .NET tool that generates AI-powered commit messages using OpenAI's GPT models.
A cross-platform .NET tool that generates AI-powered commit messages using OpenAI or Azure OpenAI.

## ✨ Features

Expand All @@ -9,7 +9,7 @@ A cross-platform .NET tool that generates AI-powered commit messages using OpenA
- 🎛️ **Highly configurable** - Adjust AI parameters to your preference
- 🧪 **Dry-run mode** - Preview generated messages without committing
- 📝 **Verbose output** - Detailed logging for debugging and transparency
- ⚡ **Fast and lightweight** - Direct OpenAI API integration for quick responses
- ⚡ **Fast and lightweight** - Direct OpenAI or Azure OpenAI integration for quick responses
- 📋 **Smart chunking** - Handles large diffs by intelligently splitting them into semantic chunks
- 🔍 **Context-aware** - Adds surrounding code lines when diffs are very small for better summaries

Expand All @@ -18,7 +18,7 @@ A cross-platform .NET tool that generates AI-powered commit messages using OpenA
### Prerequisites

- [.NET 8.0 or later](https://dotnet.microsoft.com/download)
- OpenAI API key (optional, required only if your endpoint needs authentication)
- OpenAI or Azure OpenAI API key (optional, required only if your endpoint needs authentication)
- Git repository with staged changes

### Installation
Expand Down Expand Up @@ -101,13 +101,13 @@ WriteCommit --dry-run --verbose --temperature 0.5
| `--model` | from setup | OpenAI model to use |
| `--presence` | `0` | Presence penalty (-2 to 2) |
| `--frequency` | `0` | Frequency penalty (-2 to 2) |
| `--setup` | `false` | Configure OpenAI settings |
| `--setup` | `false` | Configure OpenAI or Azure OpenAI settings |

## 🔧 How It Works

1. **Validates environment** - Checks for git repository and OpenAI settings
2. **Analyzes changes** - Processes your staged git diff using semantic chunking
3. **Generates message** - Uses OpenAI API to create meaningful commit message
3. **Generates message** - Uses OpenAI or Azure OpenAI to create meaningful commit message
4. **Commits changes** - Applies the generated message (unless `--dry-run`)

## 🔑 Configuration
Expand All @@ -121,7 +121,7 @@ WriteCommit --dry-run --verbose --temperature 0.5
WriteCommit --setup
```

This will prompt you to enter your API key (if needed), API endpoint, and default model, then save them to `~/.writecommit/config.json`.
This will prompt you to enter your API key (if needed), choose between OpenAI or Azure OpenAI, specify the endpoint, and default model/deployment. If you select Azure but leave the endpoint blank, WriteCommit will fall back to the standard OpenAI endpoint. The values are saved to `~/.writecommit/config.json`.

**Option 2: Using Environment Variables**

Expand Down
68 changes: 57 additions & 11 deletions Services/ConfigurationService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Text.Json;
using OpenAI.Chat;
using Azure.AI.OpenAI;
using System.ClientModel;
using WriteCommit.Constants;
using WriteCommit.Models;

Expand Down Expand Up @@ -94,6 +96,15 @@
return config?.DefaultModel;
}

/// <summary>
/// Returns true if configuration specifies Azure OpenAI usage
/// </summary>
public async Task<bool> UseAzureOpenAIAsync()
{
var config = await LoadConfigurationAsync();
return config?.UseAzureOpenAI ?? false;
}

/// <summary>
/// Prompts user to enter and save their OpenAI API key
/// </summary>
Expand All @@ -102,8 +113,8 @@
Console.WriteLine("WriteCommit Setup");
Console.WriteLine("=================");
Console.WriteLine();
Console.WriteLine("Please enter your OpenAI API key.");
Console.WriteLine("You can get one from: https://platform.openai.com/api-keys");
Console.WriteLine("Please enter your OpenAI API key (or Azure OpenAI key).");
Console.WriteLine("You can get one from: https://platform.openai.com/api-keys or your Azure portal");
Console.WriteLine();
Console.Write("API Key (leave blank if not required): ");

Expand All @@ -116,14 +127,38 @@
apiKey = null;
}

// Prompt for endpoint and model
Console.Write($"Endpoint (default: https://api.openai.com/v1): ");
// Ask if using Azure OpenAI
Console.Write("Use Azure OpenAI service? (y/N): ");
var azureInput = Console.ReadLine()?.Trim().ToLowerInvariant();
bool useAzure = azureInput == "y" || azureInput == "yes";

// Prompt for endpoint and model/deployment
Console.Write(
useAzure
? "Azure endpoint (leave blank to use OpenAI): "
: "Endpoint (default: https://api.openai.com/v1): "
);
var endpointInput = Console.ReadLine()?.Trim();
var endpoint = string.IsNullOrWhiteSpace(endpointInput)
? "https://api.openai.com/v1"
: endpointInput;
string endpoint;
if (string.IsNullOrWhiteSpace(endpointInput))
{
endpoint = "https://api.openai.com/v1";
// If no Azure endpoint provided, fall back to OpenAI
if (useAzure)
{
useAzure = false;
}
}
else
{
endpoint = endpointInput;
}

Console.Write($"Default model (default: gpt-4o-mini): ");
Console.Write(
useAzure
? "Deployment name (default: gpt-4o-mini): "
: "Default model (default: gpt-4o-mini): "
);
var modelInput = Console.ReadLine()?.Trim();
var model = string.IsNullOrWhiteSpace(modelInput) ? "gpt-4o-mini" : modelInput;

Expand All @@ -132,6 +167,7 @@
config.OpenAiApiKey = apiKey;
config.OpenAiEndpoint = endpoint;
config.DefaultModel = model;
config.UseAzureOpenAI = useAzure;

// Save configuration
await SaveConfigurationAsync(config);
Expand All @@ -151,7 +187,7 @@

if ((testResponse == "y" || testResponse == "yes") && !string.IsNullOrEmpty(apiKey))
{
return await TestApiKeyAsync(apiKey, verbose);
return await TestApiKeyAsync(apiKey, useAzure, endpoint, model, verbose);
}

return true;
Expand All @@ -160,13 +196,23 @@
/// <summary>
/// Tests if the API key is valid by making a simple request
/// </summary>
private async Task<bool> TestApiKeyAsync(string? apiKey, bool verbose)
private async Task<bool> TestApiKeyAsync(string? apiKey, bool useAzure, string endpoint, string model, bool verbose)
{
Console.WriteLine("Testing API key...");

try
{
var testClient = new ChatClient("gpt-4o-mini", apiKey);
ChatClient testClient;
if (useAzure)
{
var azureClient = new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(endpoint), new ApiKeyCredential(apiKey));

Check warning on line 208 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Test WriteCommit

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 208 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Test WriteCommit

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 208 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (ubuntu-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 208 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (ubuntu-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 208 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (windows-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 208 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (windows-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 208 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (macos-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 208 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (macos-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.
Copy link

Copilot AI Jul 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line has formatting issues with inconsistent spacing and alignment. Run dotnet csharpier format Services/ConfigurationService.cs to fix the formatting.

Copilot generated this review using guidance from repository custom instructions.
testClient = azureClient.GetChatClient(model);
}
else
{
var options = new OpenAI.OpenAIClientOptions { Endpoint = new Uri(endpoint) };
testClient = new ChatClient(model, new ApiKeyCredential(apiKey), options);

Check warning on line 214 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Test WriteCommit

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 214 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Test WriteCommit

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 214 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (ubuntu-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 214 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (ubuntu-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 214 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (windows-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 214 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (windows-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 214 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (macos-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 214 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (macos-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.
}
var messages = new List<ChatMessage>
{
new SystemChatMessage("You are a helpful assistant."),
Expand Down
29 changes: 18 additions & 11 deletions Services/OpenAIService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Text;
using OpenAI.Chat;
using System.ClientModel;
using Azure.AI.OpenAI;
using WriteCommit.Constants;
using WriteCommit.Models;

Expand All @@ -10,10 +11,11 @@ public class OpenAIService
{
private readonly string _apiKey;
private readonly string _endpoint;
private readonly bool _useAzure;
private readonly string _patternsDirectory;
private const int MaxContextTokens = 128000;

public OpenAIService(string apiKey, string? endpoint = null)
public OpenAIService(string apiKey, string? endpoint = null, bool useAzure = false)
{
if (string.IsNullOrEmpty(apiKey))
{
Expand All @@ -24,9 +26,22 @@ public OpenAIService(string apiKey, string? endpoint = null)
_endpoint = string.IsNullOrWhiteSpace(endpoint)
? "https://api.openai.com/v1"
: endpoint;
_useAzure = useAzure;
_patternsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "patterns");
}

private ChatClient CreateChatClient(string model)
{
if (_useAzure)
{
var azureClient = new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(_endpoint), new ApiKeyCredential(_apiKey));
Copy link

Copilot AI Jul 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line has formatting issues with inconsistent spacing and alignment. Run dotnet csharpier format Services/OpenAIService.cs to fix the formatting.

Copilot generated this review using guidance from repository custom instructions.
return azureClient.GetChatClient(model);
}

var options = new OpenAI.OpenAIClientOptions { Endpoint = new Uri(_endpoint) };
return new ChatClient(model, new ApiKeyCredential(_apiKey), options);
}

public async Task<string> GenerateCommitMessageAsync(
List<DiffChunk> chunks,
string pattern,
Expand Down Expand Up @@ -155,11 +170,7 @@ bool verbose
}

// Create a client for this specific model
var clientOptions = new OpenAI.OpenAIClientOptions
{
Endpoint = new Uri(_endpoint)
};
var chatClient = new ChatClient(model, new ApiKeyCredential(_apiKey), clientOptions);
var chatClient = CreateChatClient(model);

// Create the chat messages
var messages = new List<ChatMessage>
Expand Down Expand Up @@ -276,11 +287,7 @@ bool verbose
}

// Create a client for this specific model
var clientOptions = new OpenAI.OpenAIClientOptions
{
Endpoint = new Uri(_endpoint)
};
var chatClient = new ChatClient(model, new ApiKeyCredential(_apiKey), clientOptions);
var chatClient = CreateChatClient(model);

// Create the chat messages
var messages = new List<ChatMessage>
Expand Down
1 change: 1 addition & 0 deletions WriteCommit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
Expand Down
Loading