export const GENERATED_FILE_PATTERNS = [ // Dependency directories 'node_modules/', '.next/', 'dist/', 'build/', 'coverage/', '.turbo/', '.cache/', '.git/', '.nuxt/', '.output/', '.vercel/', '.netlify/', '.serverless/', // Lockfiles 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb', 'composer.lock', 'Gemfile.lock', 'Cargo.lock', 'poetry.lock', 'requirements.txt', 'Pipfile.lock', 'go.sum', // Minified files '*.min.js', '*.min.css', '*.min.html', // Source maps '*.map', '*.js.map', '*.css.map', // Logs '*.log', 'npm-debug.log*', 'yarn-debug.log*', 'yarn-error.log*', 'pnpm-debug.log*', // Generated directories 'vendor/', 'generated/', '__generated__/', '.generated/', 'out/', 'artifacts/', '.artifact/', // Test snapshots '*.snap', '*.snap.jsx', '*.snap.tsx', // Binary assets '*.svg', '*.png', '*.jpg', '*.jpeg', '*.gif', '*.webp', '*.ico', '*.bmp', '*.tiff', // Fonts '*.woff', '*.woff2', '*.ttf', '*.eot', '*.otf', // Other binary '*.pdf', '*.zip', '*.tar', '*.tar.gz', '*.rar', '*.7z', // Build artifacts '*.bundle.js', '*.chunk.js', '*.chunk.css', // IDE '.vscode/', '.idea/', '*.swp', '*.swo', '*~', '.DS_Store', 'Thumbs.db', // Environment '.env', '.env.*', '*.env', '.env.local', '.env.development.local', '.env.test.local', '.env.production.local', // CI/CD '.github/workflows/*.log', '.gitlab-ci.yml', 'Jenkinsfile', 'azure-pipelines.yml' ] export const DIFF_LIMITS = { MAX_DIFF_CHARS: 4000, MAX_TOTAL_DIFF_CHARS: 15000, MAX_FILE_SIZE_CHARS: 100000 } as const export const FILE_TYPE_PRIORITY = { // High priority - always analyze HIGH: [ '.ts', '.tsx', '.js', '.jsx', '.py', '.go', '.rs', '.java', '.kt', '.swift', '.rb', '.php', '.cs', '.cpp', '.c', '.h' ], // Medium priority - analyze if relevant MEDIUM: [ '.json', '.yaml', '.yml', '.toml', '.ini', '.conf', '.config', '.xml' ], // Low priority - skip unless explicitly needed LOW: [ '.md', '.txt', '.svg', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.woff', '.woff2', '.ttf', '.eot' ] } as const export const isGeneratedFile = (filePath: string): boolean => { const normalizedPath = filePath.replace(/\\/g, '/') for (const pattern of GENERATED_FILE_PATTERNS) { if (pattern.includes('*')) { // Glob pattern const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') const regex = new RegExp(regexPattern) if (regex.test(normalizedPath)) { return true } } else if (pattern.endsWith('/')) { // Directory pattern if ( normalizedPath.startsWith(pattern) || normalizedPath.includes(`/${pattern}`) ) { return true } } else { // Exact file pattern if ( normalizedPath === pattern || normalizedPath.endsWith(`/${pattern}`) ) { return true } } } return false } export const getFilePriority = ( filePath: string ): 'high' | 'medium' | 'low' => { const ext = filePath.split('.').pop()?.toLowerCase() if (!ext) return 'medium' const extWithDot = `.${ext}` as any if (FILE_TYPE_PRIORITY.HIGH.includes(extWithDot)) return 'high' if (FILE_TYPE_PRIORITY.MEDIUM.includes(extWithDot)) return 'medium' if (FILE_TYPE_PRIORITY.LOW.includes(extWithDot)) return 'low' return 'medium' } export const isDiffTooLarge = (diff: string): boolean => { return diff.length > DIFF_LIMITS.MAX_DIFF_CHARS } export const isTotalDiffTooLarge = (totalChars: number): boolean => { return totalChars > DIFF_LIMITS.MAX_TOTAL_DIFF_CHARS } export const generateIgnoredFileNotice = (filePath: string): string => { const normalizedPath = filePath.replace(/\\/g, '/') // Special handling for lockfiles if ( normalizedPath.includes('package-lock.json') || normalizedPath.includes('yarn.lock') || normalizedPath.includes('pnpm-lock.yaml') || normalizedPath.includes('bun.lockb') ) { return `⚠️ Generated dependency file skipped: ${filePath} Large generated dependency files are excluded from AI review to reduce token usage and avoid noisy analysis. Recommendation: - Only commit lockfile changes when dependencies intentionally changed. - Avoid unnecessary lockfile churn. Suggested AI agent prompt: "Review package.json changes and regenerate lockfile only if dependency versions were intentionally updated."` } // Special handling for node_modules if (normalizedPath.includes('node_modules/')) { return `⚠️ Generated dependency directory skipped: ${filePath} Generated dependencies are excluded from review and planning pipelines.` } // Special handling for build artifacts if ( normalizedPath.includes('dist/') || normalizedPath.includes('build/') || normalizedPath.includes('.next/') || normalizedPath.includes('.nuxt/') ) { return `⚠️ Build artifact skipped: ${filePath} Build output directories are excluded from AI review as they are generated from source code.` } // Generic notice return `⚠️ Generated file skipped: ${filePath} This file matches generated file patterns and is excluded from AI review to reduce token usage.` } export interface FilterMetrics { ignoredFiles: string[] totalCharsSkipped: number largestPreventedPayload: number } export const filterMetrics: FilterMetrics = { ignoredFiles: [], totalCharsSkipped: 0, largestPreventedPayload: 0 } export const logIgnoredFile = ( filePath: string, charsSkipped: number = 0 ): void => { console.log('IGNORED_GENERATED_FILE', {filePath, charsSkipped}) filterMetrics.ignoredFiles.push(filePath) filterMetrics.totalCharsSkipped += charsSkipped filterMetrics.largestPreventedPayload = Math.max( filterMetrics.largestPreventedPayload, charsSkipped ) } export const logTokenSavings = (): void => { const estimatedTokens = Math.round(filterMetrics.totalCharsSkipped / 4) // Rough estimate: 4 chars per token console.log('FILTERED_TOKEN_SAVINGS_ESTIMATE', { filesSkipped: filterMetrics.ignoredFiles.length, charsSkipped: filterMetrics.totalCharsSkipped, estimatedTokens, largestPreventedPayload: filterMetrics.largestPreventedPayload }) } export const resetFilterMetrics = (): void => { filterMetrics.ignoredFiles = [] filterMetrics.totalCharsSkipped = 0 filterMetrics.largestPreventedPayload = 0 }