{ "name": "Todo-Based MVP Builder", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "todo-mvp-builder", "responseMode": "responseNode", "options": {} }, "name": "Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 1, "position": [240, 300] }, { "parameters": { "functionCode": "const payload = $json.body || $json;\n\nconst repoName = payload.repository?.name || 'unknown';\nconst repoFullName = payload.repository?.full_name || 'unknown';\nconst repoCloneUrl = payload.repository?.clone_url || '';\nconst branch = payload.ref?.replace('refs/heads/', '') || 'main';\nconst commitSha = payload.after || '';\nconst commitMessage = payload.head_commit?.message || payload.commits?.[0]?.message || 'No message';\nconst pusher = payload.pusher?.name || payload.pusher?.username || 'unknown';\n\n// Check if this is an initial MVP prompt\nconst isInitialPush = commitMessage.startsWith('MVP Prompt:');\n\n// Extract prompt from commit message\nfunction extractPrompt(message) {\n const match = message.match(/MVP Prompt:\\s*(.+)/i);\n return match ? match[1].trim() : message;\n}\n\nreturn {\n repo_name: repoName,\n repo_full_name: repoFullName,\n repo_clone_url: repoCloneUrl,\n branch: branch,\n commit_sha: commitSha,\n commit_message: commitMessage,\n pusher: pusher,\n is_initial_push: isInitialPush,\n prompt: extractPrompt(commitMessage),\n timestamp: new Date().toISOString(),\n status: 'READY'\n};" }, "name": "Extract Repo Info", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [460, 300] }, { "parameters": { "functionCode": "const repoInfo = $json;\nconst workflow = $workflow;\n\n// Initialize or get todos\nworkflow.staticData = workflow.staticData || {};\nworkflow.staticData.todos = workflow.staticData.todos || {};\n\nif (repoInfo.is_initial_push) {\n // First push - extract prompt and prepare to create todos\n const prompt = repoInfo.prompt;\n\n // Store initial state\n workflow.staticData.todos.status = 'CREATING_TODOS';\n workflow.staticData.todos.prompt = prompt;\n workflow.staticData.todos.start_time = new Date().toISOString();\n\n return {\n ...repoInfo,\n action: 'create_todos',\n status: 'CREATING_TODOS',\n message: 'Initial MVP prompt detected, will create todos next'\n };\n} else if (workflow.staticData.todos.current_index !== undefined) {\n // Continue with existing todos\n const index = workflow.staticData.todos.current_index || 0;\n const todos = workflow.staticData.todos.list || [];\n\n if (index < todos.length) {\n const nextTodo = todos[index];\n\n return {\n ...repoInfo,\n action: 'execute_todo',\n todo: nextTodo,\n index: index,\n total: todos.length,\n status: 'IN_PROGRESS',\n message: `Executing todo ${index + 1}/${todos.length}: ${nextTodo.title}`\n };\n } else {\n // All todos complete\n workflow.staticData.todos.status = 'COMPLETE';\n return {\n ...repoInfo,\n action: 'complete',\n status: 'SUCCESS',\n message: 'All todos completed successfully',\n total_todos: todos.length,\n completed_at: new Date().toISOString()\n };\n }\n} else {\n // No todos found\n return {\n ...repoInfo,\n action: 'error',\n status: 'ERROR',\n message: 'No todos found. Please push MVP prompt first.',\n error: 'NO_TODOS'\n };\n}" }, "name": "Get Next Todo", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [680, 300] }, { "parameters": { "functionCode": "const todoData = $json;\nconst repoInfo = $node[\"Extract Repo Info\"].json;\nconst workflow = $workflow;\n\n// Handle different actions\nif (todoData.action === 'create_todos') {\n // TODO: Call OpenHands to create TODO.md\n // For now, return placeholder\n const createTodosTask = `\nAnalyze this MVP prompt: \"${todoData.prompt}\"\n\nCreate a comprehensive TODO.md file with development tasks.\nEach task should be atomic and executable.\n\nReturn the TODO.md content as JSON.\n `;\n\n // Store in staticData for next iteration\n workflow.staticData.todos.pending_task = createTodosTask;\n workflow.staticData.todos.current_index = 0;\n\n return {\n ...todoData,\n action: 'next_todo',\n message: 'Todo creation task prepared, executing next...',\n status: 'CREATING_TODOS',\n sdk_task: createTodosTask\n };\n\n} else if (todoData.action === 'execute_todo') {\n // TODO: Execute the current todo with OpenHands SDK\n // For now, return placeholder\n\n const task = `\nExecute this development task:\n\n**Task:** ${todoData.todo.title}\n**Description:** ${todoData.todo.description}\n**Category:** ${todoData.todo.category}\n\n**Steps:**\n1. Create/modify the required files\n2. Run the specified commands\n3. Ensure the expected outcome is achieved\n4. If tests fail, fix them\n5. Commit your changes\n\n**Files to work with:**\n${todoData.todo.files?.join(', ') || 'TBD'}\n\n**Commands to run:**\n${todoData.todo.commands?.join('\\n') || 'TBD'}\n\n**Expected outcome:**\n${todoData.todo.expected_outcome || 'TBD'}\n\nCurrent directory: /workspace/${repoInfo.repo_name}\n `;\n\n // Store result placeholder\n workflow.staticData.todos.results = workflow.staticData.todos.results || [];\n workflow.staticData.todos.results.push({\n todo: todoData.todo,\n output: { success: true, message: 'Placeholder execution' },\n success: true,\n timestamp: new Date().toISOString()\n });\n\n // Increment index for next iteration\n workflow.staticData.todos.current_index++;\n\n return {\n ...todoData,\n action: 'todo_executed',\n todo: todoData.todo,\n index: todoData.index,\n success: true,\n status: 'EXECUTED',\n message: `Todo \"${todoData.todo.title}\" executed successfully`,\n sdk_output: { success: true, message: 'Placeholder output' },\n next_action: 'test'\n };\n} else {\n return {\n ...todoData,\n status: 'ERROR',\n message: 'Unknown action: ' + todoData.action\n };\n}" }, "name": "Execute Todo", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [900, 300] }, { "parameters": { "functionCode": "const executeResult = $json;\n\nif (executeResult.success || executeResult.action === 'create_todos') {\n // Create or execute succeeded\n const commitEmoji = executeResult.action === 'create_todos' ? '📋' : '✅';\n const commitMessage = executeResult.action === 'create_todos'\n ? `${commitEmoji} TODOs created from MVP prompt`\n : `${commitEmoji} Complete: ${executeResult.todo?.title || 'Task'}`;\n\n return {\n ...executeResult,\n status: 'SUCCESS',\n test_status: 'PASSED',\n commit_message: commitMessage,\n commit_emoji: commitEmoji,\n\n // For Gitea status\n state: 'success',\n description: executeResult.action === 'create_todos'\n ? 'TODOs created from prompt'\n : `Todo ${executeResult.index + 1}/${executeResult.total}: ${executeResult.todo?.title}`,\n should_continue: true\n };\n} else {\n // Execution failed but continue for debugging\n const commitEmoji = '❌';\n\n return {\n ...executeResult,\n status: 'FAILED',\n test_status: 'FAILED',\n commit_message: `${commitEmoji} Failed: ${executeResult.todo?.title || 'Task'}`,\n commit_emoji: commitEmoji,\n\n state: 'failure',\n description: `Todo ${executeResult.index + 1}/${executeResult.total}: ${executeResult.todo?.title} - FAILED`,\n error: executeResult.output?.error || 'Unknown error',\n should_continue: true // Continue for debugging\n };\n}" }, "name": "Test Changes", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1120, 300] }, { "parameters": { "functionCode": "const testResult = $json;\nconst repoInfo = $node[\"Extract Repo Info\"].json;\nconst workflow = $workflow;\n\n// TODO: Implement actual Gitea commit\n// For now, just format the response\n\nconst result = {\n ...testResult,\n loop: testResult.should_continue,\n should_continue: testResult.should_continue,\n next_iteration: testResult.should_continue ? 'Get Next Todo' : null,\n final_status: !testResult.should_continue ? 'COMPLETE' : 'CONTINUING'\n};\n\n// Log what would be committed\nconsole.log('Would commit to Gitea:', {\n repo: repoInfo.repo_full_name,\n message: testResult.commit_message,\n state: testResult.state\n});\n\nreturn result;" }, "name": "Commit & Push", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1340, 300] } ], "connections": { "Webhook": { "main": [ [ { "node": "Extract Repo Info", "type": "main", "index": 0 } ] ] }, "Extract Repo Info": { "main": [ [ { "node": "Get Next Todo", "type": "main", "index": 0 } ] ] }, "Get Next Todo": { "main": [ [ { "node": "Execute Todo", "type": "main", "index": 0 } ] ] }, "Execute Todo": { "main": [ [ { "node": "Test Changes", "type": "main", "index": 0 } ] ] }, "Test Changes": { "main": [ [ { "node": "Commit & Push", "type": "main", "index": 0 } ] ] }, "Commit & Push": { "main": [ [ { "node": "Get Next Todo", "type": "main", "index": 0 } ] ] } }, "staticData": {}, "settings": { "executionOrder": "v1" } }