Convert code analysis to Linear tasks
Converts code analysis into prioritized Linear tasks from TODO comments and technical debt markers.
/plugin marketplace add davepoon/buildwithclaude/plugin install commands-utilities-debugging@buildwithclaudeConvert code analysis to Linear tasks
This command scans your codebase for TODO/FIXME comments, technical debt markers, deprecated code, and other indicators that should be tracked as tasks. It automatically creates organized, prioritized Linear tasks to ensure important code improvements aren't forgotten.
# Scan entire codebase for TODOs and create tasks
claude "Create tasks from all TODO comments in the codebase"
# Scan specific directory or module
claude "Find TODOs in src/api and create Linear tasks"
# Create tasks from specific patterns
claude "Create tasks for all deprecated functions"
# Generate technical debt report
claude "Analyze technical debt in the project and create improvement tasks"
Search for common patterns indicating needed work:
# Find TODO comments
rg "TODO|FIXME|HACK|XXX|OPTIMIZE|REFACTOR" --type-add 'code:*.{js,ts,py,java,go,rb,php}' -t code
# Find deprecated markers
rg "@deprecated|DEPRECATED|@obsolete" -t code
# Find temporary code
rg "TEMPORARY|TEMP|REMOVE BEFORE|DELETE ME" -t code -i
# Find technical debt markers
rg "TECHNICAL DEBT|TECH DEBT|REFACTOR|NEEDS REFACTORING" -t code -i
# Find security concerns
rg "SECURITY|INSECURE|VULNERABILITY|CVE-" -t code -i
# Find performance issues
rg "SLOW|PERFORMANCE|OPTIMIZE|BOTTLENECK" -t code -i
Extract meaningful information from comments:
class CommentParser {
parseComment(file, lineNumber, comment) {
const parsed = {
type: 'todo',
priority: 'medium',
title: '',
description: '',
author: null,
date: null,
tags: [],
code_context: '',
file_path: file,
line_number: lineNumber
};
// Detect comment type
if (comment.match(/FIXME/i)) {
parsed.type = 'fixme';
parsed.priority = 'high';
} else if (comment.match(/HACK|XXX/i)) {
parsed.type = 'hack';
parsed.priority = 'high';
} else if (comment.match(/OPTIMIZE|PERFORMANCE/i)) {
parsed.type = 'optimization';
} else if (comment.match(/DEPRECATED/i)) {
parsed.type = 'deprecation';
parsed.priority = 'high';
} else if (comment.match(/SECURITY/i)) {
parsed.type = 'security';
parsed.priority = 'urgent';
}
// Extract author and date
const authorMatch = comment.match(/@(\w+)|by (\w+)/i);
if (authorMatch) {
parsed.author = authorMatch[1] || authorMatch[2];
}
const dateMatch = comment.match(/(\d{4}-\d{2}-\d{2})|(\d{1,2}\/\d{1,2}\/\d{2,4})/);
if (dateMatch) {
parsed.date = dateMatch[0];
}
// Extract title and description
const cleanComment = comment
.replace(/^\/\/\s*|^\/\*\s*|\*\/\s*$|^#\s*/g, '')
.replace(/TODO|FIXME|HACK|XXX/i, '')
.trim();
const parts = cleanComment.split(/[:\-āā]/);
if (parts.length > 1) {
parsed.title = parts[0].trim();
parsed.description = parts.slice(1).join(':').trim();
} else {
parsed.title = cleanComment;
}
// Extract tags
const tagMatch = comment.match(/#(\w+)/g);
if (tagMatch) {
parsed.tags = tagMatch.map(tag => tag.substring(1));
}
return parsed;
}
getCodeContext(file, lineNumber, contextLines = 5) {
const lines = readFileLines(file);
const start = Math.max(0, lineNumber - contextLines);
const end = Math.min(lines.length, lineNumber + contextLines);
return lines.slice(start, end).map((line, i) => ({
number: start + i + 1,
content: line,
isTarget: start + i + 1 === lineNumber
}));
}
}
Organize found issues intelligently:
class TaskGrouper {
groupTasks(parsedComments) {
const groups = {
byFile: new Map(),
byType: new Map(),
byAuthor: new Map(),
byModule: new Map()
};
for (const comment of parsedComments) {
// Group by file
if (!groups.byFile.has(comment.file_path)) {
groups.byFile.set(comment.file_path, []);
}
groups.byFile.get(comment.file_path).push(comment);
// Group by type
if (!groups.byType.has(comment.type)) {
groups.byType.set(comment.type, []);
}
groups.byType.get(comment.type).push(comment);
// Group by module
const module = this.extractModule(comment.file_path);
if (!groups.byModule.has(module)) {
groups.byModule.set(module, []);
}
groups.byModule.get(module).push(comment);
}
return groups;
}
mergeSimilarTasks(tasks) {
const merged = [];
const seen = new Set();
for (const task of tasks) {
if (seen.has(task)) continue;
// Find similar tasks
const similar = tasks.filter(t =>
t !== task &&
!seen.has(t) &&
this.areSimilar(task, t)
);
if (similar.length > 0) {
// Merge into one task
const mergedTask = {
...task,
title: this.generateMergedTitle(task, similar),
description: this.generateMergedDescription(task, similar),
locations: [task, ...similar].map(t => ({
file: t.file_path,
line: t.line_number
}))
};
merged.push(mergedTask);
seen.add(task);
similar.forEach(t => seen.add(t));
} else {
merged.push(task);
seen.add(task);
}
}
return merged;
}
}
Identify code quality issues:
class TechnicalDebtAnalyzer {
async analyzeFile(filePath) {
const issues = [];
const content = await readFile(filePath);
const lines = content.split('\n');
// Check for long functions
const functionMatches = content.matchAll(/function\s+(\w+)|(\w+)\s*=\s*\(.*?\)\s*=>/g);
for (const match of functionMatches) {
const functionName = match[1] || match[2];
const startLine = getLineNumber(content, match.index);
const functionLength = this.getFunctionLength(lines, startLine);
if (functionLength > 50) {
issues.push({
type: 'long_function',
severity: functionLength > 100 ? 'high' : 'medium',
title: `Refactor long function: ${functionName}`,
description: `Function ${functionName} is ${functionLength} lines long. Consider breaking it into smaller functions.`,
file_path: filePath,
line_number: startLine
});
}
}
// Check for duplicate code
const duplicates = await this.findDuplicateCode(filePath);
for (const dup of duplicates) {
issues.push({
type: 'duplicate_code',
severity: 'medium',
title: 'Remove duplicate code',
description: `Similar code found in ${dup.otherFile}:${dup.otherLine}`,
file_path: filePath,
line_number: dup.line
});
}
// Check for complex conditionals
const complexConditions = content.matchAll(/if\s*\([^)]{50,}\)/g);
for (const match of complexConditions) {
issues.push({
type: 'complex_condition',
severity: 'low',
title: 'Simplify complex conditional',
description: 'Consider extracting conditional logic into named variables or functions',
file_path: filePath,
line_number: getLineNumber(content, match.index)
});
}
// Check for outdated dependencies
if (filePath.endsWith('package.json')) {
const outdated = await this.checkOutdatedDependencies(filePath);
for (const dep of outdated) {
issues.push({
type: 'outdated_dependency',
severity: dep.major ? 'high' : 'low',
title: `Update ${dep.name} from ${dep.current} to ${dep.latest}`,
description: dep.major ? 'Major version update available' : 'Minor update available',
file_path: filePath
});
}
}
return issues;
}
}
Convert findings into actionable tasks:
async function createLinearTasks(groupedTasks, options = {}) {
const created = [];
const skipped = [];
// Check for existing tasks to avoid duplicates
const existingTasks = await linear.searchTasks('TODO OR FIXME');
const existingTitles = new Set(existingTasks.map(t => t.title));
// Create parent task for large groups
if (options.createEpic && groupedTasks.length > 10) {
const epic = await linear.createTask({
title: `Technical Debt: ${options.module || 'Codebase'} Cleanup`,
description: `Parent task for ${groupedTasks.length} code improvements`,
priority: 2,
labels: ['technical-debt', 'code-quality']
});
options.parentId = epic.id;
}
for (const task of groupedTasks) {
// Skip if similar task exists
if (existingTitles.has(task.title)) {
skipped.push({ task, reason: 'duplicate' });
continue;
}
// Build task description
const description = buildTaskDescription(task);
// Map priority
const priorityMap = {
urgent: 1,
high: 2,
medium: 3,
low: 4
};
try {
const linearTask = await linear.createTask({
title: task.title,
description,
priority: priorityMap[task.priority] || 3,
labels: getLabelsForTask(task),
parentId: options.parentId,
estimate: estimateTaskSize(task)
});
created.push({
linear: linearTask,
source: task
});
// Add code link as comment
await linear.createComment({
issueId: linearTask.id,
body: `š Code location: \`${task.file_path}:${task.line_number}\``
});
} catch (error) {
skipped.push({ task, reason: error.message });
}
}
return { created, skipped };
}
function buildTaskDescription(task) {
let description = task.description || '';
// Add code context
if (task.code_context) {
description += '\n\n### Code Context\n```\n';
task.code_context.forEach(line => {
const prefix = line.isTarget ? '>>> ' : ' ';
description += `${prefix}${line.number}: ${line.content}\n`;
});
description += '```\n';
}
// Add metadata
description += '\n\n### Details\n';
description += `- **Type**: ${task.type}\n`;
description += `- **File**: \`${task.file_path}\`\n`;
description += `- **Line**: ${task.line_number}\n`;
if (task.author) {
description += `- **Author**: @${task.author}\n`;
}
if (task.date) {
description += `- **Date**: ${task.date}\n`;
}
if (task.tags.length > 0) {
description += `- **Tags**: ${task.tags.join(', ')}\n`;
}
// Add suggestions
if (task.type === 'deprecated') {
description += '\n### Suggested Actions\n';
description += '1. Identify all usages of this deprecated code\n';
description += '2. Update to use the recommended alternative\n';
description += '3. Add deprecation warnings if not present\n';
description += '4. Schedule for removal in next major version\n';
}
return description;
}
Create overview of findings:
function generateReport(scanResults, createdTasks) {
const report = {
summary: {
totalFound: scanResults.length,
tasksCreated: createdTasks.created.length,
tasksSkipped: createdTasks.skipped.length,
byType: {},
byPriority: {},
byFile: {}
},
details: [],
recommendations: []
};
// Analyze distribution
for (const result of scanResults) {
report.summary.byType[result.type] = (report.summary.byType[result.type] || 0) + 1;
report.summary.byPriority[result.priority] = (report.summary.byPriority[result.priority] || 0) + 1;
}
// Generate recommendations
if (report.summary.byType.security > 0) {
report.recommendations.push({
priority: 'urgent',
action: 'Address security-related TODOs immediately',
tasks: scanResults.filter(r => r.type === 'security').length
});
}
if (report.summary.byType.deprecated > 5) {
report.recommendations.push({
priority: 'high',
action: 'Create deprecation removal sprint',
tasks: report.summary.byType.deprecated
});
}
return report;
}
// Handle access errors
try {
await scanDirectory(path);
} catch (error) {
if (error.code === 'EACCES') {
console.warn(`Skipping ${path} - permission denied`);
}
}
// Handle Linear API limits
const rateLimiter = {
tasksCreated: 0,
resetTime: Date.now() + 3600000,
async createTask(taskData) {
if (this.tasksCreated >= 50) {
console.log('Rate limit approaching, batching remaining tasks...');
// Create single task with list of TODOs
return this.createBatchTask(remainingTasks);
}
this.tasksCreated++;
return linear.createTask(taskData);
}
};
// Handle malformed comments
const safeParser = {
parse(comment) {
try {
return this.parseComment(comment);
} catch (error) {
return {
type: 'todo',
title: comment.substring(0, 50) + '...',
priority: 'low',
parseError: true
};
}
}
};
Scanning codebase for TODOs and technical debt...
š Scan Results:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Found 47 items across 23 files:
⢠24 TODOs
⢠8 FIXMEs
⢠5 Deprecated functions
⢠3 Security concerns
⢠7 Performance optimizations
š Breakdown by Priority:
š“ Urgent: 3 (security related)
š High: 13 (FIXMEs + deprecations)
š” Medium: 24 (standard TODOs)
š¢ Low: 7 (optimizations)
š Hotspot Files:
1. src/api/auth.js - 8 items
2. src/utils/validation.js - 6 items
3. src/models/User.js - 5 items
šØ Critical Findings:
1. SECURITY: Hardcoded API key
File: src/config/api.js:45
TODO: Remove hardcoded key and use env variable
ā Creating task with URGENT priority
2. DEPRECATED: Legacy authentication method
File: src/api/auth.js:120
Multiple usages found in 4 files
ā Creating migration task
3. FIXME: Race condition in concurrent updates
File: src/services/sync.js:78
Author: @alice (2024-01-03)
ā Creating high-priority bug task
š Task Creation Summary:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā
Created 32 Linear tasks:
- Epic: "Q1 Technical Debt Cleanup" (LIN-456)
- 3 urgent security tasks
- 10 high-priority fixes
- 19 medium-priority improvements
āļø Skipped 15 items:
- 8 duplicates (tasks already exist)
- 4 low-value comments (e.g., "TODO: think about this")
- 3 external dependencies (waiting on upstream)
š Estimates:
- Total story points: 89
- Estimated effort: 2-3 sprints
- Recommended team size: 2-3 developers
šÆ Recommended Actions:
1. Schedule security sprint immediately (3 urgent items)
2. Assign deprecation removal to next sprint (5 items)
3. Create coding standards to reduce future TODOs
4. Set up pre-commit hook to limit new TODOs
View all created tasks:
https://linear.app/yourteam/project/q1-technical-debt-cleanup
Define project-specific patterns:
# Add custom markers to scan
claude "Scan for REVIEW, QUESTION, and ASSUMPTION comments"
# Fail build if critical TODOs found
claude "Check for SECURITY or FIXME comments and exit with error if found"
# Weekly technical debt report
claude "Generate weekly technical debt report and create tasks for new items"