mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 22:04:56 +00:00
Merge pull request #211 from johndoe6345789/codex/refactor-error-as-todo-refactor.ts
Refactor error-as-todo runner into modular components
This commit is contained in:
42
tools/refactoring/error-as-todo/parse-args.ts
Normal file
42
tools/refactoring/error-as-todo/parse-args.ts
Normal file
@@ -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 }
|
||||
}
|
||||
97
tools/refactoring/error-as-todo/reporting.ts
Normal file
97
tools/refactoring/error-as-todo/reporting.ts
Normal file
@@ -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<string[]> {
|
||||
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<Record<string, number>>((acc, todo) => ({ ...acc, [todo.category]: (acc[todo.category] || 0) + 1 }), {})
|
||||
const bySeverity = todos.reduce<Record<string, number>>((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<Record<string, TodoItem[]>>((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<void> {
|
||||
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}`)
|
||||
}
|
||||
126
tools/refactoring/error-as-todo/runner.ts
Normal file
126
tools/refactoring/error-as-todo/runner.ts
Normal file
@@ -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<TodoItem[]> {
|
||||
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
|
||||
}
|
||||
37
tools/refactoring/error-as-todo/types.ts
Normal file
37
tools/refactoring/error-as-todo/types.ts
Normal file
@@ -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'
|
||||
Reference in New Issue
Block a user