Convert Code Analysis to Linear Tasks
Convert code analysis to Linear tasks
Purpose
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.
Usage
# 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"
Instructions
1. Scan for Task Markers
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
2. Parse Comment Context
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
}));
}
}
3. Group and Deduplicate
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;
}
}
4. Analyze Technical Debt
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;
}
}
5. Create Linear Tasks
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;
}
6. Generate Summary Report
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;
}
7. Error Handling
// 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
};
}
}
};
Example Output
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
Advanced Features
Custom Patterns
Define project-specific patterns:
# Add custom markers to scan
claude "Scan for REVIEW, QUESTION, and ASSUMPTION comments"
Integration with CI/CD
# Fail build if critical TODOs found
claude "Check for SECURITY or FIXME comments and exit with error if found"
Scheduled Scans
# Weekly technical debt report
claude "Generate weekly technical debt report and create tasks for new items"
Tips
- Run regularly to prevent TODO accumulation
- Use consistent comment formats across the team
- Include author and date in TODOs
- Link TODOs to existing Linear issues when possible
- Set up IDE snippets for properly formatted TODOs
- Review and close completed TODO tasks
- Use TODO comments as a quality gate in PR reviews