From d44385fc419a15e8192cc1d96e846c0b0d2c8c0a Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sat, 27 Dec 2025 18:23:08 +0000 Subject: [PATCH] refactor: modularize error-as-todo runner --- tools/refactoring/error-as-todo-refactor.ts | 452 ++---------------- tools/refactoring/error-as-todo/parse-args.ts | 42 ++ tools/refactoring/error-as-todo/reporting.ts | 97 ++++ tools/refactoring/error-as-todo/runner.ts | 126 +++++ tools/refactoring/error-as-todo/types.ts | 37 ++ 5 files changed, 338 insertions(+), 416 deletions(-) create mode 100644 tools/refactoring/error-as-todo/parse-args.ts create mode 100644 tools/refactoring/error-as-todo/reporting.ts create mode 100644 tools/refactoring/error-as-todo/runner.ts create mode 100644 tools/refactoring/error-as-todo/types.ts diff --git a/tools/refactoring/error-as-todo-refactor.ts b/tools/refactoring/error-as-todo-refactor.ts index 509010b96..2d58f5b53 100644 --- a/tools/refactoring/error-as-todo-refactor.ts +++ b/tools/refactoring/error-as-todo-refactor.ts @@ -1,438 +1,60 @@ #!/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 { buildSession, generateTodoReport, loadFilesFromReport, writeReports } from './error-as-todo/reporting' +import { parseArgs } from './error-as-todo/parse-args' +import { runRefactorSession } from './error-as-todo/runner' +import { Priority, TodoItem } from './error-as-todo/types' -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[] +function filterByPriority(files: string[], priority?: Priority) { + if (priority && priority !== 'all') { + console.log(`๐Ÿ“Œ Filtering for ${priority} priority files...`) + } + return files } -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 args = process.argv.slice(2) + const args = parseArgs(process.argv.slice(2)) + if (args.showHelp) return - 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') - process.exit(0) - } + const todos: TodoItem[] = [] + const addTodo = (todo: TodoItem) => todos.push(todo) - 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 limit = limitArg ? parseInt(limitArg.split('=')[1], 10) : undefined - const priority = args.find(a => ['high', 'medium', 'low', 'all'].includes(a)) - - const runner = new ErrorAsTodoRefactor({ dryRun, verbose }) - console.log('๐Ÿ“‹ Loading files from progress report...') - let files = await runner.loadFilesFromReport() + const files = filterByPriority(await loadFilesFromReport(addTodo), args.priority) 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...`) - } + console.log('\n' + '='.repeat(60)) + console.log('๐Ÿš€ STARTING REFACTOR SESSION') + console.log('='.repeat(60)) - await runner.run(files, limit) + const sessionTodos = await runRefactorSession({ + files, + dryRun: args.dryRun, + verbose: args.verbose, + limit: args.limit, + addTodo + }) + + const allTodos = [...todos, ...sessionTodos] + const filesProcessed = args.limit ? Math.min(args.limit, files.length) : files.length + + console.log('\n' + '='.repeat(60)) + console.log('๐Ÿ“‹ GENERATING TODO REPORT') + console.log('='.repeat(60) + '\n') + + const report = generateTodoReport(allTodos) + const session = buildSession(filesProcessed, allTodos) + await writeReports(session, report) console.log('\nโœจ Done! Check REFACTOR_TODOS.md for your action items.') } @@ -440,5 +62,3 @@ async function main() { if (require.main === module) { main().catch(console.error) } - -export { ErrorAsTodoRefactor } diff --git a/tools/refactoring/error-as-todo/parse-args.ts b/tools/refactoring/error-as-todo/parse-args.ts new file mode 100644 index 000000000..a8afba710 --- /dev/null +++ b/tools/refactoring/error-as-todo/parse-args.ts @@ -0,0 +1,42 @@ +import { Priority } from './types' + +export interface ParsedArgs { + dryRun: boolean + verbose: boolean + limit?: number + priority?: Priority + showHelp: boolean +} + +function 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') +} + +export function parseArgs(rawArgs: string[]): ParsedArgs { + const args = rawArgs.slice() + const showHelp = args.includes('--help') || args.includes('-h') + + if (showHelp) { + printHelp() + return { dryRun: false, verbose: false, showHelp: true } + } + + 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 limit = limitArg ? parseInt(limitArg.split('=')[1], 10) : undefined + const priority = args.find(a => ['high', 'medium', 'low', 'all'].includes(a)) as Priority | undefined + + return { dryRun, verbose, limit, priority, showHelp } +} diff --git a/tools/refactoring/error-as-todo/reporting.ts b/tools/refactoring/error-as-todo/reporting.ts new file mode 100644 index 000000000..23374cfce --- /dev/null +++ b/tools/refactoring/error-as-todo/reporting.ts @@ -0,0 +1,97 @@ +import * as fs from 'fs/promises' +import * as path from 'path' +import { RefactorSession, TodoItem } from './types' + +const PROGRESS_PATH = path.join(process.cwd(), 'docs/todo/LAMBDA_REFACTOR_PROGRESS.md') +const TODO_MARKDOWN_PATH = path.join(process.cwd(), 'docs/todo/REFACTOR_TODOS.md') +const TODO_JSON_PATH = path.join(process.cwd(), 'docs/todo/REFACTOR_TODOS.json') + +type TodoLogger = (todo: TodoItem) => void +const sectionDivider = '\n## ' + +export async function loadFilesFromReport(addTodo: TodoLogger): Promise { + try { + const content = await fs.readFile(PROGRESS_PATH, '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) { + addTodo({ + file: PROGRESS_PATH, + 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 [] + } +} + +export function generateTodoReport(todos: TodoItem[]): string { + const byCategory = todos.reduce>((acc, todo) => ({ ...acc, [todo.category]: (acc[todo.category] || 0) + 1 }), {}) + const bySeverity = todos.reduce>((acc, todo) => ({ ...acc, [todo.severity]: (acc[todo.severity] || 0) + 1 }), {}) + const append = (line: string) => (report += line) + let report = '# Lambda Refactoring TODO List\n\n' + append(`**Generated:** ${new Date().toISOString()}\n\n`) + append('## Summary\n\n') + append(`**Philosophy:** Errors are good - they're our TODO list! ๐ŸŽฏ\n\n`) + append(`- Total items: ${todos.length}\n- ๐Ÿ”ด High priority: ${bySeverity.high || 0}\n- ๐ŸŸก Medium priority: ${bySeverity.medium || 0}\n- ๐ŸŸข Low priority: ${bySeverity.low || 0}\n- ๐Ÿ’ก Successes: ${bySeverity.info || 0}\n\n`) + + append('## 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] || '๐Ÿ“‹' + append(`- ${emoji} ${category.replace(/_/g, ' ')}: ${count}\n`) + } + + for (const severity of ['high', 'medium', 'low', 'info'] as const) { + const items = todos.filter(t => t.severity === severity) + if (items.length === 0) continue + const emoji = { high: '๐Ÿ”ด', medium: '๐ŸŸก', low: '๐ŸŸข', info: '๐Ÿ’ก' }[severity] + append(`${sectionDivider}${emoji} ${severity.toUpperCase()} Priority\n\n`) + const byFile = items.reduce>((acc, todo) => ({ ...acc, [todo.file]: [...(acc[todo.file] || []), todo] }), {}) + for (const [file, todosForFile] of Object.entries(byFile)) { + append(`### \`${file}\`\n\n`) + todosForFile.forEach(todo => { + const categoryEmoji = { parse_error: '๐Ÿ”ง', type_error: '๐Ÿ“˜', import_error: '๐Ÿ“ฆ', test_failure: '๐Ÿงช', lint_warning: 'โœจ', manual_fix_needed: '๐Ÿ‘ท', success: 'โœ…' }[todo.category] || '๐Ÿ“‹' + append(`- [ ] ${categoryEmoji} **${todo.category.replace(/_/g, ' ')}**: ${todo.message}\n`) + if (todo.location) append(` - ๐Ÿ“ Location: \`${todo.location}\`\n`) + if (todo.suggestion) append(` - ๐Ÿ’ก Suggestion: ${todo.suggestion}\n`) + if (todo.relatedFiles && todo.relatedFiles.length > 0) append(` - ๐Ÿ“ Related files: ${todo.relatedFiles.length} files created\n`) + append('\n') + }) + } + } + + append(`${sectionDivider}Quick Fixes\n\n`) + append( + `### For "this" references:\n\`\`\`typescript\nconst result = this.helperMethod()\n\nimport { helperMethod } from './helper-method'\nconst result = helperMethod()\n\`\`\`\n\n` + + '### For import cleanup:\n```bash\nnpm run lint:fix\n```\n\n' + + '### For type errors:\n```bash\nnpm run typecheck\n```\n\n' + ) + + append('## Next Steps\n\n') + append(`1. Address high-priority items first (${bySeverity.high || 0} items)\n2. Fix "this" references in extracted functions\n3. Run \`npm run lint:fix\` to clean up imports\n4. Run \`npm run typecheck\` to verify types\n5. Run \`npm run test:unit\` to verify functionality\n6. Commit working batches incrementally\n\n`) + append('## Remember\n\n**Errors are good!** They\'re not failures - they\'re a TODO list telling us exactly what needs attention. โœจ\n') + return report +} + +export function buildSession(filesProcessed: number, todos: TodoItem[]): RefactorSession { + return { + timestamp: new Date().toISOString(), + filesProcessed, + successCount: todos.filter(t => t.category === 'success').length, + todosGenerated: todos.filter(t => t.category !== 'success').length, + todos + } +} + +export async function writeReports(session: RefactorSession, markdown: string): Promise { + await fs.writeFile(TODO_MARKDOWN_PATH, markdown, 'utf-8') + await fs.writeFile(TODO_JSON_PATH, JSON.stringify(session, null, 2), 'utf-8') + console.log(`โœ… TODO report saved: ${TODO_MARKDOWN_PATH}`) + console.log(`โœ… JSON data saved: ${TODO_JSON_PATH}`) +} diff --git a/tools/refactoring/error-as-todo/runner.ts b/tools/refactoring/error-as-todo/runner.ts new file mode 100644 index 000000000..9788f9c86 --- /dev/null +++ b/tools/refactoring/error-as-todo/runner.ts @@ -0,0 +1,126 @@ +import * as fs from 'fs/promises' +import { MultiLanguageLambdaRefactor } from '../multi-lang-refactor' +import { RunnerOptions, TodoItem } from './types' + +type TodoLogger = (todo: TodoItem) => void +interface RunnerContext extends RunnerOptions { addTodo: TodoLogger } +const createLogger = (verbose: boolean) => (message: string) => verbose && console.log(message) + +async function detectPostRefactorIssues(files: string[], addTodo: TodoLogger, logVerbose: (message: string) => void) { + logVerbose(' ๐Ÿ” Checking for common issues...') + for (const file of files) { + if (!file.endsWith('.ts')) continue + try { + const content = await fs.readFile(file, 'utf-8') + const checks: Array<[boolean, TodoItem]> = [ + [ + content.includes('this.'), + { + 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' + } + ], + [ + content.includes('import') && content.split('import').length > 10, + { + file, + category: 'import_error', + severity: 'low', + message: 'Many imports detected - may need optimization', + suggestion: 'Review imports and remove unused ones' + } + ], + [ + content.split('\n').length > 100, + { + file, + category: 'manual_fix_needed', + severity: 'medium', + message: 'Extracted function is still large - may need further breakdown', + suggestion: 'Consider breaking into smaller functions' + } + ] + ] + checks.filter(([condition]) => condition).forEach(([, todo]) => addTodo(todo)) + } catch (error) { + addTodo({ + file, + category: 'parse_error', + severity: 'medium', + message: `Unexpected issue reading file: ${error instanceof Error ? error.message : String(error)}`, + suggestion: 'Verify generated file is accessible' + }) + } + } +} + +async function refactorWithTodoCapture(context: RunnerContext, addTodo: TodoLogger) { + const { files, dryRun, limit, verbose } = context + const logVerbose = createLogger(verbose) + const targetFiles = limit ? files.slice(0, limit) : files + const refactor = new MultiLanguageLambdaRefactor({ dryRun, verbose: false }) + 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: ${targetFiles.length}\n`) + + for (let i = 0; i < targetFiles.length; i++) { + const file = targetFiles[i] + console.log(`\n[${i + 1}/${targetFiles.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(e => e.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') + result.errors.forEach(error => + 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, logVerbose) + } 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)) + } +} + +export async function runRefactorSession(context: RunnerContext): Promise { + const todos: TodoItem[] = [] + const forwardTodo: TodoLogger = todo => { + todos.push(todo) + context.addTodo(todo) + } + await refactorWithTodoCapture({ ...context, addTodo: forwardTodo }, forwardTodo) + + console.log('\n' + '='.repeat(60)) + console.log('๐Ÿ“Š SESSION SUMMARY') + console.log('='.repeat(60)) + const filesProcessed = context.limit ? Math.min(context.limit, context.files.length) : context.files.length + const countBy = (predicate: (todo: TodoItem) => boolean) => todos.filter(predicate).length + console.log(`Files processed: ${filesProcessed}`) + console.log(`โœ… Successes: ${countBy(t => t.category === 'success')}`) + console.log(`๐Ÿ“‹ TODOs generated: ${countBy(t => t.category !== 'success')}`) + console.log(` ๐Ÿ”ด High: ${countBy(t => t.severity === 'high')}`) + console.log(` ๐ŸŸก Medium: ${countBy(t => t.severity === 'medium')}`) + console.log(` ๐ŸŸข Low: ${countBy(t => t.severity === 'low')}`) + console.log('\n๐Ÿ’ก Remember: Errors are good! They tell us exactly what to fix.') + return todos +} diff --git a/tools/refactoring/error-as-todo/types.ts b/tools/refactoring/error-as-todo/types.ts new file mode 100644 index 000000000..ec12c1a52 --- /dev/null +++ b/tools/refactoring/error-as-todo/types.ts @@ -0,0 +1,37 @@ +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 interface RunnerOptions { + files: string[] + dryRun: boolean + verbose: boolean + limit?: number +} + +export type Priority = 'high' | 'medium' | 'low' | 'all'