diff --git a/tools/refactoring/README.md b/tools/refactoring/README.md index cde7bd6e9..b66b7faf1 100644 --- a/tools/refactoring/README.md +++ b/tools/refactoring/README.md @@ -76,6 +76,8 @@ npx tsx tools/refactoring/cli/refactor-to-lambda.ts Runs refactoring and treats all errors as actionable TODO items! +Modular building blocks now live under `tools/refactoring/error-as-todo-refactor/` with an `index.ts` re-export for easy imports in other scripts. + ```bash # Process files and generate TODO list npx tsx tools/refactoring/error-as-todo-refactor.ts high --limit=10 diff --git a/tools/refactoring/error-as-todo-refactor.ts b/tools/refactoring/error-as-todo-refactor.ts index 509010b96..4fc2d20ee 100644 --- a/tools/refactoring/error-as-todo-refactor.ts +++ b/tools/refactoring/error-as-todo-refactor.ts @@ -1,438 +1,51 @@ #!/usr/bin/env tsx -/** - * Error-as-TODO Refactoring Runner - * - * Runs refactoring and captures all errors/issues as actionable TODO items. - * Philosophy: Errors are good - they tell us what needs to be fixed! - */ -import { MultiLanguageLambdaRefactor } from './multi-lang-refactor' -import * as fs from 'fs/promises' -import * as path from 'path' +import { loadFilesFromReport, runErrorAsTodoRefactor } from './error-as-todo-refactor/index' +import type { TodoItem } from './error-as-todo-refactor/index' -interface TodoItem { - file: string - category: 'parse_error' | 'type_error' | 'import_error' | 'test_failure' | 'lint_warning' | 'manual_fix_needed' | 'success' - severity: 'high' | 'medium' | 'low' | 'info' - message: string - location?: string - suggestion?: string - relatedFiles?: string[] +const printHelp = () => { + console.log('Error-as-TODO Refactoring Runner\n') + console.log('Treats all errors as actionable TODO items!\n') + console.log('Usage: tsx error-as-todo-refactor.ts [options] [priority]\n') + console.log('Options:') + console.log(' -d, --dry-run Preview without writing') + console.log(' -v, --verbose Show detailed output') + console.log(' --limit=N Process only N files') + console.log(' high|medium|low Filter by priority') + console.log(' -h, --help Show help\n') + console.log('Examples:') + console.log(' tsx error-as-todo-refactor.ts high --limit=5') + console.log(' tsx error-as-todo-refactor.ts --dry-run medium') } -interface RefactorSession { - timestamp: string - filesProcessed: number - successCount: number - todosGenerated: number - todos: TodoItem[] -} - -class ErrorAsTodoRefactor { - private todos: TodoItem[] = [] - private dryRun: boolean - private verbose: boolean - - constructor(options: { dryRun?: boolean; verbose?: boolean } = {}) { - this.dryRun = options.dryRun || false - this.verbose = options.verbose || false - } - - private log(message: string) { - if (this.verbose) { - console.log(message) - } - } - - private addTodo(todo: TodoItem) { - this.todos.push(todo) - const emoji = { - high: '๐Ÿ”ด', - medium: '๐ŸŸก', - low: '๐ŸŸข', - info: '๐Ÿ’ก' - }[todo.severity] - - this.log(` ${emoji} TODO: ${todo.message}`) - } - - async loadFilesFromReport(): Promise { - try { - const reportPath = path.join(process.cwd(), 'docs/todo/LAMBDA_REFACTOR_PROGRESS.md') - const content = await fs.readFile(reportPath, 'utf-8') - - const files: string[] = [] - const lines = content.split('\n') - - for (const line of lines) { - if (line.includes('### Skipped')) break - const match = line.match(/- \[ \] `([^`]+)`/) - if (match) { - files.push(match[1]) - } - } - - return files - } catch (error) { - this.addTodo({ - file: 'docs/todo/LAMBDA_REFACTOR_PROGRESS.md', - category: 'parse_error', - severity: 'high', - message: 'Could not load progress report - run refactor-to-lambda.ts first', - suggestion: 'npx tsx tools/refactoring/cli/refactor-to-lambda.ts' - }) - return [] - } - } - - async refactorWithTodoCapture(files: string[]): Promise { - console.log('๐ŸŽฏ Error-as-TODO Refactoring Runner') - console.log(' Philosophy: Errors are good - they tell us what to fix!\n') - console.log(` Mode: ${this.dryRun ? '๐Ÿ” DRY RUN' : 'โšก LIVE'}`) - console.log(` Files: ${files.length}\n`) - - const refactor = new MultiLanguageLambdaRefactor({ dryRun: this.dryRun, verbose: false }) - - for (let i = 0; i < files.length; i++) { - const file = files[i] - console.log(`\n[${i + 1}/${files.length}] ๐Ÿ“ ${file}`) - - try { - // Check if file exists - try { - await fs.access(file) - } catch { - this.addTodo({ - file, - category: 'parse_error', - severity: 'low', - message: 'File not found - may have been moved or deleted', - suggestion: 'Update progress report or verify file location' - }) - continue - } - - // Attempt refactoring - const result = await refactor.refactorFile(file) - - if (result.success) { - console.log(' โœ… Refactored successfully') - this.addTodo({ - file, - category: 'success', - severity: 'info', - message: `Successfully refactored into ${result.newFiles.length} files`, - relatedFiles: result.newFiles - }) - } else if (result.errors.some(e => e.includes('skipping'))) { - console.log(' โญ๏ธ Skipped (not enough functions)') - this.addTodo({ - file, - category: 'manual_fix_needed', - severity: 'low', - message: 'File has < 3 functions - manual refactoring may not be needed', - suggestion: 'Review file to see if refactoring would add value' - }) - } else { - console.log(' โš ๏ธ Encountered issues') - for (const error of result.errors) { - this.addTodo({ - file, - category: 'parse_error', - severity: 'medium', - message: error, - suggestion: 'May need manual intervention or tool improvement' - }) - } - } - - // Check for common issues in refactored code - if (result.success && !this.dryRun) { - await this.detectPostRefactorIssues(file, result.newFiles) - } - - } catch (error) { - console.log(' โŒ Error occurred') - this.addTodo({ - file, - category: 'parse_error', - severity: 'high', - message: `Unexpected error: ${error instanceof Error ? error.message : String(error)}`, - suggestion: 'Report this error for tool improvement' - }) - } - - await new Promise(resolve => setTimeout(resolve, 50)) - } - } - - async detectPostRefactorIssues(originalFile: string, newFiles: string[]): Promise { - this.log(' ๐Ÿ” Checking for common issues...') - - // Check for 'this' references in extracted functions - for (const file of newFiles) { - if (!file.endsWith('.ts')) continue - - try { - const content = await fs.readFile(file, 'utf-8') - - // Check for 'this' keyword - if (content.includes('this.')) { - this.addTodo({ - file, - category: 'manual_fix_needed', - severity: 'high', - message: 'Contains "this" reference - needs manual conversion from class method to function', - location: file, - suggestion: 'Replace "this.methodName" with direct function calls or pass data as parameters' - }) - } - - // Check for missing imports - if (content.includes('import') && content.split('import').length > 10) { - this.addTodo({ - file, - category: 'import_error', - severity: 'low', - message: 'Many imports detected - may need optimization', - suggestion: 'Review imports and remove unused ones' - }) - } - - // Check file size (shouldn't be too large after refactoring) - const lines = content.split('\n').length - if (lines > 100) { - this.addTodo({ - file, - category: 'manual_fix_needed', - severity: 'medium', - message: `Extracted function is still ${lines} lines - may need further breakdown`, - suggestion: 'Consider breaking into smaller functions' - }) - } - - } catch (error) { - // File read error - already handled elsewhere - } - } - } - - generateTodoReport(): string { - const byCategory = this.todos.reduce((acc, todo) => { - acc[todo.category] = (acc[todo.category] || 0) + 1 - return acc - }, {} as Record) - - const bySeverity = this.todos.reduce((acc, todo) => { - acc[todo.severity] = (acc[todo.severity] || 0) + 1 - return acc - }, {} as Record) - - let report = '# Lambda Refactoring TODO List\n\n' - report += `**Generated:** ${new Date().toISOString()}\n\n` - report += `## Summary\n\n` - report += `**Philosophy:** Errors are good - they're our TODO list! ๐ŸŽฏ\n\n` - report += `- Total items: ${this.todos.length}\n` - report += `- ๐Ÿ”ด High priority: ${bySeverity.high || 0}\n` - report += `- ๐ŸŸก Medium priority: ${bySeverity.medium || 0}\n` - report += `- ๐ŸŸข Low priority: ${bySeverity.low || 0}\n` - report += `- ๐Ÿ’ก Successes: ${bySeverity.info || 0}\n\n` - - report += `## By Category\n\n` - for (const [category, count] of Object.entries(byCategory).sort((a, b) => b[1] - a[1])) { - const emoji = { - parse_error: '๐Ÿ”ง', - type_error: '๐Ÿ“˜', - import_error: '๐Ÿ“ฆ', - test_failure: '๐Ÿงช', - lint_warning: 'โœจ', - manual_fix_needed: '๐Ÿ‘ท', - success: 'โœ…' - }[category] || '๐Ÿ“‹' - - report += `- ${emoji} ${category.replace(/_/g, ' ')}: ${count}\n` - } - - // Group by severity - const severityOrder = ['high', 'medium', 'low', 'info'] as const - - for (const severity of severityOrder) { - const items = this.todos.filter(t => t.severity === severity) - if (items.length === 0) continue - - const emoji = { - high: '๐Ÿ”ด', - medium: '๐ŸŸก', - low: '๐ŸŸข', - info: '๐Ÿ’ก' - }[severity] - - report += `\n## ${emoji} ${severity.toUpperCase()} Priority\n\n` - - // Group by file - const byFile = items.reduce((acc, todo) => { - const file = todo.file - if (!acc[file]) acc[file] = [] - acc[file].push(todo) - return acc - }, {} as Record) - - for (const [file, todos] of Object.entries(byFile)) { - report += `### \`${file}\`\n\n` - - for (const todo of todos) { - const categoryEmoji = { - parse_error: '๐Ÿ”ง', - type_error: '๐Ÿ“˜', - import_error: '๐Ÿ“ฆ', - test_failure: '๐Ÿงช', - lint_warning: 'โœจ', - manual_fix_needed: '๐Ÿ‘ท', - success: 'โœ…' - }[todo.category] || '๐Ÿ“‹' - - report += `- [ ] ${categoryEmoji} **${todo.category.replace(/_/g, ' ')}**: ${todo.message}\n` - - if (todo.location) { - report += ` - ๐Ÿ“ Location: \`${todo.location}\`\n` - } - - if (todo.suggestion) { - report += ` - ๐Ÿ’ก Suggestion: ${todo.suggestion}\n` - } - - if (todo.relatedFiles && todo.relatedFiles.length > 0) { - report += ` - ๐Ÿ“ Related files: ${todo.relatedFiles.length} files created\n` - } - - report += '\n' - } - } - } - - report += `\n## Quick Fixes\n\n` - report += `### For "this" references:\n` - report += `\`\`\`typescript\n` - report += `// Before (in extracted function)\n` - report += `const result = this.helperMethod()\n\n` - report += `// After (convert to function call)\n` - report += `import { helperMethod } from './helper-method'\n` - report += `const result = helperMethod()\n` - report += `\`\`\`\n\n` - - report += `### For import cleanup:\n` - report += `\`\`\`bash\n` - report += `npm run lint:fix\n` - report += `\`\`\`\n\n` - - report += `### For type errors:\n` - report += `\`\`\`bash\n` - report += `npm run typecheck\n` - report += `\`\`\`\n\n` - - report += `## Next Steps\n\n` - report += `1. Address high-priority items first (${bySeverity.high || 0} items)\n` - report += `2. Fix "this" references in extracted functions\n` - report += `3. Run \`npm run lint:fix\` to clean up imports\n` - report += `4. Run \`npm run typecheck\` to verify types\n` - report += `5. Run \`npm run test:unit\` to verify functionality\n` - report += `6. Commit working batches incrementally\n\n` - - report += `## Remember\n\n` - report += `**Errors are good!** They're not failures - they're a TODO list telling us exactly what needs attention. โœจ\n` - - return report - } - - async run(files: string[], limitFiles?: number): Promise { - if (limitFiles) { - files = files.slice(0, limitFiles) - } - - await this.refactorWithTodoCapture(files) - - // Generate reports - console.log('\n' + '='.repeat(60)) - console.log('๐Ÿ“‹ GENERATING TODO REPORT') - console.log('='.repeat(60) + '\n') - - const report = this.generateTodoReport() - const todoPath = path.join(process.cwd(), 'docs/todo/REFACTOR_TODOS.md') - - await fs.writeFile(todoPath, report, 'utf-8') - console.log(`โœ… TODO report saved: ${todoPath}`) - - // Save JSON for programmatic access - const session: RefactorSession = { - timestamp: new Date().toISOString(), - filesProcessed: files.length, - successCount: this.todos.filter(t => t.category === 'success').length, - todosGenerated: this.todos.filter(t => t.category !== 'success').length, - todos: this.todos - } - - const jsonPath = path.join(process.cwd(), 'docs/todo/REFACTOR_TODOS.json') - await fs.writeFile(jsonPath, JSON.stringify(session, null, 2), 'utf-8') - console.log(`โœ… JSON data saved: ${jsonPath}`) - - // Summary - console.log('\n' + '='.repeat(60)) - console.log('๐Ÿ“Š SESSION SUMMARY') - console.log('='.repeat(60)) - console.log(`Files processed: ${files.length}`) - console.log(`โœ… Successes: ${session.successCount}`) - console.log(`๐Ÿ“‹ TODOs generated: ${session.todosGenerated}`) - console.log(` ๐Ÿ”ด High: ${this.todos.filter(t => t.severity === 'high').length}`) - console.log(` ๐ŸŸก Medium: ${this.todos.filter(t => t.severity === 'medium').length}`) - console.log(` ๐ŸŸข Low: ${this.todos.filter(t => t.severity === 'low').length}`) - - console.log('\n๐Ÿ’ก Remember: Errors are good! They tell us exactly what to fix.') - } -} - -// CLI -async function main() { +const main = async () => { const args = process.argv.slice(2) if (args.includes('--help') || args.includes('-h')) { - console.log('Error-as-TODO Refactoring Runner\n') - console.log('Treats all errors as actionable TODO items!\n') - console.log('Usage: tsx error-as-todo-refactor.ts [options] [priority]\n') - console.log('Options:') - console.log(' -d, --dry-run Preview without writing') - console.log(' -v, --verbose Show detailed output') - console.log(' --limit=N Process only N files') - console.log(' high|medium|low Filter by priority') - console.log(' -h, --help Show help\n') - console.log('Examples:') - console.log(' tsx error-as-todo-refactor.ts high --limit=5') - console.log(' tsx error-as-todo-refactor.ts --dry-run medium') + printHelp() process.exit(0) } const dryRun = args.includes('--dry-run') || args.includes('-d') const verbose = args.includes('--verbose') || args.includes('-v') - const limitArg = args.find(a => a.startsWith('--limit=')) + const limitArg = args.find(arg => arg.startsWith('--limit=')) const limit = limitArg ? parseInt(limitArg.split('=')[1], 10) : undefined - const priority = args.find(a => ['high', 'medium', 'low', 'all'].includes(a)) + const priority = args.find(arg => ['high', 'medium', 'low', 'all'].includes(arg)) - const runner = new ErrorAsTodoRefactor({ dryRun, verbose }) - console.log('๐Ÿ“‹ Loading files from progress report...') - let files = await runner.loadFilesFromReport() + const seedTodos: TodoItem[] = [] + const files = await loadFilesFromReport(todo => seedTodos.push(todo)) if (files.length === 0) { console.log('โŒ No files found. Run refactor-to-lambda.ts first.') process.exit(1) } - // Filter by priority if specified if (priority && priority !== 'all') { - // This would need the priority data from the report console.log(`๐Ÿ“Œ Filtering for ${priority} priority files...`) } - await runner.run(files, limit) + await runErrorAsTodoRefactor(files, { dryRun, verbose, limit, seedTodos }) console.log('\nโœจ Done! Check REFACTOR_TODOS.md for your action items.') } @@ -440,5 +53,3 @@ async function main() { if (require.main === module) { main().catch(console.error) } - -export { ErrorAsTodoRefactor } diff --git a/tools/refactoring/error-as-todo-refactor/index.ts b/tools/refactoring/error-as-todo-refactor/index.ts new file mode 100644 index 000000000..5a3d65bd7 --- /dev/null +++ b/tools/refactoring/error-as-todo-refactor/index.ts @@ -0,0 +1,163 @@ +import * as fs from 'fs/promises' +import * as path from 'path' + +import { MultiLanguageLambdaRefactor } from '../multi-lang-refactor' +import { loadFilesFromReport } from './load-files' +import { detectPostRefactorIssues } from './post-refactor-checks' +import { generateTodoReport } from './reporting' +import { AddTodo, RefactorSession, TodoItem } from './types' + +export interface ErrorAsTodoOptions { + dryRun?: boolean + verbose?: boolean + limit?: number + seedTodos?: TodoItem[] +} + +const createLogger = (verbose: boolean) => (message: string) => { + if (verbose) { + console.log(message) + } +} + +const createTodoRecorder = (verbose: boolean, seedTodos: TodoItem[] = []) => { + const todos: TodoItem[] = [...seedTodos] + const addTodo: AddTodo = todo => { + todos.push(todo) + const emoji = { + high: '๐Ÿ”ด', + medium: '๐ŸŸก', + low: '๐ŸŸข', + info: '๐Ÿ’ก' + }[todo.severity] + + if (verbose) { + console.log(` ${emoji} TODO: ${todo.message}`) + } + } + + return { todos, addTodo } +} + +const summarizeSession = (files: string[], todos: TodoItem[]): RefactorSession => ({ + timestamp: new Date().toISOString(), + filesProcessed: files.length, + successCount: todos.filter(t => t.category === 'success').length, + todosGenerated: todos.filter(t => t.category !== 'success').length, + todos +}) + +export const runErrorAsTodoRefactor = async ( + files: string[], + options: ErrorAsTodoOptions = {} +): Promise<{ todos: TodoItem[]; session: RefactorSession }> => { + const { dryRun = false, verbose = false, limit, seedTodos } = options + const log = createLogger(verbose) + const { todos, addTodo } = createTodoRecorder(verbose, seedTodos) + const refactor = new MultiLanguageLambdaRefactor({ dryRun, verbose: false }) + const selectedFiles = typeof limit === 'number' ? files.slice(0, limit) : files + + console.log('๐ŸŽฏ Error-as-TODO Refactoring Runner') + console.log(' Philosophy: Errors are good - they tell us what to fix!\n') + console.log(` Mode: ${dryRun ? '๐Ÿ” DRY RUN' : 'โšก LIVE'}`) + console.log(` Files: ${selectedFiles.length}\n`) + + for (let i = 0; i < selectedFiles.length; i++) { + const file = selectedFiles[i] + console.log(`\n[${i + 1}/${selectedFiles.length}] ๐Ÿ“ ${file}`) + + try { + try { + await fs.access(file) + } catch { + addTodo({ + file, + category: 'parse_error', + severity: 'low', + message: 'File not found - may have been moved or deleted', + suggestion: 'Update progress report or verify file location' + }) + continue + } + + const result = await refactor.refactorFile(file) + + if (result.success) { + console.log(' โœ… Refactored successfully') + addTodo({ + file, + category: 'success', + severity: 'info', + message: `Successfully refactored into ${result.newFiles.length} files`, + relatedFiles: result.newFiles + }) + } else if (result.errors.some(error => error.includes('skipping'))) { + console.log(' โญ๏ธ Skipped (not enough functions)') + addTodo({ + file, + category: 'manual_fix_needed', + severity: 'low', + message: 'File has < 3 functions - manual refactoring may not be needed', + suggestion: 'Review file to see if refactoring would add value' + }) + } else { + console.log(' โš ๏ธ Encountered issues') + for (const error of result.errors) { + addTodo({ + file, + category: 'parse_error', + severity: 'medium', + message: error, + suggestion: 'May need manual intervention or tool improvement' + }) + } + } + + if (result.success && !dryRun) { + await detectPostRefactorIssues(result.newFiles, addTodo, log) + } + } catch (error) { + console.log(' โŒ Error occurred') + addTodo({ + file, + category: 'parse_error', + severity: 'high', + message: `Unexpected error: ${error instanceof Error ? error.message : String(error)}`, + suggestion: 'Report this error for tool improvement' + }) + } + + await new Promise(resolve => setTimeout(resolve, 50)) + } + + console.log('\n' + '='.repeat(60)) + console.log('๐Ÿ“‹ GENERATING TODO REPORT') + console.log('='.repeat(60) + '\n') + + const report = generateTodoReport(todos) + const todoPath = path.join(process.cwd(), 'docs/todo/REFACTOR_TODOS.md') + await fs.writeFile(todoPath, report, 'utf-8') + console.log(`โœ… TODO report saved: ${todoPath}`) + + const session = summarizeSession(selectedFiles, todos) + const jsonPath = path.join(process.cwd(), 'docs/todo/REFACTOR_TODOS.json') + await fs.writeFile(jsonPath, JSON.stringify(session, null, 2), 'utf-8') + console.log(`โœ… JSON data saved: ${jsonPath}`) + + console.log('\n' + '='.repeat(60)) + console.log('๐Ÿ“Š SESSION SUMMARY') + console.log('='.repeat(60)) + console.log(`Files processed: ${selectedFiles.length}`) + console.log(`โœ… Successes: ${session.successCount}`) + console.log(`๐Ÿ“‹ TODOs generated: ${session.todosGenerated}`) + console.log(` ๐Ÿ”ด High: ${todos.filter(t => t.severity === 'high').length}`) + console.log(` ๐ŸŸก Medium: ${todos.filter(t => t.severity === 'medium').length}`) + console.log(` ๐ŸŸข Low: ${todos.filter(t => t.severity === 'low').length}`) + + console.log('\n๐Ÿ’ก Remember: Errors are good! They tell us exactly what to fix.') + + return { todos, session } +} + +export { detectPostRefactorIssues, generateTodoReport, loadFilesFromReport, runErrorAsTodoRefactor } +export type { AddTodo, RefactorSession, TodoItem } diff --git a/tools/refactoring/error-as-todo-refactor/load-files.ts b/tools/refactoring/error-as-todo-refactor/load-files.ts new file mode 100644 index 000000000..c02ac1722 --- /dev/null +++ b/tools/refactoring/error-as-todo-refactor/load-files.ts @@ -0,0 +1,37 @@ +import * as fs from 'fs/promises' +import * as path from 'path' + +import { AddTodo } from './types' + +const noop: AddTodo = () => undefined + +export const loadFilesFromReport = async ( + addTodo?: AddTodo, + reportPath = path.join(process.cwd(), 'docs/todo/LAMBDA_REFACTOR_PROGRESS.md') +): Promise => { + const recordTodo = addTodo ?? noop + + try { + const content = await fs.readFile(reportPath, 'utf-8') + const files: string[] = [] + + for (const line of content.split('\n')) { + if (line.includes('### Skipped')) break + const match = line.match(/- \[ \] `([^`]+)`/) + if (match) { + files.push(match[1]) + } + } + + return files + } catch (error) { + recordTodo({ + file: reportPath, + category: 'parse_error', + severity: 'high', + message: 'Could not load progress report - run refactor-to-lambda.ts first', + suggestion: 'npx tsx tools/refactoring/cli/refactor-to-lambda.ts' + }) + return [] + } +} diff --git a/tools/refactoring/error-as-todo-refactor/post-refactor-checks.ts b/tools/refactoring/error-as-todo-refactor/post-refactor-checks.ts new file mode 100644 index 000000000..8c549bafa --- /dev/null +++ b/tools/refactoring/error-as-todo-refactor/post-refactor-checks.ts @@ -0,0 +1,53 @@ +import * as fs from 'fs/promises' + +import { AddTodo } from './types' + +export const detectPostRefactorIssues = async ( + newFiles: string[], + addTodo: AddTodo, + log: (message: string) => void +): Promise => { + log(' ๐Ÿ” Checking for common issues...') + + for (const file of newFiles) { + if (!file.endsWith('.ts')) continue + + try { + const content = await fs.readFile(file, 'utf-8') + + if (content.includes('this.')) { + addTodo({ + file, + category: 'manual_fix_needed', + severity: 'high', + message: 'Contains "this" reference - needs manual conversion from class method to function', + location: file, + suggestion: 'Replace "this.methodName" with direct function calls or pass data as parameters' + }) + } + + if (content.includes('import') && content.split('import').length > 10) { + addTodo({ + file, + category: 'import_error', + severity: 'low', + message: 'Many imports detected - may need optimization', + suggestion: 'Review imports and remove unused ones' + }) + } + + const lines = content.split('\n').length + if (lines > 100) { + addTodo({ + file, + category: 'manual_fix_needed', + severity: 'medium', + message: `Extracted function is still ${lines} lines - may need further breakdown`, + suggestion: 'Consider breaking into smaller functions' + }) + } + } catch { + // File read errors are captured elsewhere in the flow + } + } +} diff --git a/tools/refactoring/error-as-todo-refactor/reporting.ts b/tools/refactoring/error-as-todo-refactor/reporting.ts new file mode 100644 index 000000000..a63707fe7 --- /dev/null +++ b/tools/refactoring/error-as-todo-refactor/reporting.ts @@ -0,0 +1,119 @@ +import { TodoItem } from './types' + +const severityEmoji: Record = { + high: '๐Ÿ”ด', + medium: '๐ŸŸก', + low: '๐ŸŸข', + info: '๐Ÿ’ก' +} + +const categoryEmoji: Record = { + parse_error: '๐Ÿ”ง', + type_error: '๐Ÿ“˜', + import_error: '๐Ÿ“ฆ', + test_failure: '๐Ÿงช', + lint_warning: 'โœจ', + manual_fix_needed: '๐Ÿ‘ท', + success: 'โœ…' +} + +export const generateTodoReport = (todos: TodoItem[]): string => { + const byCategory = todos.reduce((acc, todo) => { + acc[todo.category] = (acc[todo.category] || 0) + 1 + return acc + }, {} as Record) + + const bySeverity = todos.reduce((acc, todo) => { + acc[todo.severity] = (acc[todo.severity] || 0) + 1 + return acc + }, {} as Record) + + let report = '# Lambda Refactoring TODO List\n\n' + report += `**Generated:** ${new Date().toISOString()}\n\n` + report += `## Summary\n\n` + report += `**Philosophy:** Errors are good - they're our TODO list! ๐ŸŽฏ\n\n` + report += `- Total items: ${todos.length}\n` + report += `- ๐Ÿ”ด High priority: ${bySeverity.high || 0}\n` + report += `- ๐ŸŸก Medium priority: ${bySeverity.medium || 0}\n` + report += `- ๐ŸŸข Low priority: ${bySeverity.low || 0}\n` + report += `- ๐Ÿ’ก Successes: ${bySeverity.info || 0}\n\n` + + report += `## By Category\n\n` + for (const [category, count] of Object.entries(byCategory).sort((a, b) => b[1] - a[1])) { + const emoji = categoryEmoji[category as TodoItem['category']] || '๐Ÿ“‹' + report += `- ${emoji} ${category.replace(/_/g, ' ')}: ${count}\n` + } + + const severityOrder = ['high', 'medium', 'low', 'info'] as const + + for (const severity of severityOrder) { + const items = todos.filter(t => t.severity === severity) + if (items.length === 0) continue + + const emoji = severityEmoji[severity] + report += `\n## ${emoji} ${severity.toUpperCase()} Priority\n\n` + + const byFile = items.reduce((acc, todo) => { + const file = todo.file + if (!acc[file]) acc[file] = [] + acc[file].push(todo) + return acc + }, {} as Record) + + for (const [file, fileTodos] of Object.entries(byFile)) { + report += `### \`${file}\`\n\n` + + for (const todo of fileTodos) { + const emojiForCategory = categoryEmoji[todo.category] || '๐Ÿ“‹' + report += `- [ ] ${emojiForCategory} **${todo.category.replace(/_/g, ' ')}**: ${todo.message}\n` + + if (todo.location) { + report += ` - ๐Ÿ“ Location: \`${todo.location}\`\n` + } + + if (todo.suggestion) { + report += ` - ๐Ÿ’ก Suggestion: ${todo.suggestion}\n` + } + + if (todo.relatedFiles && todo.relatedFiles.length > 0) { + report += ` - ๐Ÿ“ Related files: ${todo.relatedFiles.length} files created\n` + } + + report += '\n' + } + } + } + + report += `\n## Quick Fixes\n\n` + report += `### For "this" references:\n` + report += `\`\`\`typescript\n` + report += `// Before (in extracted function)\n` + report += `const result = this.helperMethod()\n\n` + report += `// After (convert to function call)\n` + report += `import { helperMethod } from './helper-method'\n` + report += `const result = helperMethod()\n` + report += `\`\`\`\n\n` + + report += `### For import cleanup:\n` + report += `\`\`\`bash\n` + report += `npm run lint:fix\n` + report += `\`\`\`\n\n` + + report += `### For type errors:\n` + report += `\`\`\`bash\n` + report += `npm run typecheck\n` + report += `\`\`\`\n\n` + + report += `## Next Steps\n\n` + report += `1. Address high-priority items first (${bySeverity.high || 0} items)\n` + report += `2. Fix "this" references in extracted functions\n` + report += `3. Run \`npm run lint:fix\` to clean up imports\n` + report += `4. Run \`npm run typecheck\` to verify types\n` + report += `5. Run \`npm run test:unit\` to verify functionality\n` + report += `6. Commit working batches incrementally\n\n` + + report += `## Remember\n\n` + report += `**Errors are good!** They're not failures - they're a TODO list telling us exactly what needs attention. โœจ\n` + + return report +} diff --git a/tools/refactoring/error-as-todo-refactor/types.ts b/tools/refactoring/error-as-todo-refactor/types.ts new file mode 100644 index 000000000..8962c6d73 --- /dev/null +++ b/tools/refactoring/error-as-todo-refactor/types.ts @@ -0,0 +1,30 @@ +export type TodoCategory = + | 'parse_error' + | 'type_error' + | 'import_error' + | 'test_failure' + | 'lint_warning' + | 'manual_fix_needed' + | 'success' + +export type TodoSeverity = 'high' | 'medium' | 'low' | 'info' + +export interface TodoItem { + file: string + category: TodoCategory + severity: TodoSeverity + message: string + location?: string + suggestion?: string + relatedFiles?: string[] +} + +export interface RefactorSession { + timestamp: string + filesProcessed: number + successCount: number + todosGenerated: number + todos: TodoItem[] +} + +export type AddTodo = (todo: TodoItem) => void