-
-
Notifications
You must be signed in to change notification settings - Fork 509
485 lines (411 loc) · 19.1 KB
/
issue-assessment.yaml
File metadata and controls
485 lines (411 loc) · 19.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
name: Issue Assessment
on:
issues:
types: [labeled]
jobs:
# Comment when need-more-info label is manually added
need-more-info:
if: github.event.label.name == 'need-more-info'
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add comment
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## Additional Information Needed
This issue is missing information we need to investigate the problem.
### Please provide:
- [ ] **Reproduction repository** — A public GitHub repo that demonstrates the issue
- [ ] **Prettier extension logs** — Click "Prettier" in the status bar and copy the output
- [ ] **Steps to reproduce** — Clear steps we can follow to see the issue
### Why this matters
Most issues are specific to your project's configuration. Without a reproduction, we usually cannot diagnose the problem.
### Resources
- [Creating a minimal reproduction](https://github.com/prettier/prettier-vscode/blob/main/docs/troubleshooting.md)
- [Writing a good issue](https://github.com/prettier/prettier-vscode/blob/main/docs/writing-an-issue.md)
> **Note:** Issues without the requested information may be closed after 7 days.`
});
triage:
if: github.event.label.name == 'triage-pending'
runs-on: ubuntu-latest
permissions:
issues: write
models: read
contents: read
outputs:
triage_labels: ${{ steps.triage.outputs.ai_assessments }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Run Issue Triage
id: triage
uses: github/ai-assessment-comment-labeler@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue_number: ${{ github.event.issue.number }}
issue_body: ${{ github.event.issue.body }}
ai_review_label: "triage-pending"
prompts_directory: "./.github/prompts"
labels_to_prompts_mapping: "triage-pending,issue-triage.prompt.yml"
- name: Remove pending label
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
name: 'triage-pending'
}).catch(() => {});
post-triage:
runs-on: ubuntu-latest
needs: triage
if: always() && needs.triage.result == 'success'
permissions:
issues: write
steps:
- name: Process triage results and post comments
uses: actions/github-script@v7
env:
TRIAGE_OUTPUT: ${{ needs.triage.outputs.triage_labels }}
with:
script: |
const issueNumber = context.issue.number;
const triageOutput = process.env.TRIAGE_OUTPUT;
let triageAssessments = [];
try {
triageAssessments = JSON.parse(triageOutput || '[]');
} catch (e) {
console.log('Could not parse triage output:', e);
}
const triageLabel = triageAssessments[0]?.assessmentLabel || '';
console.log('Triage assessment label:', triageLabel);
const responses = {
'ai:issue-triage:needs-reproduction': {
addLabel: 'need-more-info',
comment: `## Reproduction Repository Needed
Thanks for the report! To investigate this issue, we need more information.
### Please provide:
1. **A GitHub repository** that reproduces the issue
2. **Step-by-step instructions** to reproduce the problem
3. **Prettier extension logs** — Click "Prettier" in the status bar and copy the output
### Why this matters
Most issues depend on specific project configurations. A reproduction repository lets us see exactly what you're experiencing.
### Resources
- [Creating a minimal reproduction](https://github.com/prettier/prettier-vscode/blob/main/docs/troubleshooting.md)
- [Writing a good issue](https://github.com/prettier/prettier-vscode/blob/main/docs/writing-an-issue.md)
> **Note:** Issues without reproduction info may be closed after 7 days.`
},
'ai:issue-triage:prettier-core': {
addLabel: 'prettier-core',
comment: `## This Looks Like a Prettier Core Issue
Thanks for the report! This issue appears to be about **how Prettier formats code**, not about the VS Code extension.
### Understanding the difference
| Prettier Core | VS Code Extension |
|--------------|-------------------|
| Decides *how* code is formatted | Integrates Prettier into VS Code |
| Formatting rules and output | Editor integration, settings UI |
| [prettier/prettier](https://github.com/prettier/prettier) | [prettier/prettier-vscode](https://github.com/prettier/prettier-vscode) |
### To confirm this is a Prettier Core issue
Run in your terminal:
\`\`\`bash
npx prettier --write yourfile.js
\`\`\`
If you see the same formatting behavior, it's a Prettier Core issue.
### Next steps
1. Check the [Prettier Playground](https://prettier.io/playground/) to test formatting
2. Search [existing Prettier issues](https://github.com/prettier/prettier/issues)
3. Open an issue at **[prettier/prettier](https://github.com/prettier/prettier/issues/new)**
### If this IS an extension issue
If the CLI formats differently than the extension, please reply with:
- Output from \`npx prettier --version\`
- Prettier extension logs
- Example showing the difference`
},
'ai:issue-triage:performance-info-needed': {
addLabel: 'need-more-info',
comment: `## Performance Information Needed
Thanks for reporting this performance issue! To investigate, we need additional details.
### Please provide:
| Information | How to get it |
|------------|---------------|
| Prettier version | Check the Prettier output panel |
| File size | Size of slow-to-format files |
| File types affected | Which languages/extensions |
| Prettier plugins | List any plugins you're using |
| Debug logs | Enable with \`"prettier.enableDebugLogs": true\` |
### Quick test
Compare with the CLI:
\`\`\`bash
time npx prettier --write yourfile.js
\`\`\`
If the CLI is also slow, the issue is with Prettier Core, not this extension.
### Optional: VS Code performance profile
1. Open Command Palette → **Developer: Start Performance Profile**
2. Format a document
3. Stop the profile and attach it here
> **Note:** Issues without the requested information may be closed after 7 days.`
},
'ai:issue-triage:incomplete-template': {
addLabel: 'need-more-info',
comment: `## Issue Template Incomplete
Thanks for opening this issue! Some required information is missing.
### Please edit your issue to include:
- [ ] **Summary** — Brief description of the problem
- [ ] **Reproduction repository** — Public GitHub repo demonstrating the issue
- [ ] **Steps to reproduce** — Clear steps to see the problem
- [ ] **Expected vs actual result** — What should happen vs what happens
- [ ] **Environment info:**
- VS Code version (\`Help → About → Copy\`)
- Prettier extension version
- Operating system
- [ ] **Prettier logs** — Click "Prettier" in status bar, copy output
### Resources
- [Writing a good issue](https://github.com/prettier/prettier-vscode/blob/main/docs/writing-an-issue.md)
- [Troubleshooting guide](https://github.com/prettier/prettier-vscode/blob/main/docs/troubleshooting.md)
> **Note:** Issues without the requested information may be closed after 7 days.`
},
'ai:issue-triage:support-question': {
comment: `## This Looks Like a Support Question
Thanks for reaching out! This appears to be a question about configuration or usage rather than a bug report.
### Get help here:
| Resource | Best for |
|----------|----------|
| [Stack Overflow](https://stackoverflow.com/questions/tagged/prettier-vscode) | Configuration questions |
| [Prettier Docs](https://prettier.io/docs/en/index.html) | Formatting options |
| [Extension Settings](https://github.com/prettier/prettier-vscode#settings) | VS Code settings |
| [Troubleshooting Guide](https://github.com/prettier/prettier-vscode/blob/main/docs/troubleshooting.md) | Common issues |
### If this is actually a bug
Please reopen with:
1. What you've already tried
2. A minimal reproduction repository
3. Prettier extension logs
We're happy to help with confirmed bugs!`
}
};
if (triageLabel && responses[triageLabel]) {
const response = responses[triageLabel];
if (response.comment) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: response.comment
});
}
if (response.addLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
labels: [response.addLabel]
});
}
}
# Duplicate detection
duplicate-search:
runs-on: ubuntu-latest
needs: triage
if: always() && needs.triage.result == 'success'
permissions:
issues: write
models: read
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Search for candidate issues
id: search
uses: actions/github-script@v7
with:
script: |
const issueTitle = context.payload.issue.title;
const issueBody = context.payload.issue.body || '';
const issueNumber = context.issue.number;
// Common words to filter out
const stopWords = new Set([
'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
'should', 'may', 'might', 'must', 'can', 'need', 'to', 'of', 'in',
'for', 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through',
'during', 'before', 'after', 'then', 'once', 'here', 'there', 'when',
'where', 'why', 'how', 'all', 'each', 'few', 'more', 'most', 'other',
'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so',
'than', 'too', 'very', 'just', 'and', 'but', 'if', 'or', 'because',
'until', 'while', 'this', 'that', 'these', 'those', 'i', 'me', 'my',
'we', 'our', 'you', 'your', 'he', 'him', 'she', 'her', 'it', 'its',
'they', 'them', 'their', 'what', 'which', 'who', 'whom',
'prettier', 'vscode', 'extension', 'code', 'file', 'files', 'issue',
'bug', 'error', 'problem', 'help', 'please', 'work', 'working',
'works', 'doesnt', "doesn't", 'dont', "don't", 'cant', "can't",
'wont', "won't", 'using', 'use', 'get', 'getting', 'not', 'format',
'formatting', 'save', 'saving'
]);
const extractKeywords = (text) => {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, ' ')
.split(/\s+/)
.filter(word => word.length > 2 && !stopWords.has(word));
};
const titleKeywords = extractKeywords(issueTitle);
console.log('Keywords:', titleKeywords);
if (titleKeywords.length === 0) {
console.log('No keywords, skipping');
core.setOutput('candidates', '[]');
core.setOutput('has_candidates', 'false');
return;
}
const searchKeywords = titleKeywords.slice(0, 4).join(' ');
const query = `repo:${context.repo.owner}/${context.repo.repo} is:issue ${searchKeywords}`;
let candidates = [];
try {
const result = await github.rest.search.issuesAndPullRequests({
q: query,
sort: 'updated',
order: 'desc',
per_page: 10
});
for (const issue of result.data.items) {
if (issue.number === issueNumber) continue;
candidates.push({
number: issue.number,
title: issue.title,
body: (issue.body || '').substring(0, 500),
state: issue.state,
url: issue.html_url
});
}
} catch (e) {
console.log('Search error:', e.message);
}
candidates = candidates.slice(0, 8);
console.log(`Found ${candidates.length} candidates`);
// Build comparison text for AI
const newIssue = `## NEW ISSUE #${issueNumber}
Title: ${issueTitle}
Body: ${issueBody.substring(0, 1000)}`;
let candidateText = '';
for (const c of candidates) {
candidateText += `\n\n## CANDIDATE ISSUE #${c.number} (${c.state})
Title: ${c.title}
Body: ${c.body}`;
}
const comparisonInput = newIssue + '\n\n---\n\nCANDIDATE ISSUES TO COMPARE:' + candidateText;
core.setOutput('candidates', JSON.stringify(candidates));
core.setOutput('has_candidates', candidates.length > 0 ? 'true' : 'false');
core.setOutput('comparison_input', comparisonInput);
- name: Duplicate Analysis
id: analysis
if: steps.search.outputs.has_candidates == 'true'
uses: github/ai-assessment-comment-labeler@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue_number: ${{ github.event.issue.number }}
issue_body: ${{ steps.search.outputs.comparison_input }}
ai_review_label: "triage-pending"
prompts_directory: "./.github/prompts"
labels_to_prompts_mapping: "triage-pending,duplicate-comparison.prompt.yml"
suppress_comments: true
suppress_labels: true
- name: Post duplicate analysis results
if: steps.search.outputs.has_candidates == 'true'
uses: actions/github-script@v7
env:
AI_OUTPUT: ${{ steps.analysis.outputs.ai_assessments }}
CANDIDATES: ${{ steps.search.outputs.candidates }}
with:
script: |
const issueNumber = context.issue.number;
const aiOutput = process.env.AI_OUTPUT;
const candidatesJson = process.env.CANDIDATES;
let candidates = [];
try {
candidates = JSON.parse(candidatesJson || '[]');
} catch (e) {
console.log('Could not parse candidates');
return;
}
if (candidates.length === 0) {
console.log('No candidates to analyze');
return;
}
// Try to parse AI response
let aiAssessments = [];
let analysis = null;
try {
aiAssessments = JSON.parse(aiOutput || '[]');
const response = aiAssessments[0]?.response || '';
console.log('AI Response:', response);
// Try to extract JSON from the response
const jsonMatch = response.match(/\{[\s\S]*\}/);
if (jsonMatch) {
analysis = JSON.parse(jsonMatch[0]);
}
} catch (e) {
console.log('Could not parse AI analysis:', e.message);
}
// Build comment based on AI analysis
let comment = '';
let hasDuplicates = false;
let hasRelated = false;
if (analysis && (analysis.duplicates?.length > 0 || analysis.related?.length > 0)) {
// AI found duplicates or related issues
if (analysis.duplicates?.length > 0) {
hasDuplicates = true;
comment += `### Likely Duplicate Issues Found\n\n`;
comment += `This issue appears to be a duplicate of:\n\n`;
comment += `| Issue | Title | Confidence | Reason |\n`;
comment += `|-------|-------|------------|--------|\n`;
for (const dup of analysis.duplicates) {
const candidate = candidates.find(c => c.number === dup.number);
if (candidate) {
const title = candidate.title.replace(/\|/g, '\\|').substring(0, 40) + (candidate.title.length > 40 ? '...' : '');
const status = candidate.state === 'open' ? '🟢' : '🔴';
comment += `| ${status} #${dup.number} | ${title} | ${dup.confidence} | ${dup.reason} |\n`;
}
}
comment += `\n**Please check these issues.** If your issue is indeed a duplicate, close this issue and add your information to the existing one instead.\n\n`;
}
if (analysis.related?.length > 0) {
hasRelated = true;
comment += `### Possibly Related Issues\n\n`;
comment += `| Issue | Title | Reason |\n`;
comment += `|-------|-------|--------|\n`;
for (const rel of analysis.related) {
const candidate = candidates.find(c => c.number === rel.number);
if (candidate) {
const title = candidate.title.replace(/\|/g, '\\|').substring(0, 50) + (candidate.title.length > 50 ? '...' : '');
const status = candidate.state === 'open' ? '🟢' : '🔴';
comment += `| ${status} #${rel.number} | ${title} | ${rel.reason} |\n`;
}
}
comment += `\nThese issues might provide useful context or workarounds.\n\n`;
}
} else {
// Fallback: just show candidate issues if AI analysis failed
console.log('AI analysis unavailable, showing keyword matches');
comment = `### Potentially Related Issues\n\n`;
comment += `I found some existing issues that might be related based on keywords:\n\n`;
comment += `| Issue | Title | Status |\n`;
comment += `|-------|-------|--------|\n`;
for (const c of candidates.slice(0, 5)) {
const title = c.title.replace(/\|/g, '\\|').substring(0, 50) + (c.title.length > 50 ? '...' : '');
const status = c.state === 'open' ? '🟢 Open' : '🔴 Closed';
comment += `| #${c.number} | ${title} | ${status} |\n`;
}
comment += `\nPlease check if any of these address your issue.\n\n`;
}
comment += `---\n<sub>This analysis was generated automatically to help identify duplicate issues.</sub>`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: comment
});