Skip to content

Commit d36d5e6

Browse files
committed
Improve some similarity checks
1 parent dbcdc75 commit d36d5e6

File tree

2 files changed

+92
-9
lines changed

2 files changed

+92
-9
lines changed

packages/toolkit/scripts/issue-triage/src/index.ts

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
*/
66

77
import { writeFile } from 'fs/promises'
8+
import path from 'path'
89
import { GitHubClient, checkGhCli } from './github/gh-client.js'
910
import { GhCliError, GhApiError, GhParseError } from './utils/errors.js'
1011
import { categorizeIssues, CATEGORIES } from './categorize/index.js'
1112
import { findAllDuplicates, createWorkClusters } from './similarity/index.js'
13+
import { generateReport, type ReportData } from './reports/index.js'
1214

1315
async function main() {
1416
console.log('GitHub Issues Triage Tool v1.0.0')
@@ -248,15 +250,51 @@ async function main() {
248250
await writeFile(outputPath, JSON.stringify(exportData, null, 2), 'utf-8')
249251
console.log(`✓ Results exported to ${outputPath}`)
250252

251-
console.log('\n✓ Categorization complete!')
253+
// Step 11: Generate Markdown reports
254+
console.log('\n📝 Generating reports...')
255+
256+
const reportData: ReportData = {
257+
issues: categorizedItems,
258+
duplicates: duplicateGroups,
259+
clusters: workClusters,
260+
metadata: {
261+
generatedAt: new Date().toISOString(),
262+
totalIssues: issues.length,
263+
totalPRs: prs.length,
264+
categorizedIssues: categorizedItems.filter(
265+
(i) => i.categorization.primary !== 'uncategorized',
266+
).length,
267+
uncategorizedIssues: categorizedItems.filter(
268+
(i) => i.categorization.primary === 'uncategorized',
269+
).length,
270+
},
271+
}
272+
273+
// Generate full report
274+
const fullReport = generateReport(reportData, { variant: 'full' })
275+
const fullReportPath = path.join('cache', 'triage-report-full.md')
276+
await writeFile(fullReportPath, fullReport, 'utf-8')
277+
console.log(`✓ Full report: ${fullReportPath}`)
278+
279+
// Generate priority report
280+
const priorityReport = generateReport(reportData, {
281+
variant: 'priority',
282+
maxIssuesPerSection: 30,
283+
})
284+
const priorityReportPath = path.join('cache', 'triage-report-priority.md')
285+
await writeFile(priorityReportPath, priorityReport, 'utf-8')
286+
console.log(`✓ Priority report: ${priorityReportPath}`)
287+
288+
console.log('\n✓ Triage complete!')
289+
console.log('\nGenerated files:')
290+
console.log(` - ${outputPath} (JSON data)`)
291+
console.log(` - ${fullReportPath} (Full markdown report)`)
292+
console.log(` - ${priorityReportPath} (Priority issues report)`)
252293
console.log('\nNext steps:')
253-
console.log(
254-
'- Review categorization results in cache/categorization-results.json',
255-
)
256-
console.log('- Use LLM to analyze categorization accuracy')
257-
console.log('- Generate detailed triage reports')
258-
console.log('- Add LLM-based categorization for ambiguous cases')
259-
console.log('- Implement trend analysis')
294+
console.log('- Review the generated reports')
295+
console.log('- Use reports to prioritize maintenance work')
296+
console.log('- Close duplicate issues')
297+
console.log('- Address urgent bugs and easy fixes')
260298
} catch (error) {
261299
// Handle specific error types with helpful messages
262300
if (error instanceof GhCliError) {

packages/toolkit/scripts/issue-triage/src/similarity/clusters.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,52 @@ function generateClusterReasoning(
8080
category: string,
8181
): string {
8282
const [primary, secondary] = category.split('/')
83-
return `All issues in ${primary}${secondary !== 'other' ? `/${secondary}` : ''} category with related functionality`
83+
84+
// Extract common keywords from titles
85+
const allWords = issues.flatMap((i) =>
86+
i.title
87+
.toLowerCase()
88+
.replace(/[^\w\s]/g, ' ')
89+
.split(/\s+/)
90+
.filter((w) => w.length > 3),
91+
)
92+
93+
const wordCounts: Record<string, number> = {}
94+
for (const word of allWords) {
95+
wordCounts[word] = (wordCounts[word] || 0) + 1
96+
}
97+
98+
const commonWords = Object.entries(wordCounts)
99+
.filter(([_, count]) => count >= 2)
100+
.sort((a, b) => b[1] - a[1])
101+
.slice(0, 3)
102+
.map(([word]) => word)
103+
104+
let reasoning = `Related ${primary}${secondary !== 'other' ? `/${secondary}` : ''} issues`
105+
106+
if (commonWords.length > 0) {
107+
reasoning += ` involving: ${commonWords.join(', ')}`
108+
}
109+
110+
// Add specific patterns
111+
const hasErrors = issues.some((i) => i.body?.toLowerCase().includes('error'))
112+
const hasTypes = issues.some((i) => i.body?.toLowerCase().includes('type'))
113+
const hasPerf = issues.some(
114+
(i) =>
115+
i.title.toLowerCase().includes('performance') ||
116+
i.title.toLowerCase().includes('slow'),
117+
)
118+
119+
const patterns: string[] = []
120+
if (hasErrors) patterns.push('error handling')
121+
if (hasTypes) patterns.push('TypeScript types')
122+
if (hasPerf) patterns.push('performance')
123+
124+
if (patterns.length > 0) {
125+
reasoning += `. Focus areas: ${patterns.join(', ')}`
126+
}
127+
128+
return reasoning
84129
}
85130

86131
/**

0 commit comments

Comments
 (0)