Skip to content

Commit 31fc4d9

Browse files
committed
feat: improve Gemini security and implement smart git workflow
- Update Gemini provider to use x-goog-api-key header instead of URL parameter for better security - Add latest Gemini 2.x and OpenAI model options (gemini-2.5-flash, gpt-4o, o1-mini) - Remove includeUnstaged setting and implement smart fallback logic - Auto-analyze staged files first, fallback to unstaged if no staged changes - Update all providers with consistent code formatting - Fix Gemini default model to gemini-2.5-flash
1 parent 816cfab commit 31fc4d9

16 files changed

+1020
-969
lines changed

package.json

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,45 +148,39 @@
148148
"description": "Language for generated commit messages",
149149
"order": 3
150150
},
151-
"universal-commit-assistant.includeUnstaged": {
152-
"type": "boolean",
153-
"default": true,
154-
"description": "Include unstaged changes when generating commit messages",
155-
"order": 4
156-
},
157151
"universal-commit-assistant.customPrompt": {
158152
"type": "string",
159153
"default": "",
160154
"description": "Custom prompt template for commit message generation (used when messageStyle is 'custom')",
161-
"order": 5
155+
"order": 4
162156
},
163157
"universal-commit-assistant.systemPrompt": {
164158
"type": "string",
165159
"default": "You are a git commit message generator. Return ONLY the commit message with no additional text, explanations, or prefixes.",
166160
"description": "Custom system prompt for AI providers",
167-
"order": 6
161+
"order": 5
168162
},
169163
"universal-commit-assistant.temperature": {
170164
"type": "number",
171165
"default": 0.3,
172166
"minimum": 0,
173167
"maximum": 2,
174168
"description": "Temperature for AI responses (0 = deterministic, 2 = very creative)",
175-
"order": 7
169+
"order": 6
176170
},
177171
"universal-commit-assistant.maxTokens": {
178172
"type": "number",
179173
"default": 200,
180174
"minimum": 100,
181175
"maximum": 500,
182176
"description": "Maximum tokens for generated commit messages",
183-
"order": 8
177+
"order": 7
184178
},
185179
"universal-commit-assistant.enableAnalytics": {
186180
"type": "boolean",
187181
"default": false,
188182
"description": "Enable usage analytics (no sensitive data is collected)",
189-
"order": 9
183+
"order": 8
190184
}
191185
}
192186
},
@@ -196,6 +190,18 @@
196190
"universal-commit-assistant.openai.model": {
197191
"type": "string",
198192
"default": "gpt-4o-mini",
193+
"enum": [
194+
"gpt-4o-mini",
195+
"gpt-4o",
196+
"o1-mini",
197+
"o1-preview"
198+
],
199+
"enumDescriptions": [
200+
"GPT-4o Mini - Fast and cost-effective (recommended)",
201+
"GPT-4o - Full capability model",
202+
"o1 Mini - Reasoning model for complex logic",
203+
"o1 Preview - Advanced reasoning model"
204+
],
199205
"description": "OpenAI model to use"
200206
},
201207
"universal-commit-assistant.anthropic.model": {
@@ -205,7 +211,17 @@
205211
},
206212
"universal-commit-assistant.gemini.model": {
207213
"type": "string",
208-
"default": "gemini-1.5-flash",
214+
"default": "gemini-2.5-flash",
215+
"enum": [
216+
"gemini-2.5-flash",
217+
"gemini-2.5-pro",
218+
"gemini-2.0-flash"
219+
],
220+
"enumDescriptions": [
221+
"Gemini 2.5 Flash - Latest stable model (recommended)",
222+
"Gemini 2.5 Pro - Advanced reasoning with thinking mode",
223+
"Gemini 2.0 Flash - Newest with 1M context window"
224+
],
209225
"description": "Gemini model to use"
210226
},
211227
"universal-commit-assistant.mistral.model": {

src/providers/anthropicProvider.ts

Lines changed: 79 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,91 @@
1-
import axios from 'axios';
2-
import * as vscode from 'vscode';
3-
import { BaseProvider } from './baseProvider';
4-
import { GenerationOptions } from '../types';
5-
import { ConfigurationManager } from '../utils/configurationManager';
1+
import * as vscode from "vscode";
2+
3+
import { BaseProvider } from "./baseProvider";
4+
import { ConfigurationManager } from "../utils/configurationManager";
5+
import { GenerationOptions } from "../types";
6+
import axios from "axios";
67

78
export class AnthropicProvider extends BaseProvider {
8-
private readonly API_KEY_SECRET = 'universal-commit-assistant.anthropic.apiKey';
9+
private readonly API_KEY_SECRET = "universal-commit-assistant.anthropic.apiKey";
910

10-
constructor(
11-
private readonly configManager: ConfigurationManager,
12-
private readonly secretStorage: vscode.SecretStorage
13-
) {
14-
super();
15-
}
11+
constructor(
12+
private readonly configManager: ConfigurationManager,
13+
private readonly secretStorage: vscode.SecretStorage
14+
) {
15+
super();
16+
}
1617

17-
async generateCommitMessage(changes: string, options?: GenerationOptions): Promise<string> {
18-
const apiKey = await this.getApiKey();
19-
const model = this.configManager.getAnthropicModel();
20-
const temperature = this.configManager.getTemperature();
21-
const systemPrompt = this.configManager.getSystemPrompt();
22-
const style = options?.style || this.configManager.getMessageStyle();
23-
const language = this.configManager.getLanguage();
24-
const maxTokens = options?.maxTokens || (style === 'detailed' ? 300 : this.configManager.getMaxTokens());
18+
async generateCommitMessage(changes: string, options?: GenerationOptions): Promise<string> {
19+
const apiKey = await this.getApiKey();
20+
const model = this.configManager.getAnthropicModel();
21+
const temperature = this.configManager.getTemperature();
22+
const systemPrompt = this.configManager.getSystemPrompt();
23+
const style = options?.style || this.configManager.getMessageStyle();
24+
const language = this.configManager.getLanguage();
25+
const maxTokens = options?.maxTokens || (style === "detailed" ? 300 : this.configManager.getMaxTokens());
2526

26-
const prompt = this.buildPrompt(changes, style, options?.customPrompt, language);
27+
const prompt = this.buildPrompt(changes, style, options?.customPrompt, language);
2728

28-
try {
29-
const response = await axios.post(
30-
'https://api.anthropic.com/v1/messages',
31-
{
32-
model,
33-
max_tokens: maxTokens,
34-
temperature: temperature,
35-
system: systemPrompt,
36-
messages: [
37-
{
38-
role: 'user',
39-
content: prompt
40-
}
41-
]
42-
},
43-
{
44-
headers: {
45-
'x-api-key': apiKey,
46-
'Content-Type': 'application/json',
47-
'anthropic-version': '2023-06-01'
48-
}
49-
}
50-
);
29+
try {
30+
const response = await axios.post(
31+
"https://api.anthropic.com/v1/messages",
32+
{
33+
model,
34+
max_tokens: maxTokens,
35+
temperature: temperature,
36+
system: systemPrompt,
37+
messages: [
38+
{
39+
role: "user",
40+
content: prompt,
41+
},
42+
],
43+
},
44+
{
45+
headers: {
46+
"x-api-key": apiKey,
47+
"Content-Type": "application/json",
48+
"anthropic-version": "2023-06-01",
49+
},
50+
}
51+
);
5152

52-
const message = response.data.content[0]?.text;
53-
if (!message) {
54-
throw new Error('No response from Anthropic');
55-
}
53+
const message = response.data.content[0]?.text;
54+
if (!message) {
55+
throw new Error("No response from Anthropic");
56+
}
5657

57-
return this.validateResponse(message, style);
58-
} catch (error) {
59-
if (axios.isAxiosError(error)) {
60-
throw new Error(`Anthropic API error: ${error.response?.data?.error?.message || error.message}`);
61-
}
62-
throw error;
63-
}
58+
return this.validateResponse(message, style);
59+
} catch (error) {
60+
if (axios.isAxiosError(error)) {
61+
throw new Error(`Anthropic API error: ${error.response?.data?.error?.message || error.message}`);
62+
}
63+
throw error;
6464
}
65+
}
6566

66-
async validateConfiguration(): Promise<boolean> {
67-
const apiKey = await this.getApiKey();
68-
return !!apiKey;
69-
}
67+
async validateConfiguration(): Promise<boolean> {
68+
const apiKey = await this.getApiKey();
69+
return !!apiKey;
70+
}
7071

71-
private async getApiKey(): Promise<string> {
72-
let apiKey = await this.secretStorage.get(this.API_KEY_SECRET);
73-
74-
if (!apiKey) {
75-
apiKey = await vscode.window.showInputBox({
76-
prompt: 'Enter your Anthropic API key',
77-
password: true,
78-
placeHolder: 'sk-ant-...'
79-
});
80-
81-
if (!apiKey) {
82-
throw new Error('Anthropic API key is required');
83-
}
84-
85-
await this.secretStorage.store(this.API_KEY_SECRET, apiKey);
86-
}
87-
88-
return apiKey;
72+
private async getApiKey(): Promise<string> {
73+
let apiKey = await this.secretStorage.get(this.API_KEY_SECRET);
74+
75+
if (!apiKey) {
76+
apiKey = await vscode.window.showInputBox({
77+
prompt: "Enter your Anthropic API key",
78+
password: true,
79+
placeHolder: "sk-ant-...",
80+
});
81+
82+
if (!apiKey) {
83+
throw new Error("Anthropic API key is required");
84+
}
85+
86+
await this.secretStorage.store(this.API_KEY_SECRET, apiKey);
8987
}
90-
}
88+
89+
return apiKey;
90+
}
91+
}

src/providers/baseProvider.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,10 @@ This change improves security and provides users with convenient social login op
5858
return "Respond in English. Use imperative mood like 'add', 'fix', 'update'.";
5959
}
6060

61-
const styleNote = (style === "conventional" || style === "detailed")
62-
? " Keep type prefixes (feat, fix, docs, etc.) in English for tooling compatibility, but translate the description."
63-
: "";
61+
const styleNote =
62+
style === "conventional" || style === "detailed"
63+
? " Keep type prefixes (feat, fix, docs, etc.) in English for tooling compatibility, but translate the description."
64+
: "";
6465

6566
switch (language) {
6667
case "spanish":

0 commit comments

Comments
 (0)