AINode.jsAutomation
Bug Report to GitHub Issue Converter
TT
TopicTrick Team
Bug Report to GitHub Issue Converter
Users describe bugs in every possible format — "it broke", "the thing doesn't work", "I get an error when...". This tool takes any unstructured bug description and converts it into a properly formatted GitHub issue with title, steps to reproduce, expected vs actual behaviour, severity label, and component tags.
This is Tool 23 of the Build 50 AI Automation Tools course.
What You'll Build
POST /convert— parse a bug report and create a GitHub issue automatically- Extract: title, steps to reproduce, expected/actual behaviour, severity, affected component
- Apply appropriate GitHub labels based on severity and component
Setup
bash
mkdir bug-to-issue && cd bug-to-issue
npm init -y
npm install express @octokit/rest openai dotenvbash
# .env
GITHUB_TOKEN=ghp_your-token
GITHUB_OWNER=yourorg
GITHUB_REPO=your-repo
OPENAI_API_KEY=sk-your-key-here
PORT=3000Bug Parsing Service
js
// src/services/bugService.js
import { Octokit } from '@octokit/rest';
import OpenAI from 'openai';
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const COMPONENTS = ['auth', 'api', 'ui', 'database', 'billing', 'notifications', 'search', 'upload', 'admin', 'mobile'];
async function parseBugReport(rawReport, productContext = '') {
const contextNote = productContext ? `Product context: ${productContext}` : '';
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{
role: 'system',
content: `You are an expert software QA engineer. Parse this bug report and structure it as a GitHub issue.
${contextNote}
Known components: ${COMPONENTS.join(', ')}
Return ONLY a JSON object:
{
"title": "concise issue title (under 80 chars, starts with verb: 'Cannot', 'Error:', 'Crash:', etc.)",
"severity": "critical | high | medium | low",
"component": "best matching component from the list, or 'general'",
"stepsToReproduce": ["ordered list of steps to reproduce the bug"],
"expectedBehaviour": "string — what should happen",
"actualBehaviour": "string — what actually happens",
"environment": "browser/OS/version info if mentioned, or 'Not specified'",
"affectedUsers": "description of who is affected (all users, specific plan, etc.)",
"workaround": "string or null — any known workaround",
"additionalContext": "any other relevant information",
"labels": ["severity-{level}", "component-{component}", "bug"],
"formattedBody": "full GitHub issue body in markdown format"
}`,
},
{ role: 'user', content: rawReport },
],
temperature: 0.2,
response_format: { type: 'json_object' },
});
return JSON.parse(response.choices[0].message.content);
}
async function ensureLabelsExist(owner, repo, labels) {
const LABEL_COLORS = {
'severity-critical': 'd73a4a',
'severity-high': 'e99695',
'severity-medium': 'fbca04',
'severity-low': '0075ca',
'bug': 'd73a4a',
};
await Promise.all(labels.map(async label => {
try {
await octokit.issues.createLabel({
owner, repo,
name: label,
color: LABEL_COLORS[label] || 'cccccc',
});
} catch (err) {
// Label already exists — ignore
if (err.status !== 422) throw err;
}
}));
}
export async function convertBugToIssue(rawReport, owner, repo, productContext) {
const parsed = await parseBugReport(rawReport, productContext);
await ensureLabelsExist(owner, repo, parsed.labels);
const { data: issue } = await octokit.issues.create({
owner,
repo,
title: parsed.title,
body: parsed.formattedBody,
labels: parsed.labels,
});
return { parsed, issue: { number: issue.number, url: issue.html_url, title: issue.title } };
}Server
js
// src/server.js
import 'dotenv/config';
import express from 'express';
import { convertBugToIssue } from './services/bugService.js';
const app = express();
app.use(express.json());
const DEFAULT_OWNER = process.env.GITHUB_OWNER;
const DEFAULT_REPO = process.env.GITHUB_REPO;
app.post('/convert', async (req, res, next) => {
try {
const { report, owner, repo, productContext } = req.body;
if (!report?.trim()) return res.status(400).json({ error: 'Bug report text required' });
const result = await convertBugToIssue(
report,
owner || DEFAULT_OWNER,
repo || DEFAULT_REPO,
productContext,
);
res.json({ success: true, ...result });
} catch (err) { next(err); }
});
app.use((err, _req, res, _next) => res.status(500).json({ error: err.message }));
app.listen(process.env.PORT ?? 3000, () => console.log('Bug to Issue converter running'));Testing
bash
curl -X POST http://localhost:3000/convert \
-H "Content-Type: application/json" \
-d '{
"report": "Hey team, users on the pro plan are reporting that when they try to upload files over 10MB they get a white screen and the upload never completes. This started happening sometime yesterday afternoon. Refreshing doesnt help. Regular plan users seem fine. Im seeing it on Chrome and Safari.",
"productContext": "SaaS file management platform"
}'Sample response:
json
{
"parsed": {
"title": "File upload fails with white screen for files over 10MB on Pro plan",
"severity": "high",
"component": "upload",
"stepsToReproduce": [
"Log in with a Pro plan account",
"Navigate to file upload page",
"Attempt to upload a file larger than 10MB",
"Observe white screen and failed upload"
],
"expectedBehaviour": "File should upload successfully and appear in the file list",
"actualBehaviour": "White screen appears and upload never completes",
"environment": "Chrome and Safari browsers, Pro plan only",
"affectedUsers": "All Pro plan users uploading files over 10MB",
"labels": ["severity-high", "component-upload", "bug"]
},
"issue": {
"number": 847,
"url": "https://github.com/yourorg/your-repo/issues/847",
"title": "File upload fails with white screen for files over 10MB on Pro plan"
}
}Slack to GitHub Integration
Add a Slack slash command that creates GitHub issues directly from Slack:
js
// In your Slack Bolt app (from Tool 15)
app.command('/bug', async ({ command, ack, say }) => {
await ack();
const result = await convertBugToIssue(command.text, DEFAULT_OWNER, DEFAULT_REPO);
await say(`✅ Created GitHub issue #${result.issue.number}: ${result.issue.url}`);
});Build 50 AI Automation Tools — Tool 23 of 50
Bug-to-issue conversion is live. Continue to Tool 24 to build an AI API documentation generator.
Summary
- AI parsing structures any informal bug description into actionable, specific GitHub issues
- Label auto-creation ensures GitHub has the right labels without manual setup
- Severity inference from language patterns surfaces critical bugs immediately
- Slack integration lets anyone on the team create properly structured issues with a slash command
- The same extraction JSON works for Jira, Linear, or any other issue tracker — just swap the API call
Continue to Tool 24: API Documentation Generator →
Post Navigation (Previous/Next)
