| "use strict"; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.getFileCategory = exports.shouldSkipLLM = exports.filterFilesForLLM = exports.logTokenSavings = exports.generateConsolidatedRecommendations = exports.generateRecommendation = exports.classifyFiles = exports.classifyFile = exports.LOCKFILES = exports.IGNORED_PATTERNS = void 0; |
| exports.IGNORED_PATTERNS = [ |
| /node_modules\//, |
| /dist\//, |
| /build\//, |
| /\.next\//, |
| /coverage\//, |
| /\.min\.js$/, |
| /\.bundle\.js$/, |
| /\.generated\./, |
| /vendor\//, |
| /\.map$/, |
| /\.snap$/, |
| /\.lock$/, |
| /\.md$/, |
| /__pycache__\//, |
| /\.pyc$/, |
| /\.egg-info\//, |
| /target\//, |
| /out\//, |
| /\.cache\//, |
| /\.vscode\//, |
| /\.idea\//, |
| /\.DS_Store$/, |
| /Thumbs\.db$/ |
| ]; |
| exports.LOCKFILES = [ |
| 'package-lock.json', |
| 'pnpm-lock.yaml', |
| 'yarn.lock', |
| 'Cargo.lock', |
| 'poetry.lock', |
| 'requirements.txt', |
| 'Pipfile.lock', |
| 'go.sum', |
| 'composer.lock' |
| ]; |
| const TOKENS_PER_LINE = 4; |
| const TOKENS_PER_KB = 150; |
| const classifyFile = (filename) => { |
| const lowerFilename = filename.toLowerCase(); |
| if (lowerFilename.includes('node_modules/')) { |
| return { |
| filename, |
| category: 'node_modules', |
| skipLLM: true, |
| reason: 'node_modules directory should not be committed to Git' |
| }; |
| } |
| const isLockfile = exports.LOCKFILES.some(lockfile => { |
| const lockfileLower = lockfile.toLowerCase(); |
| return (lowerFilename.endsWith(lockfileLower) || |
| lowerFilename.includes(lockfileLower)); |
| }); |
| if (isLockfile) { |
| return { |
| filename, |
| category: 'lockfile', |
| skipLLM: true, |
| reason: 'Dependency lockfile - skipping deep AI analysis' |
| }; |
| } |
| for (const pattern of exports.IGNORED_PATTERNS) { |
| if (pattern.test(filename)) { |
| return { |
| filename, |
| category: 'generated', |
| skipLLM: true, |
| reason: 'Generated or build artifact' |
| }; |
| } |
| } |
| return { |
| filename, |
| category: 'source', |
| skipLLM: false |
| }; |
| }; |
| exports.classifyFile = classifyFile; |
| const classifyFiles = (filenames) => { |
| const classifications = []; |
| const metrics = { |
| skippedFiles: 0, |
| skippedTokensEstimate: 0, |
| categories: {} |
| }; |
| for (const filename of filenames) { |
| const classification = (0, exports.classifyFile)(filename); |
| classifications.push(classification); |
| if (classification.skipLLM) { |
| metrics.skippedFiles++; |
| |
| metrics.categories[classification.category] = |
| (metrics.categories[classification.category] || 0) + 1; |
| |
| |
| const estimatedTokens = Math.max(1000, filename.length * TOKENS_PER_LINE); |
| metrics.skippedTokensEstimate += estimatedTokens; |
| } |
| } |
| return { classifications, metrics }; |
| }; |
| exports.classifyFiles = classifyFiles; |
| const generateRecommendation = (classification) => { |
| if (!classification.skipLLM) { |
| return null; |
| } |
| switch (classification.category) { |
| case 'node_modules': |
| return `## 💡 Recommendation
|
|
|
| \`node_modules/\` was detected in this pull request.
|
|
|
| This directory should usually not be committed to Git.
|
|
|
| ### Suggested Fix
|
|
|
| Add this to \`.gitignore\`:
|
|
|
| \`\`\`gitignore
|
| node_modules/
|
| \`\`\`
|
|
|
| ### AI Agent Prompt
|
|
|
| Remove committed node_modules files and update .gitignore to exclude them permanently.`; |
| case 'lockfile': |
| return `## ℹ️ Optimization Suggestion
|
|
|
| Large dependency lockfile changes detected.
|
|
|
| Consider separating dependency updates into dedicated PRs to reduce review noise and improve review clarity.`; |
| case 'generated': |
| return `## 💡 Recommendation
|
|
|
| Generated/build artifacts were detected and skipped from AI review to improve review performance and reduce unnecessary noise.`; |
| default: |
| return `## ℹ️ Information
|
|
|
| File \`${classification.filename}\` was skipped from AI review: ${classification.reason || 'No specific reason provided'}`; |
| } |
| }; |
| exports.generateRecommendation = generateRecommendation; |
| const generateConsolidatedRecommendations = (classifications) => { |
| const recommendations = []; |
| const seenCategories = new Set(); |
| |
| const byCategory = new Map(); |
| for (const classification of classifications) { |
| if (classification.skipLLM) { |
| if (!byCategory.has(classification.category)) { |
| byCategory.set(classification.category, []); |
| } |
| byCategory.get(classification.category).push(classification); |
| } |
| } |
| |
| for (const [category, files] of byCategory.entries()) { |
| if (seenCategories.has(category)) |
| continue; |
| seenCategories.add(category); |
| const representative = files[0]; |
| const recommendation = (0, exports.generateRecommendation)(representative); |
| if (recommendation) { |
| |
| let finalRecommendation = recommendation; |
| if (files.length > 1) { |
| finalRecommendation = recommendation.replace(/was detected/, `(${files.length} files) were detected`); |
| } |
| recommendations.push(finalRecommendation); |
| } |
| } |
| return recommendations; |
| }; |
| exports.generateConsolidatedRecommendations = generateConsolidatedRecommendations; |
| const logTokenSavings = (metrics) => { |
| console.log('REVIEW_OPTIMIZATION', { |
| skippedFiles: metrics.skippedFiles, |
| skippedTokensEstimate: metrics.skippedTokensEstimate, |
| reason: 'File classification filtering', |
| categories: metrics.categories |
| }); |
| |
| if (metrics.skippedFiles > 0) { |
| const tokenK = (metrics.skippedTokensEstimate / 1000).toFixed(0); |
| console.log(`Skipped ${metrics.skippedFiles} generated/irrelevant files`); |
| console.log(`Estimated token savings: ${tokenK}k`); |
| } |
| }; |
| exports.logTokenSavings = logTokenSavings; |
| const filterFilesForLLM = (filenames) => { |
| return filenames.filter(filename => !(0, exports.classifyFile)(filename).skipLLM); |
| }; |
| exports.filterFilesForLLM = filterFilesForLLM; |
| const shouldSkipLLM = (filename) => { |
| return (0, exports.classifyFile)(filename).skipLLM; |
| }; |
| exports.shouldSkipLLM = shouldSkipLLM; |
| const getFileCategory = (filename) => { |
| return (0, exports.classifyFile)(filename).category; |
| }; |
| exports.getFileCategory = getFileCategory; |
| |