Check for duplicates before adding tickets
Some checks failed
Publish Library / Build NPM Project (push) Successful in 14s
Code review / review (pull_request) Failing after 28s
Publish Library / Tag Version (push) Successful in 27s

This commit is contained in:
2025-12-30 23:17:45 -05:00
parent 57bbc1fdb4
commit 8add830d2b

View File

@@ -28,14 +28,14 @@ dotenv.config({path: '.env.local', override: true, quiet: true});
// Fetch issue
const issueRes = await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues/${ticket}`, {
headers: {'Authorization': `token ${auth}`}
});
if(!issueRes.ok) throw new Error(`${issueRes.status} ${await issueRes.text()}`);
}).then(async resp => !resp.ok ? throw new Error(`${updateRes.status} ${await updateRes.text()}`) : null);
const issueData = await issueRes.json();
if(!issueData.labels?.some(l => l.name === 'Review/AI')) {
if(issueData.labels[0] !== 'Review/AI' || issueData.labels.length !== 1) {
console.log('Skipping');
return process.exit();
}
// Gather readme & template
let title = '', type = '', readme = '', readmeP = path.join(process.cwd(), 'README.md');
if(fs.existsSync(readmeP)) readme = fs.readFileSync(readmeP, 'utf-8');
const template = p ? fs.readFileSync(p, 'utf-8') : `## Description
@@ -80,6 +80,7 @@ Implementation details, constraints, dependencies, design decisions
| **Total** | **0-15** |
`;
// Create AI
let options = {ollama: {model, host}};
if(host === 'anthropic') options = {anthropic: {model, token}};
else if(host === 'openai') options = {openAi: {model, token}};
@@ -141,34 +142,61 @@ ${template.trim()}
Output ONLY markdown. No explanations, labels, or extra formatting.`});
// Format ticket with AI
const messages = await ai.language.ask(`Title: ${issueData.title}\n\nDescription:\n${issueData.body || 'No description provided'}`).catch(() => []);
const body = messages?.pop()?.content;
if(!body) {
console.log('Invalid response from AI');
return process.exit(1);
if(!body) throw new Error('Invalid response from AI');
// Check for duplicates
const search = await fetch(`${git}/api/v1/repos/issues/search`, {
method: 'POST',
headers: {'Authorization': `token ${auth}`, 'Content-Type': 'application/json'},
body: JSON.stringify({
priority_repo_id: repo,
type: 'issues',
limit: 3,
q: title
})
}).then(resp => resp.ok ? resp.json() : []);
let dupeId = null;
const dupeIds = search.map(t => t.id);
const dupes = search.map(t => `ID: ${t.id}\nTitle: ${t.title}\n\`\`\`markdown\n${t.body}\n\`\`\``).join('\n\n');
const hasDuplicates = (await ai.language.ask(`${title}\n\`\`\`markdown\n${body}\n\`\`\``, {
system: `Your job is to identify duplicates. Respond with the ID number of the duplicate or nothing if there are no matches \n\n${dupes}`
}))?.pop()?.content;
// Handle duplicates
if(!!hasDuplicates && (dupeId = dupeIds.find(id => hasDuplicates.includes(id.toString()))) != null) {
await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues/${ticket}/comments`, {
method: 'POST',
headers: {'Authorization': `token ${auth}`, 'Content-Type': 'application/json'},
body: `{"body": "Duplicate of #${dupeId}"}`
}).then(async resp => !resp.ok ? throw new Error(`${updateRes.status} ${await updateRes.text()}`) : null);
await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues/${ticket}/labels`, {
method: 'POST',
headers: {'Authorization': `token ${auth}`, 'Content-Type': 'application/json'},
body: '{"labels":["Reviewed/Duplicate"]}'
}).then(async resp => !resp.ok ? throw new Error(`${updateRes.status} ${await updateRes.text()}`) : null);
await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues/${ticket}`, {
method: 'PATCH',
headers: {'Authorization': `token ${auth}`, 'Content-Type': 'application/json'},
body: '{"state": "closed"}'
}).then(async resp => !resp.ok ? throw new Error(`${updateRes.status} ${await updateRes.text()}`) : null);
console.log('Duplicate');
return process.exit();
}
// Update ticket
const updateRes = await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues/${ticket}`, {
method: 'PATCH',
headers: {
'Authorization': `token ${auth}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
title,
body,
})
});
if(!updateRes.ok) throw new Error(`${updateRes.status} ${await updateRes.text()}`);
if(type) {
const resp = await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues/${ticket}/labels`, {
headers: {'Authorization': `token ${auth}`, 'Content-Type': 'application/json'},
body: JSON.stringify({title, body})
}).then(async resp => !resp.ok ? throw new Error(`${updateRes.status} ${await updateRes.text()}`) : null);
if(type) { // Label
await fetch(`${git}/api/v1/repos/${owner}/${repo}/issues/${ticket}/labels`, {
method: 'POST',
headers: {
'Authorization': `token ${auth}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({labels: [`Kind/${type[0].toUpperCase() + type.slice(1).toLowerCase()}`]})
});
if(!resp.ok) throw new Error(`${resp.status} ${await resp.text()}`);
headers: {'Authorization': `token ${auth}`, 'Content-Type': 'application/json'},
body: `{"labels":["Reviewed/${type[0].toUpperCase() + type.slice(1).toLowerCase()}"]}`
}).then(async resp => !resp.ok ? throw new Error(`${updateRes.status} ${await updateRes.text()}`) : null);
}
console.log(`Title: ${title}\nType: ${type}\nBody:\n${body}`);