Skip to content

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