Skip to content
Open
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 src/commands.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { generateCommitMsg } from './generate-commit-msg';
import { ConfigurationManager } from './config';
import { Logger } from './logger';

/**
* Manages the registration and disposal of commands.
Expand Down Expand Up @@ -55,8 +56,10 @@ export class CommandManager {
private registerCommand(command: string, handler: (...args: any[]) => any) {
const disposable = vscode.commands.registerCommand(command, async (...args) => {
try {
Logger.info(`Executing command: ${command}`);
await handler(...args);
} catch (error) {
Logger.error(`Command '${command}' failed:`, error);
const result = await vscode.window.showErrorMessage(
`Failed: ${error.message}`,
'Retry',
Expand Down
3 changes: 2 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { createOpenAIApi } from './openai-utils';
import { createGeminiAPIClient } from './gemini-utils';
import { Logger } from './logger';

/**
* Configuration keys used in the AI commit extension.
Expand Down Expand Up @@ -91,7 +92,7 @@ export class ConfigurationManager {
await config.update('OPENAI_MODEL', 'gpt-4', vscode.ConfigurationTarget.Global);
}
} catch (error) {
console.error('Failed to fetch OpenAI models:', error);
Logger.error('Failed to fetch OpenAI models:', error);
}
}

Expand Down
9 changes: 8 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { CommandManager } from './commands';
import { ConfigurationManager } from './config';
import { Logger } from './logger';

/**
* Activates the extension and registers commands.
Expand All @@ -9,6 +10,9 @@ import { ConfigurationManager } from './config';
*/
export async function activate(context: vscode.ExtensionContext) {
try {
Logger.initialize();
Logger.info('Activating AI Commit extension...');

const configManager = ConfigurationManager.getInstance(context);

const commandManager = new CommandManager(context);
Expand All @@ -18,9 +22,12 @@ export async function activate(context: vscode.ExtensionContext) {
dispose: () => {
configManager.dispose();
commandManager.dispose();
Logger.dispose();
}
});

Logger.info('AI Commit extension activated successfully');

const apiKey = configManager.getConfig<string>('OPENAI_API_KEY');
if (!apiKey) {
const result = await vscode.window.showWarningMessage(
Expand All @@ -37,7 +44,7 @@ export async function activate(context: vscode.ExtensionContext) {
}
}
} catch (error) {
console.error('Failed to activate extension:', error);
Logger.error('Failed to activate extension:', error);
throw error;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/gemini-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { GoogleGenerativeAI } from "@google/generative-ai";
import { ConfigKeys, ConfigurationManager } from './config';
import { Logger } from './logger';

/**
* Creates and returns a Gemini API configuration object.
Expand Down Expand Up @@ -58,7 +59,7 @@ export async function GeminiAPI(messages: any[]) {
return text;

} catch (error) {
console.error('Gemini API call failed:', error);
Logger.error('Gemini API call failed:', error);
throw error;
}
}
41 changes: 25 additions & 16 deletions src/generate-commit-msg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ChatGPTAPI } from './openai-utils';
import { getMainCommitPrompt } from './prompts';
import { ProgressHandler } from './utils';
import { GeminiAPI } from './gemini-utils';
import { Logger } from './logger';

/**
* Generates a chat completion prompt for the commit message based on the provided diff.
Expand Down Expand Up @@ -74,6 +75,7 @@ export async function generateCommitMsg(arg) {
const repo = await getRepo(arg);

const aiProvider = configManager.getConfig<string>(ConfigKeys.AI_PROVIDER, 'openai');
Logger.info(`Using AI provider: ${aiProvider}`);

progress.report({ message: 'Getting staged changes...' });
const { diff, error } = await getDiffStaged(repo);
Expand Down Expand Up @@ -127,27 +129,33 @@ export async function generateCommitMsg(arg) {


if (commitMessage) {
Logger.info('Commit message generated successfully');
scmInputBox.value = commitMessage;
} else {
throw new Error('Failed to generate commit message');
}
} catch (err) {
let errorMessage = 'An unexpected error occurred';

if (aiProvider === 'openai' && err.response?.status) {
switch (err.response.status) {
case 401:
errorMessage = 'Invalid OpenAI API key or unauthorized access';
break;
case 429:
errorMessage = 'Rate limit exceeded. Please try again later';
break;
case 500:
errorMessage = 'OpenAI server error. Please try again later';
break;
case 503:
errorMessage = 'OpenAI service is temporarily unavailable';
break;
Logger.error(`${aiProvider} API call failed:`, err);

let errorMessage = err.message || 'An unexpected error occurred';

if (aiProvider === 'openai') {
const status = err.response?.status || err.status;
if (status) {
switch (status) {
case 401:
errorMessage = 'Invalid OpenAI API key or unauthorized access';
break;
case 429:
errorMessage = 'Rate limit exceeded. Please try again later';
break;
case 500:
errorMessage = 'OpenAI server error. Please try again later';
break;
case 503:
errorMessage = 'OpenAI service is temporarily unavailable';
break;
}
}
} else if (aiProvider === 'gemini') {
errorMessage = `Gemini API error: ${err.message}`;
Expand All @@ -156,6 +164,7 @@ export async function generateCommitMsg(arg) {
throw new Error(errorMessage);
}
} catch (error) {
Logger.error('Failed to generate commit message:', error);
throw error;
}
});
Expand Down
3 changes: 2 additions & 1 deletion src/git-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import simpleGit from 'simple-git';
import * as vscode from 'vscode';
import { Logger } from './logger';

/**
* Retrieves the staged changes from the Git repository.
Expand All @@ -23,7 +24,7 @@ export async function getDiffStaged(
error: null
};
} catch (error) {
console.error('Error reading Git diff:', error);
Logger.error('Error reading Git diff:', error);
return { diff: '', error: error.message };
}
}
52 changes: 52 additions & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as vscode from 'vscode';

class Logger {
private static outputChannel: vscode.OutputChannel;

static initialize() {
this.outputChannel = vscode.window.createOutputChannel('AI Commit');
}

static info(message: string, ...args: any[]) {
this.log('INFO', message, ...args);
}

static warn(message: string, ...args: any[]) {
this.log('WARN', message, ...args);
}

static error(message: string, ...args: any[]) {
this.log('ERROR', message, ...args);
}

private static log(level: string, message: string, ...args: any[]) {
const timestamp = new Date().toISOString();
const formattedMessage = `[${timestamp}] [${level}] ${message}`;

if (args.length > 0) {
this.outputChannel.appendLine(`${formattedMessage} ${args.map(a => this.formatArg(a)).join(' ')}`);
} else {
this.outputChannel.appendLine(formattedMessage);
}
}

private static formatArg(a: any): string {
if (a instanceof Error) {
return `${a.message}\n${a.stack || ''}`;
}
if (typeof a === 'object') {
return JSON.stringify(a, null, 2);
}
return String(a);
}

static show() {
this.outputChannel?.show(true);
}

static dispose() {
this.outputChannel?.dispose();
}
}

export { Logger };