| 278 | } |
| 279 | |
| 280 | function basicFilter(commits: CommitInfo[]): CommitInfo[] { |
| 281 | return commits.filter((commit) => { |
| 282 | const { message, stats } = commit |
| 283 | const lowerMessage = message.toLowerCase() |
| 284 | |
| 285 | // Filter out obvious non-candidates |
| 286 | if ( |
| 287 | // Dependency updates |
| 288 | lowerMessage.includes('update dependencies') || |
| 289 | lowerMessage.includes('bump ') || |
| 290 | (lowerMessage.includes('upgrade ') && lowerMessage.includes('version')) || |
| 291 | // Auto-generated |
| 292 | lowerMessage.includes('auto-generated') || |
| 293 | lowerMessage.includes('generated by') || |
| 294 | // Build/CI changes |
| 295 | lowerMessage.includes('ci:') || |
| 296 | lowerMessage.includes('build:') || |
| 297 | // Formatting/linting |
| 298 | (lowerMessage.includes('format') && lowerMessage.includes('code')) || |
| 299 | lowerMessage.includes('lint') || |
| 300 | lowerMessage.includes('prettier') || |
| 301 | // Merge commits |
| 302 | lowerMessage.startsWith('merge ') || |
| 303 | // Reverts |
| 304 | lowerMessage.startsWith('revert ') || |
| 305 | // Documentation only (unless it's substantial) |
| 306 | (lowerMessage.includes('readme') && stats.filesChanged <= 1) || |
| 307 | (lowerMessage.includes('docs:') && stats.filesChanged <= 2) |
| 308 | ) { |
| 309 | return false |
| 310 | } |
| 311 | |
| 312 | // Filter by stats - too small or too large |
| 313 | if ( |
| 314 | stats.filesChanged === 0 || |
| 315 | (stats.filesChanged <= 1 && stats.insertions + stats.deletions < 5) || |
| 316 | stats.filesChanged > 50 || // Massive changes |
| 317 | stats.insertions + stats.deletions > 2000 // Huge diffs |
| 318 | ) { |
| 319 | return false |
| 320 | } |
| 321 | |
| 322 | return true |
| 323 | }) |
| 324 | } |
| 325 | |
| 326 | export function createGithubUrl(repoUrl: string, sha: string): string { |
| 327 | // Convert repo URL to GitHub commit URL |