UNPKG

incize

Version:

AI Commit Copilot for Power Developers

319 lines (318 loc) โ€ข 13.2 kB
"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;