UNPKG

bugger-mcp

Version:

MCP Server for managing bugs, feature requests, and improvements

917 lines 38.5 kB
// Todo management operations for subtasks import { TokenUsageTracker } from './token-usage-tracker.js'; import { formatTokenUsage } from './format.js'; export class TodoManager { tokenTracker; constructor() { this.tokenTracker = TokenUsageTracker.getInstance(); } /** * Initialize todos database table */ async initTodosTable(db) { return new Promise((resolve, reject) => { const createTableQuery = ` CREATE TABLE IF NOT EXISTS todo_items ( id TEXT PRIMARY KEY, subtaskId TEXT NOT NULL, parentId TEXT NOT NULL, description TEXT NOT NULL, completed INTEGER DEFAULT 0, dateCreated TEXT NOT NULL, dateCompleted TEXT, orderIndex INTEGER ) `; db.run(createTableQuery, (err) => { if (err) { reject(new Error(`Failed to create todo_items table: ${err.message}`)); } else { resolve(); } }); }); } /** * Manage todos with various operations */ async manageTodos(db, args) { const { operation } = args; switch (operation) { case 'list': return this.listTodos(db, args); case 'create': return this.createTodo(db, args); case 'toggle': return this.toggleTodo(db, args); case 'update': return this.updateTodo(db, args); case 'delete': return this.deleteTodo(db, args); case 'bulk_toggle': return this.bulkToggleTodos(db, args); case 'get_completion': return this.getTodoCompletion(db, args); case 'generate_from_task': return this.generateTodosFromTask(db, args); default: throw new Error(`Unknown todo operation: ${operation}`); } } /** * List todos for a subtask (auto-generates if none exist) */ async listTodos(db, args) { this.tokenTracker.startOperation('list_todos'); const { subtaskId, parentId, showCompleted = true } = args; if (!subtaskId && !parentId) { throw new Error('Either subtaskId or parentId is required'); } try { let query = 'SELECT * FROM todo_items WHERE '; const params = []; if (subtaskId) { query += 'subtaskId = ?'; params.push(subtaskId); } else { query += 'parentId = ?'; params.push(parentId); } if (!showCompleted) { query += ' AND completed = 0'; } query += ' ORDER BY orderIndex ASC'; let todos = await new Promise((resolve, reject) => { db.all(query, params, (err, rows) => { if (err) { reject(new Error(`Failed to list todos: ${err.message}`)); } else { const todos = rows.map(row => ({ ...row, completed: row.completed === 1 })); resolve(todos); } }); }); // Auto-generate todos if none exist and we have a specific subtaskId if (todos.length === 0 && subtaskId) { todos = await this.autoGenerateTodos(db, subtaskId); } // Format output const formattedOutput = this.formatTodosList(todos, subtaskId || parentId); // Record token usage const inputText = JSON.stringify(args); const outputText = formattedOutput; const tokenUsage = this.tokenTracker.recordUsage(inputText, outputText, 'list_todos'); return `${outputText}${formatTokenUsage(tokenUsage)}`; } catch (error) { throw new Error(`Failed to list todos: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Create a new todo */ async createTodo(db, args) { this.tokenTracker.startOperation('create_todo'); const { subtaskId, description } = args; if (!subtaskId || !description) { throw new Error('subtaskId and description are required'); } try { // Get parent ID from subtask const parentId = await this.getParentIdFromSubtask(db, subtaskId); if (!parentId) { throw new Error(`Subtask ${subtaskId} not found`); } // Generate todo ID and get next order index const todoId = await this.generateNextTodoId(db, subtaskId); const orderIndex = await this.getNextTodoOrderIndex(db, subtaskId); const now = new Date().toISOString(); await new Promise((resolve, reject) => { const insertQuery = ` INSERT INTO todo_items ( id, subtaskId, parentId, description, completed, dateCreated, dateCompleted, orderIndex ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `; db.run(insertQuery, [ todoId, subtaskId, parentId, description, 0, // completed = false now, null, // dateCompleted orderIndex ], function (err) { if (err) { reject(new Error(`Failed to create todo: ${err.message}`)); } else { resolve(); } }); }); // Record token usage const inputText = JSON.stringify(args); const outputText = `Todo ${todoId} created successfully.`; const tokenUsage = this.tokenTracker.recordUsage(inputText, outputText, 'create_todo'); return `${outputText}${formatTokenUsage(tokenUsage)}`; } catch (error) { throw new Error(`Failed to create todo: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Toggle todo completion status */ async toggleTodo(db, args) { this.tokenTracker.startOperation('toggle_todo'); const { todoId } = args; if (!todoId) { throw new Error('todoId is required'); } try { // Get current status const currentTodo = await new Promise((resolve, reject) => { db.get('SELECT * FROM todo_items WHERE id = ?', [todoId], (err, row) => { if (err) { reject(new Error(`Failed to get todo: ${err.message}`)); } else if (!row) { reject(new Error(`Todo ${todoId} not found`)); } else { resolve(row); } }); }); const newCompleted = currentTodo.completed === 0 ? 1 : 0; const dateCompleted = newCompleted === 1 ? new Date().toISOString() : null; await new Promise((resolve, reject) => { const updateQuery = 'UPDATE todo_items SET completed = ?, dateCompleted = ? WHERE id = ?'; db.run(updateQuery, [newCompleted, dateCompleted, todoId], function (err) { if (err) { reject(new Error(`Failed to toggle todo: ${err.message}`)); } else { resolve(); } }); }); const status = newCompleted === 1 ? 'completed' : 'reopened'; // Record token usage const inputText = JSON.stringify(args); const outputText = `Todo ${todoId} ${status}.`; const tokenUsage = this.tokenTracker.recordUsage(inputText, outputText, 'toggle_todo'); return `${outputText}${formatTokenUsage(tokenUsage)}`; } catch (error) { throw new Error(`Failed to toggle todo: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Update todo description */ async updateTodo(db, args) { this.tokenTracker.startOperation('update_todo'); const { todoId, description } = args; if (!todoId || !description) { throw new Error('todoId and description are required'); } try { await new Promise((resolve, reject) => { db.run('UPDATE todo_items SET description = ? WHERE id = ?', [description, todoId], function (err) { if (err) { reject(new Error(`Failed to update todo: ${err.message}`)); } else if (this.changes === 0) { reject(new Error(`Todo ${todoId} not found`)); } else { resolve(); } }); }); // Record token usage const inputText = JSON.stringify(args); const outputText = `Todo ${todoId} updated successfully.`; const tokenUsage = this.tokenTracker.recordUsage(inputText, outputText, 'update_todo'); return `${outputText}${formatTokenUsage(tokenUsage)}`; } catch (error) { throw new Error(`Failed to update todo: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Delete a todo */ async deleteTodo(db, args) { this.tokenTracker.startOperation('delete_todo'); const { todoId } = args; if (!todoId) { throw new Error('todoId is required'); } try { await new Promise((resolve, reject) => { db.run('DELETE FROM todo_items WHERE id = ?', [todoId], function (err) { if (err) { reject(new Error(`Failed to delete todo: ${err.message}`)); } else if (this.changes === 0) { reject(new Error(`Todo ${todoId} not found`)); } else { resolve(); } }); }); // Record token usage const inputText = JSON.stringify(args); const outputText = `Todo ${todoId} deleted successfully.`; const tokenUsage = this.tokenTracker.recordUsage(inputText, outputText, 'delete_todo'); return `${outputText}${formatTokenUsage(tokenUsage)}`; } catch (error) { throw new Error(`Failed to delete todo: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Bulk toggle multiple todos */ async bulkToggleTodos(db, args) { this.tokenTracker.startOperation('bulk_toggle_todos'); const { todoIds, completed } = args; if (!todoIds || !Array.isArray(todoIds)) { throw new Error('todoIds array is required'); } if (completed === undefined) { throw new Error('completed status is required'); } try { const completedValue = completed ? 1 : 0; const dateCompleted = completed ? new Date().toISOString() : null; let updatedCount = 0; // Begin transaction await new Promise((resolve, reject) => { db.run('BEGIN TRANSACTION', (err) => { if (err) reject(err); else resolve(); }); }); // Update each todo for (const todoId of todoIds) { await new Promise((resolve, reject) => { db.run('UPDATE todo_items SET completed = ?, dateCompleted = ? WHERE id = ?', [completedValue, dateCompleted, todoId], function (err) { if (err) { reject(err); } else { updatedCount += this.changes; resolve(); } }); }); } // Commit transaction await new Promise((resolve, reject) => { db.run('COMMIT', (err) => { if (err) reject(err); else resolve(); }); }); const status = completed ? 'completed' : 'reopened'; // Record token usage const inputText = JSON.stringify(args); const outputText = `${updatedCount} todos ${status} successfully.`; const tokenUsage = this.tokenTracker.recordUsage(inputText, outputText, 'bulk_toggle_todos'); return `${outputText}${formatTokenUsage(tokenUsage)}`; } catch (error) { // Rollback on error await new Promise((resolve) => { db.run('ROLLBACK', () => resolve()); }); throw new Error(`Failed to bulk toggle todos: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Get todo completion statistics */ async getTodoCompletion(db, args) { this.tokenTracker.startOperation('get_todo_completion'); const { subtaskId, parentId } = args; if (!subtaskId && !parentId) { throw new Error('Either subtaskId or parentId is required'); } try { let query = ` SELECT COUNT(*) as total, SUM(CASE WHEN completed = 1 THEN 1 ELSE 0 END) as completed, SUM(CASE WHEN completed = 0 THEN 1 ELSE 0 END) as remaining FROM todo_items WHERE `; const params = []; if (subtaskId) { query += 'subtaskId = ?'; params.push(subtaskId); } else { query += 'parentId = ?'; params.push(parentId); } const stats = await new Promise((resolve, reject) => { db.get(query, params, (err, row) => { if (err) { reject(new Error(`Failed to get todo completion: ${err.message}`)); } else { resolve(row); } }); }); // Calculate completion percentage const completionPercentage = stats.total > 0 ? Math.round((stats.completed / stats.total) * 100) : 0; // Format output const target = subtaskId || parentId; const formattedOutput = this.formatCompletionStats(stats, completionPercentage, target); // Record token usage const inputText = JSON.stringify(args); const outputText = formattedOutput; const tokenUsage = this.tokenTracker.recordUsage(inputText, outputText, 'get_todo_completion'); return `${outputText}${formatTokenUsage(tokenUsage)}`; } catch (error) { throw new Error(`Failed to get todo completion: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Generate todos directly from a task (Bug/Feature/Improvement) */ async generateTodosFromTask(db, args) { this.tokenTracker.startOperation('generate_todos_from_task'); const { parentId, parentType } = args; if (!parentId || !parentType) { throw new Error('parentId and parentType are required'); } try { // Check if todos already exist for this task const existingTodos = await this.getExistingTodosForTask(db, parentId); if (existingTodos.length > 0) { return this.formatTodosList(existingTodos, parentId); } // Get parent task details const parentTask = await this.getParentTaskDetails(db, parentId, parentType); if (!parentTask) { throw new Error(`Task ${parentId} not found`); } // Generate todos based on task content const generatedTodos = this.generateTodosForTask(parentTask); // Create todos in database const createdTodos = []; for (let i = 0; i < generatedTodos.length; i++) { const todoData = generatedTodos[i]; const todoId = await this.generateNextTodoIdForTask(db, parentId); const now = new Date().toISOString(); const todo = { id: todoId, subtaskId: '', // Will be assigned when subtasks are generated parentId: parentId, description: todoData.description, completed: false, dateCreated: now, orderIndex: i }; // Insert into database await new Promise((resolve, reject) => { const insertQuery = ` INSERT INTO todo_items ( id, subtaskId, parentId, description, completed, dateCreated, dateCompleted, orderIndex ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `; db.run(insertQuery, [ todo.id, todo.subtaskId, todo.parentId, todo.description, 0, // completed = false todo.dateCreated, null, // dateCompleted todo.orderIndex ], function (err) { if (err) { reject(new Error(`Failed to create todo: ${err.message}`)); } else { resolve(); } }); }); createdTodos.push(todo); } // Format and return the list const formattedOutput = this.formatTodosList(createdTodos, parentId); // Record token usage const inputText = JSON.stringify(args); const outputText = formattedOutput; const tokenUsage = this.tokenTracker.recordUsage(inputText, outputText, 'generate_todos_from_task'); return `${outputText}${formatTokenUsage(tokenUsage)}`; } catch (error) { throw new Error(`Failed to generate todos from task: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // Helper methods /** * Auto-generate todos for a subtask based on its content */ async autoGenerateTodos(db, subtaskId, parentId, parentType, taskData) { try { // Get subtask details const subtask = await this.getSubtaskDetails(db, subtaskId); if (!subtask) { return []; } // Generate todos based on subtask content const generatedTodos = this.generateTodosForSubtask(subtask); // Create todos in database const createdTodos = []; for (let i = 0; i < generatedTodos.length; i++) { const todoData = generatedTodos[i]; const todoId = await this.generateNextTodoId(db, subtaskId); const now = new Date().toISOString(); const todo = { id: todoId, subtaskId: subtaskId, parentId: subtask.parentId, description: todoData.description, completed: false, dateCreated: now, orderIndex: i }; // Insert into database await new Promise((resolve, reject) => { const insertQuery = ` INSERT INTO todo_items ( id, subtaskId, parentId, description, completed, dateCreated, dateCompleted, orderIndex ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `; db.run(insertQuery, [ todo.id, todo.subtaskId, todo.parentId, todo.description, 0, // completed = false todo.dateCreated, null, // dateCompleted todo.orderIndex ], function (err) { if (err) { reject(new Error(`Failed to create auto-generated todo: ${err.message}`)); } else { resolve(); } }); }); createdTodos.push(todo); } return createdTodos; } catch (error) { console.error('Error auto-generating todos:', error); return []; } } /** * Get subtask details from database */ async getSubtaskDetails(db, subtaskId) { return new Promise((resolve, reject) => { const query = 'SELECT * FROM subtasks WHERE id = ?'; db.get(query, [subtaskId], (err, row) => { if (err) { reject(new Error(`Failed to get subtask: ${err.message}`)); } else { resolve(row); } }); }); } /** * Generate todos based on subtask content and type */ generateTodosForSubtask(subtask) { const title = subtask.title || ''; const description = subtask.description || ''; const content = `${title} ${description}`.toLowerCase(); // Common patterns for different types of subtasks if (content.includes('investigate') || content.includes('analyze') || content.includes('research')) { return [ { description: 'Review existing code and documentation' }, { description: 'Identify root cause of the issue' }, { description: 'Document findings and create reproduction steps' }, { description: 'Research potential solutions and approaches' } ]; } if (content.includes('design') || content.includes('architecture') || content.includes('plan')) { return [ { description: 'Define requirements and constraints' }, { description: 'Create technical design document' }, { description: 'Design API interfaces and data models' }, { description: 'Review design with team and get approval' } ]; } if (content.includes('implement') || content.includes('build') || content.includes('create')) { return [ { description: 'Set up development environment and dependencies' }, { description: 'Implement core functionality' }, { description: 'Add error handling and validation' }, { description: 'Perform initial testing and debugging' } ]; } if (content.includes('test') || content.includes('verify') || content.includes('validate')) { return [ { description: 'Write unit tests for new functionality' }, { description: 'Create integration tests' }, { description: 'Perform manual testing and edge case validation' }, { description: 'Update test documentation' } ]; } if (content.includes('performance') || content.includes('optimize') || content.includes('speed')) { return [ { description: 'Profile current performance and identify bottlenecks' }, { description: 'Implement performance optimizations' }, { description: 'Measure and compare performance improvements' }, { description: 'Document performance gains and best practices' } ]; } if (content.includes('ui') || content.includes('component') || content.includes('interface')) { return [ { description: 'Create component structure and basic layout' }, { description: 'Implement styling and responsive design' }, { description: 'Add interactive functionality and event handlers' }, { description: 'Test across different browsers and devices' } ]; } if (content.includes('database') || content.includes('storage') || content.includes('data')) { return [ { description: 'Design database schema and relationships' }, { description: 'Create migration scripts' }, { description: 'Implement data access layer' }, { description: 'Test data integrity and performance' } ]; } if (content.includes('documentation') || content.includes('guide') || content.includes('manual')) { return [ { description: 'Outline documentation structure and content' }, { description: 'Write technical documentation' }, { description: 'Create user guides and examples' }, { description: 'Review and publish documentation' } ]; } // Generic todos for unrecognized patterns return [ { description: 'Break down the task into smaller steps' }, { description: 'Complete the main implementation' }, { description: 'Test and validate the results' }, { description: 'Clean up and finalize the work' } ]; } /** * Get parent ID from subtask */ async getParentIdFromSubtask(db, subtaskId) { return new Promise((resolve, reject) => { db.get('SELECT parentId FROM subtasks WHERE id = ?', [subtaskId], (err, row) => { if (err) { reject(new Error(`Failed to get parent ID: ${err.message}`)); } else { resolve(row ? row.parentId : null); } }); }); } /** * Generate next todo ID */ async generateNextTodoId(db, subtaskId) { return new Promise((resolve, reject) => { const query = 'SELECT COUNT(*) as count FROM todo_items WHERE subtaskId = ?'; db.get(query, [subtaskId], (err, row) => { if (err) { reject(new Error(`Failed to generate todo ID: ${err.message}`)); } else { const nextNum = (row?.count || 0) + 1; resolve(`${subtaskId}-T${nextNum.toString().padStart(2, '0')}`); } }); }); } /** * Get next order index for todos in a subtask */ async getNextTodoOrderIndex(db, subtaskId) { return new Promise((resolve, reject) => { const query = 'SELECT MAX(orderIndex) as maxOrder FROM todo_items WHERE subtaskId = ?'; db.get(query, [subtaskId], (err, row) => { if (err) { reject(new Error(`Failed to get next todo order index: ${err.message}`)); } else { resolve((row?.maxOrder || -1) + 1); } }); }); } /** * Format todos list for display */ formatTodosList(todos, target) { if (todos.length === 0) { return `No todos found for ${target}.`; } let output = `Todos for ${target}:\n\n`; todos.forEach((todo, index) => { const checkbox = todo.completed ? '✅' : '☐'; output += `${index + 1}. ${checkbox} ${todo.description} [${todo.id}]\n`; if (todo.completed && todo.dateCompleted) { const completedDate = new Date(todo.dateCompleted).toLocaleDateString(); output += ` Completed: ${completedDate}\n`; } output += '\n'; }); return output; } /** * Format completion statistics */ formatCompletionStats(stats, completionPercentage, target) { let output = `Todo completion for ${target}:\n\n`; output += `Progress: ${completionPercentage}% (${stats.completed}/${stats.total})\n\n`; output += `Status:\n`; output += `- ✅ Completed: ${stats.completed}\n`; output += `- ☐ Remaining: ${stats.remaining}\n`; return output; } /** * Get existing todos for a task */ async getExistingTodosForTask(db, parentId) { return new Promise((resolve, reject) => { const query = 'SELECT * FROM todo_items WHERE parentId = ? ORDER BY orderIndex'; db.all(query, [parentId], (err, rows) => { if (err) { reject(new Error(`Failed to get existing todos: ${err.message}`)); } else { const todos = rows.map(row => ({ id: row.id, subtaskId: row.subtaskId, parentId: row.parentId, description: row.description, completed: row.completed === 1, dateCreated: row.dateCreated, dateCompleted: row.dateCompleted, orderIndex: row.orderIndex })); resolve(todos); } }); }); } /** * Get parent task details by ID and type */ async getParentTaskDetails(db, parentId, parentType) { return new Promise((resolve, reject) => { let table; switch (parentType) { case 'bug': table = 'bugs'; break; case 'feature': table = 'feature_requests'; break; case 'improvement': table = 'improvements'; break; default: reject(new Error(`Unknown parent type: ${parentType}`)); return; } const query = `SELECT *, '${parentType}' as type FROM ${table} WHERE id = ?`; db.get(query, [parentId], (err, row) => { if (err) { reject(new Error(`Failed to get parent task: ${err.message}`)); } else { resolve(row); } }); }); } /** * Generate todos based on task content (direct from task, not subtask) */ generateTodosForTask(parentTask) { const title = parentTask.title || ''; const description = parentTask.description || ''; const taskType = parentTask.type || ''; const content = `${title} ${description}`.toLowerCase(); // Type-specific patterns if (taskType === 'bug') { return this.generateBugTodos(content); } else if (taskType === 'feature') { return this.generateFeatureTodos(content); } else if (taskType === 'improvement') { return this.generateImprovementTodos(content); } // Generic fallback return [ { description: 'Analyze the requirements and current state' }, { description: 'Plan the implementation approach' }, { description: 'Implement the solution' }, { description: 'Test and validate the implementation' }, { description: 'Document the changes' } ]; } /** * Generate bug-specific todos */ generateBugTodos(content) { let todos = []; // Investigation phase todos.push({ description: 'Reproduce the issue and gather details' }); todos.push({ description: 'Analyze logs and error messages' }); // Root cause analysis if (content.includes('performance') || content.includes('slow')) { todos.push({ description: 'Profile performance and identify bottlenecks' }); todos.push({ description: 'Optimize slow queries or operations' }); } else if (content.includes('database') || content.includes('data')) { todos.push({ description: 'Check database integrity and indexes' }); todos.push({ description: 'Fix data consistency issues' }); } else if (content.includes('api') || content.includes('endpoint')) { todos.push({ description: 'Review API endpoint logic and validation' }); todos.push({ description: 'Fix API response handling' }); } else { todos.push({ description: 'Identify the root cause of the issue' }); todos.push({ description: 'Implement the fix' }); } // Testing and validation todos.push({ description: 'Write regression tests' }); todos.push({ description: 'Test the fix in different scenarios' }); todos.push({ description: 'Verify the fix resolves the original issue' }); return todos; } /** * Generate feature-specific todos */ generateFeatureTodos(content) { let todos = []; // Planning phase todos.push({ description: 'Define feature requirements and acceptance criteria' }); todos.push({ description: 'Design the feature architecture and user flow' }); // Implementation phase if (content.includes('ui') || content.includes('frontend') || content.includes('interface')) { todos.push({ description: 'Design the user interface mockups' }); todos.push({ description: 'Implement the frontend components' }); todos.push({ description: 'Add interactive functionality and event handlers' }); } else if (content.includes('api') || content.includes('backend')) { todos.push({ description: 'Design the API endpoints and data models' }); todos.push({ description: 'Implement the backend logic' }); todos.push({ description: 'Add input validation and error handling' }); } else if (content.includes('database') || content.includes('data')) { todos.push({ description: 'Design the database schema changes' }); todos.push({ description: 'Create migration scripts' }); todos.push({ description: 'Implement data access layer' }); } else { todos.push({ description: 'Implement the core feature functionality' }); todos.push({ description: 'Add proper error handling and validation' }); } // Testing and integration todos.push({ description: 'Write comprehensive tests for the feature' }); todos.push({ description: 'Integrate with existing systems' }); todos.push({ description: 'Test edge cases and error scenarios' }); todos.push({ description: 'Update documentation and user guides' }); return todos; } /** * Generate improvement-specific todos */ generateImprovementTodos(content) { let todos = []; // Analysis phase todos.push({ description: 'Analyze current implementation and identify pain points' }); todos.push({ description: 'Research best practices and alternative approaches' }); // Implementation phase if (content.includes('performance') || content.includes('optimize')) { todos.push({ description: 'Profile current performance metrics' }); todos.push({ description: 'Implement performance optimizations' }); todos.push({ description: 'Measure and compare performance improvements' }); } else if (content.includes('refactor') || content.includes('code')) { todos.push({ description: 'Refactor legacy code for better maintainability' }); todos.push({ description: 'Improve code organization and structure' }); todos.push({ description: 'Add better documentation and comments' }); } else if (content.includes('security') || content.includes('vulnerability')) { todos.push({ description: 'Identify security vulnerabilities' }); todos.push({ description: 'Implement security improvements' }); todos.push({ description: 'Perform security testing and validation' }); } else { todos.push({ description: 'Implement the improvement changes' }); todos.push({ description: 'Test the improvements' }); } // Validation and documentation todos.push({ description: 'Validate that improvements meet the goals' }); todos.push({ description: 'Update tests and documentation' }); todos.push({ description: 'Monitor the impact of changes' }); return todos; } /** * Generate next todo ID for a task */ async generateNextTodoIdForTask(db, parentId) { return new Promise((resolve, reject) => { const query = 'SELECT COUNT(*) as count FROM todo_items WHERE parentId = ?'; db.get(query, [parentId], (err, row) => { if (err) { reject(new Error(`Failed to count todos: ${err.message}`)); } else { const count = row.count + 1; const todoId = `${parentId}-T${count.toString().padStart(2, '0')}`; resolve(todoId); } }); }); } } //# sourceMappingURL=todos.js.map