583 lines
16 KiB
Markdown
583 lines
16 KiB
Markdown
# 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
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
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
|
|
```javascript
|
|
// 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:
|
|
```bash
|
|
# Add custom markers to scan
|
|
claude "Scan for REVIEW, QUESTION, and ASSUMPTION comments"
|
|
```
|
|
|
|
### Integration with CI/CD
|
|
```bash
|
|
# Fail build if critical TODOs found
|
|
claude "Check for SECURITY or FIXME comments and exit with error if found"
|
|
```
|
|
|
|
### Scheduled Scans
|
|
```bash
|
|
# 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 |