feat: map GitHub issue labels to task categories
## Changes - Automatically categorize tasks based on GitHub issue labels - Added label-to-category mapping function with comprehensive coverage ## Category Mapping - **bug_fix**: Issues labeled with bug, defect, error, fix - **security**: Issues labeled with security, vulnerability, cve - **performance**: Issues labeled with performance, optimization, speed - **ui_ux**: Issues labeled with ui, ux, design, styling - **infrastructure**: Issues labeled with infrastructure, devops, deployment, ci, cd - **testing**: Issues labeled with test, testing, qa - **refactoring**: Issues labeled with refactor, cleanup, maintenance, chore, tech-debt - **documentation**: Issues labeled with documentation, docs - **feature**: Default for enhancement, feature, improvement, or unlabeled issues ## Implementation - Updated `determineCategoryFromLabels()` to return proper TaskCategory types - Modified `createSpecForIssue()` to accept labels array parameter - Updated both investigation and import handlers to pass labels - Tasks now display with correct category badge in Kanban board ## Example - GitHub issue with "bug" label → Task category: "bug_fix" - GitHub issue with "enhancement" label → Task category: "feature" - GitHub issue with no labels → Task category: "feature" (default) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -47,11 +47,12 @@ export function registerImportIssues(agentManager: AgentManager): void {
|
||||
};
|
||||
|
||||
// Build description with metadata
|
||||
const labels = issue.labels.map(l => l.name).join(', ');
|
||||
const labelNames = issue.labels.map(l => l.name);
|
||||
const labelsString = labelNames.join(', ');
|
||||
const description = `# ${issue.title}
|
||||
|
||||
**GitHub Issue:** [#${issue.number}](${issue.html_url})
|
||||
${labels ? `**Labels:** ${labels}` : ''}
|
||||
${labelsString ? `**Labels:** ${labelsString}` : ''}
|
||||
|
||||
## Description
|
||||
|
||||
@@ -64,7 +65,8 @@ ${issue.body || 'No description provided.'}
|
||||
issue.number,
|
||||
issue.title,
|
||||
description,
|
||||
issue.html_url
|
||||
issue.html_url,
|
||||
labelNames
|
||||
);
|
||||
|
||||
// Start spec creation with the existing spec directory
|
||||
|
||||
@@ -146,7 +146,8 @@ export function registerInvestigateIssue(
|
||||
issue.number,
|
||||
issue.title,
|
||||
taskDescription,
|
||||
issue.html_url
|
||||
issue.html_url,
|
||||
labels
|
||||
);
|
||||
|
||||
// NOTE: We intentionally do NOT call agentManager.startSpecCreation() here
|
||||
|
||||
@@ -51,6 +51,58 @@ function slugifyTitle(title: string): string {
|
||||
.substring(0, 50);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine task category based on GitHub issue labels
|
||||
* Maps to TaskCategory type from shared/types/task.ts
|
||||
*/
|
||||
function determineCategoryFromLabels(labels: string[]): 'feature' | 'bug_fix' | 'refactoring' | 'documentation' | 'security' | 'performance' | 'ui_ux' | 'infrastructure' | 'testing' {
|
||||
const lowerLabels = labels.map(l => l.toLowerCase());
|
||||
|
||||
// Check for bug labels
|
||||
if (lowerLabels.some(l => l.includes('bug') || l.includes('defect') || l.includes('error') || l.includes('fix'))) {
|
||||
return 'bug_fix';
|
||||
}
|
||||
|
||||
// Check for security labels
|
||||
if (lowerLabels.some(l => l.includes('security') || l.includes('vulnerability') || l.includes('cve'))) {
|
||||
return 'security';
|
||||
}
|
||||
|
||||
// Check for performance labels
|
||||
if (lowerLabels.some(l => l.includes('performance') || l.includes('optimization') || l.includes('speed'))) {
|
||||
return 'performance';
|
||||
}
|
||||
|
||||
// Check for UI/UX labels
|
||||
if (lowerLabels.some(l => l.includes('ui') || l.includes('ux') || l.includes('design') || l.includes('styling'))) {
|
||||
return 'ui_ux';
|
||||
}
|
||||
|
||||
// Check for infrastructure labels
|
||||
if (lowerLabels.some(l => l.includes('infrastructure') || l.includes('devops') || l.includes('deployment') || l.includes('ci') || l.includes('cd'))) {
|
||||
return 'infrastructure';
|
||||
}
|
||||
|
||||
// Check for testing labels
|
||||
if (lowerLabels.some(l => l.includes('test') || l.includes('testing') || l.includes('qa'))) {
|
||||
return 'testing';
|
||||
}
|
||||
|
||||
// Check for refactoring labels
|
||||
if (lowerLabels.some(l => l.includes('refactor') || l.includes('cleanup') || l.includes('maintenance') || l.includes('chore') || l.includes('tech-debt') || l.includes('technical debt'))) {
|
||||
return 'refactoring';
|
||||
}
|
||||
|
||||
// Check for documentation labels
|
||||
if (lowerLabels.some(l => l.includes('documentation') || l.includes('docs'))) {
|
||||
return 'documentation';
|
||||
}
|
||||
|
||||
// Check for enhancement/feature labels (default)
|
||||
// This catches 'enhancement', 'feature', 'improvement', or any unlabeled issues
|
||||
return 'feature';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new spec directory and initial files
|
||||
*/
|
||||
@@ -59,7 +111,8 @@ export function createSpecForIssue(
|
||||
issueNumber: number,
|
||||
issueTitle: string,
|
||||
taskDescription: string,
|
||||
githubUrl: string
|
||||
githubUrl: string,
|
||||
labels: string[] = []
|
||||
): SpecCreationData {
|
||||
const specsBaseDir = getSpecsDir(project.autoBuildPath);
|
||||
const specsDir = path.join(project.path, specsBaseDir);
|
||||
@@ -104,12 +157,15 @@ export function createSpecForIssue(
|
||||
JSON.stringify(requirements, null, 2)
|
||||
);
|
||||
|
||||
// Determine category from GitHub issue labels
|
||||
const category = determineCategoryFromLabels(labels);
|
||||
|
||||
// task_metadata.json
|
||||
const metadata: TaskMetadata = {
|
||||
sourceType: 'github',
|
||||
githubIssueNumber: issueNumber,
|
||||
githubUrl,
|
||||
category: 'feature'
|
||||
category
|
||||
};
|
||||
writeFileSync(
|
||||
path.join(specDir, 'task_metadata.json'),
|
||||
|
||||
Reference in New Issue
Block a user