incize
Version:
AI Commit Copilot for Power Developers
319 lines (318 loc) โข 13.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommitMessageGenerator = void 0;
const RealAIService_1 = require("./RealAIService");
class CommitMessageGenerator {
aiService;
constructor() {
this.aiService = new RealAIService_1.RealAIService();
}
/**
* Generate commit message suggestions based on staged changes
*/
async generateSuggestions(diff, commit, options = {}) {
try {
// Use AI to analyze the changes and generate contextual suggestions
const analysis = await this.aiService.analyze(diff, commit, 'local', {
model: 'claude-3-5-sonnet',
focus: 'quality'
});
return this.generateContextualSuggestions(diff, analysis, options);
}
catch (error) {
// Fallback to intelligent suggestions based on file analysis
return this.generateIntelligentFallbackSuggestions(diff, options);
}
}
/**
* Generate contextual suggestions based on AI analysis
*/
generateContextualSuggestions(diff, analysis, options) {
const { conventional = true, emoji = false } = options;
const suggestions = [];
// Analyze the staged changes
const fileTypes = this.analyzeFileTypes(diff);
const changeTypes = this.analyzeChangeTypes(diff);
const aiKeywords = this.extractContentKeywords(analysis);
const stagedKeywords = this.analyzeStagedChanges(diff);
// Combine keywords from both sources
const allKeywords = [...new Set([...aiKeywords, ...stagedKeywords])];
// Generate primary suggestion based on most significant change
const primarySuggestion = this.generatePrimarySuggestion(fileTypes, changeTypes, allKeywords, conventional);
suggestions.push(primarySuggestion);
// Generate secondary suggestions
const secondarySuggestions = this.generateSecondarySuggestions(fileTypes, changeTypes, allKeywords, conventional);
suggestions.push(...secondarySuggestions);
// Add emojis if requested
if (emoji) {
return suggestions.map(s => this.addEmoji(s));
}
return suggestions.slice(0, 3);
}
/**
* Analyze file types in the staged changes
*/
analyzeFileTypes(diff) {
const fileTypes = {};
diff.files.forEach(file => {
const extension = file.path.split('.').pop()?.toLowerCase() || 'unknown';
fileTypes[extension] = (fileTypes[extension] || 0) + 1;
});
return fileTypes;
}
/**
* Analyze types of changes (additions, deletions, modifications)
*/
analyzeChangeTypes(diff) {
let additions = 0;
let deletions = 0;
let modifications = 0;
diff.files.forEach(file => {
if (file.status === 'added')
additions++;
else if (file.status === 'deleted')
deletions++;
else
modifications++;
});
return { additions, deletions, modifications };
}
/**
* Extract keywords from AI analysis and diff content
*/
extractContentKeywords(analysis) {
const keywords = [];
if (analysis.summary) {
const summary = analysis.summary.toLowerCase();
// Extract key terms from AI analysis
if (summary.includes('auth') || summary.includes('authentication'))
keywords.push('auth');
if (summary.includes('security'))
keywords.push('security');
if (summary.includes('performance'))
keywords.push('performance');
if (summary.includes('test'))
keywords.push('test');
if (summary.includes('docs') || summary.includes('documentation'))
keywords.push('docs');
if (summary.includes('refactor'))
keywords.push('refactor');
if (summary.includes('fix') || summary.includes('bug'))
keywords.push('fix');
if (summary.includes('feature') || summary.includes('add'))
keywords.push('feat');
if (summary.includes('jwt') || summary.includes('token'))
keywords.push('auth');
if (summary.includes('middleware'))
keywords.push('auth');
if (summary.includes('password'))
keywords.push('security');
}
return keywords;
}
/**
* Analyze staged changes for better context
*/
analyzeStagedChanges(diff) {
const keywords = [];
// Analyze file names and paths
diff.files.forEach(file => {
const fileName = file.path.toLowerCase();
if (fileName.includes('auth'))
keywords.push('auth');
if (fileName.includes('test') || fileName.includes('spec'))
keywords.push('test');
if (fileName.includes('middleware'))
keywords.push('auth');
if (fileName.includes('security'))
keywords.push('security');
if (fileName.includes('config'))
keywords.push('config');
if (fileName.includes('docs') || fileName.includes('readme'))
keywords.push('docs');
if (fileName.includes('style') || fileName.includes('css'))
keywords.push('style');
});
return keywords;
}
/**
* Generate primary suggestion based on most significant change
*/
generatePrimarySuggestion(fileTypes, changeTypes, keywords, conventional) {
// Determine the type of change
let changeType = 'feat';
if (changeTypes.deletions > changeTypes.additions)
changeType = 'fix';
if (keywords.includes('refactor'))
changeType = 'refactor';
if (keywords.includes('test'))
changeType = 'test';
if (keywords.includes('docs'))
changeType = 'docs';
if (keywords.includes('security'))
changeType = 'fix';
if (keywords.includes('performance'))
changeType = 'perf';
// Generate scope based on file types and keywords
let scope = this.determineScope(fileTypes, keywords);
// Generate description based on content and file analysis
let description = this.generateDescription(fileTypes, changeTypes, keywords);
if (conventional) {
return `${changeType}${scope ? `(${scope})` : ''}: ${description}`;
}
else {
return `${description}`;
}
}
/**
* Determine the scope based on file types and keywords
*/
determineScope(fileTypes, keywords) {
const fileExtensions = Object.keys(fileTypes);
// Check for specific keywords first
if (keywords.includes('auth'))
return 'auth';
if (keywords.includes('security'))
return 'security';
if (keywords.includes('test'))
return 'test';
if (keywords.includes('docs'))
return 'docs';
// Check file extensions
if (fileExtensions.includes('js') || fileExtensions.includes('ts')) {
return 'core';
}
if (fileExtensions.includes('test') || fileExtensions.includes('spec'))
return 'test';
if (fileExtensions.includes('md') || fileExtensions.includes('txt'))
return 'docs';
if (fileExtensions.includes('css') || fileExtensions.includes('scss'))
return 'style';
if (fileExtensions.includes('json') || fileExtensions.includes('yaml'))
return 'config';
return '';
}
/**
* Generate description based on changes
*/
generateDescription(fileTypes, changeTypes, keywords) {
const fileCount = Object.values(fileTypes).reduce((a, b) => a + b, 0);
// Check for specific keywords first
if (keywords.includes('auth'))
return 'add authentication functionality';
if (keywords.includes('security'))
return 'address security vulnerabilities';
if (keywords.includes('performance'))
return 'optimize performance';
if (keywords.includes('test'))
return 'add test coverage';
if (keywords.includes('docs'))
return 'update documentation';
if (keywords.includes('refactor'))
return 'refactor code structure';
if (keywords.includes('fix'))
return 'fix bugs and issues';
// Analyze file types for better descriptions
const fileExtensions = Object.keys(fileTypes);
if (fileExtensions.includes('test') || fileExtensions.includes('spec')) {
return 'add test coverage';
}
if (fileExtensions.includes('md') || fileExtensions.includes('txt')) {
return 'update documentation';
}
if (fileExtensions.includes('json') || fileExtensions.includes('yaml')) {
return 'update configuration';
}
if (fileExtensions.includes('css') || fileExtensions.includes('scss')) {
return 'update styling';
}
// Generic descriptions based on change types
if (changeTypes.additions > changeTypes.deletions) {
if (fileCount === 1)
return 'add new functionality';
return `add ${fileCount} new files`;
}
else if (changeTypes.deletions > changeTypes.additions) {
return 'remove unused code';
}
else {
return 'update existing functionality';
}
}
/**
* Generate secondary suggestions
*/
generateSecondarySuggestions(fileTypes, changeTypes, keywords, conventional) {
const suggestions = [];
// Alternative change types
if (keywords.includes('auth')) {
suggestions.push(conventional ? 'feat(auth): implement user authentication' : 'Add user authentication system');
}
if (keywords.includes('security')) {
suggestions.push(conventional ? 'fix(security): enhance security measures' : 'Improve security implementation');
}
if (keywords.includes('performance')) {
suggestions.push(conventional ? 'perf: optimize application performance' : 'Optimize application performance');
}
// File type specific suggestions
const fileExtensions = Object.keys(fileTypes);
if (fileExtensions.includes('test') || fileExtensions.includes('spec')) {
suggestions.push(conventional ? 'test: add comprehensive test suite' : 'Add comprehensive test coverage');
}
if (fileExtensions.includes('md') || fileExtensions.includes('txt')) {
suggestions.push(conventional ? 'docs: update project documentation' : 'Update project documentation');
}
// Generic suggestions
if (changeTypes.additions > 0) {
suggestions.push(conventional ? 'feat: add new features' : 'Add new features');
}
if (changeTypes.modifications > 0) {
suggestions.push(conventional ? 'refactor: improve code structure' : 'Improve code structure');
}
return suggestions.slice(0, 2); // Return max 2 secondary suggestions
}
/**
* Add emoji to commit message
*/
addEmoji(message) {
if (message.includes('feat'))
return '๐ ' + message;
if (message.includes('fix'))
return '๐ ' + message;
if (message.includes('refactor'))
return 'โป๏ธ ' + message;
if (message.includes('test'))
return '๐งช ' + message;
if (message.includes('docs'))
return '๐ ' + message;
if (message.includes('perf'))
return 'โก ' + message;
if (message.includes('security'))
return '๐ ' + message;
if (message.includes('auth'))
return '๐ ' + message;
return 'โจ ' + message;
}
/**
* Generate intelligent fallback suggestions when AI fails
*/
generateIntelligentFallbackSuggestions(diff, options) {
const { conventional = true, emoji = false } = options;
const suggestions = [];
const fileTypes = this.analyzeFileTypes(diff);
const changeTypes = this.analyzeChangeTypes(diff);
const stagedKeywords = this.analyzeStagedChanges(diff);
// Generate suggestions based on file analysis
const primarySuggestion = this.generatePrimarySuggestion(fileTypes, changeTypes, stagedKeywords, conventional);
suggestions.push(primarySuggestion);
// Generate secondary suggestions
const secondarySuggestions = this.generateSecondarySuggestions(fileTypes, changeTypes, stagedKeywords, conventional);
suggestions.push(...secondarySuggestions);
// Add emojis if requested
if (emoji) {
return suggestions.map(s => this.addEmoji(s));
}
return suggestions.slice(0, 3);
}
}
exports.CommitMessageGenerator = CommitMessageGenerator;