{
  "id": "CCpAUiVj1gyJZJUw",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Code Review with Red-Marked Corrections in Spreadsheet",
  "tags": [],
  "nodes": [
    {
      "id": "4af3c4b2-b7ad-407f-8ead-8509bde98b71",
      "name": "\u30b3\u30fc\u30c9\u5165\u529b\u30b7\u30fc\u30c8\u76e3\u8996",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -624,
        0
      ],
      "parameters": {
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "<YOUR_SHEET_GID_OR_NAME>",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ABYuKFtVf1qgXa8S8yDNOKu7yCuDQzKPZQ-oufhsTJY/edit#gid=0",
          "cachedResultName": "\u5bfe\u8c61\u30b3\u30fc\u30c9"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "<YOUR_SPREADSHEET_ID>",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ABYuKFtVf1qgXa8S8yDNOKu7yCuDQzKPZQ-oufhsTJY/edit?usp=drivesdk",
          "cachedResultName": "\u30b3\u30fc\u30c9\u30ec\u30d3\u30e5\u30fc"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "47c973ae-f613-4a3c-ba53-0166e9a617a0",
      "name": "\u30ec\u30d3\u30e5\u30fc\u7d50\u679c\u6574\u5f62",
      "type": "n8n-nodes-base.set",
      "position": [
        464,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "reviewedCode",
              "type": "string",
              "value": "={{ $json.output }}"
            },
            {
              "id": "id-2",
              "name": "originalCode",
              "type": "string",
              "value": "={{ $('\u30b3\u30fc\u30c9\u5165\u529b\u30b7\u30fc\u30c8\u76e3\u8996').item.json.code }}"
            },
            {
              "id": "id-3",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "2bab7e4b-daab-4a05-83bd-2b60465077e9",
      "name": "\u30ec\u30d3\u30e5\u30fc\u7d50\u679c\u66f8\u304d\u8fbc\u307f",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        608,
        0
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "<YOUR_SHEET_GID_OR_NAME>",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ABYuKFtVf1qgXa8S8yDNOKu7yCuDQzKPZQ-oufhsTJY/edit#gid=1115979776",
          "cachedResultName": "\u30ec\u30d3\u30e5\u30fc\u7d50\u679c"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "<YOUR_SPREADSHEET_ID>",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ABYuKFtVf1qgXa8S8yDNOKu7yCuDQzKPZQ-oufhsTJY/edit?usp=drivesdk",
          "cachedResultName": "\u30b3\u30fc\u30c9\u30ec\u30d3\u30e5\u30fc"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "b6dda288-7b1b-41a0-9e88-f8cadc6499b6",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -176,
        0
      ],
      "parameters": {
        "text": "=Lint summary (consider in review):\n{{ JSON.stringify($('Lint Check').first().json.lintErrors) }}\n\nLint score: {{ $('Lint Check').first().json.lintScore }} / 10\n\n=\u3042\u306a\u305f\u306f\u30b3\u30fc\u30c9\u30ec\u30d3\u30e5\u30fc\u306e\u5c02\u9580\u5bb6\u3067\u3059\u3002\u4ee5\u4e0b\u306e\u30b3\u30fc\u30c9\u3092\u30ec\u30d3\u30e5\u30fc\u3057\u3001\u4fee\u6b63\u304c\u5fc5\u8981\u306a\u7b87\u6240\u3092\u8d64\u5b57\u3067\u793a\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u5143\u306e\u30b3\u30fc\u30c9\u306b\u5bfe\u3057\u3066\u3001\u4fee\u6b63\u6848\u3092\u8d64\u5b57\uff08HTML\u306e<span style=\"color:red\">\u30bf\u30b0\uff09\u3067\u8ffd\u8a18\u3057\u305f\u5f62\u5f0f\u3067\u51fa\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n{{ $json.reviewPrompt }}\n\n\u30b3\u30fc\u30c9:\n{{ $('\u30b3\u30fc\u30c9\u5165\u529b\u30b7\u30fc\u30c8\u76e3\u8996').item.json.code }}\n\n\u3010Review Rules\u3011\n- Classify issues as Critical / Major / Minor\n- Use <span style=\"color:red\">red</span> for critical fixes; <span style=\"color:orange\">orange</span> for minor suggestions\n- Append JSON at end: {\"overall_score\": <0-10 number>}\n",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 2.2
    },
    {
      "id": "05ee83db-4b56-43ee-b153-3ba224393f88",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -176,
        224
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "a4fd65ae-4f56-4b3a-be8c-29d2a0b171f8",
      "name": "Get row(s) in sheet in Google Sheets",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        -32,
        224
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "<YOUR_SHEET_GID_OR_NAME>",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ABYuKFtVf1qgXa8S8yDNOKu7yCuDQzKPZQ-oufhsTJY/edit#gid=2003594084",
          "cachedResultName": "\u30b3\u30fc\u30c9\u898f\u7d04"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "<YOUR_SPREADSHEET_ID>",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1ABYuKFtVf1qgXa8S8yDNOKu7yCuDQzKPZQ-oufhsTJY/edit?usp=drivesdk",
          "cachedResultName": "\u30b3\u30fc\u30c9\u30ec\u30d3\u30e5\u30fc"
        },
        "authentication": "serviceAccount",
        "descriptionType": "manual",
        "toolDescription": "\u8a18\u8f09\u3055\u308c\u308b\u5185\u5bb9\u3092\u3082\u3068\u306b\u30ec\u30d3\u30e5\u30fc"
      },
      "typeVersion": 4.7
    },
    {
      "id": "27a24123-0992-4af0-98f4-d37148cb6b5d",
      "name": "Sheet Trigger Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -688,
        -128
      ],
      "parameters": {
        "content": "Monitors the 'Input Code' sheet for new or edited rows to start the review process automatically."
      },
      "typeVersion": 1
    },
    {
      "id": "11e67e60-aaf1-49f0-a53c-93c9efde194d",
      "name": "AI Review Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -448,
        -128
      ],
      "parameters": {
        "content": "Sends the submitted code to the connected AI model (e.g., Gemini or GPT) for detailed review and feedback."
      },
      "typeVersion": 1
    },
    {
      "id": "e81e15e9-55fa-46c4-b4d1-4304b6bb975c",
      "name": "Formatting Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -192,
        -128
      ],
      "parameters": {
        "content": "Formats AI\u2019s review response \u2014 adds red-colored text for corrections and clear comments for improvements."
      },
      "typeVersion": 1
    },
    {
      "id": "d2569a9f-fa2c-4979-9137-a4182b425528",
      "name": "Write Output Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        176,
        -128
      ],
      "parameters": {
        "width": 528,
        "content": "Writes the reviewed and corrected code output into the 'Review Results' sheet for easy comparison."
      },
      "typeVersion": 1
    },
    {
      "id": "6cb8265d-a2a8-4542-80b6-358181a239ee",
      "name": "Lint Check",
      "type": "n8n-nodes-base.function",
      "position": [
        -400,
        0
      ],
      "parameters": {
        "functionCode": "const code = $('\u30b3\u30fc\u30c9\u5165\u529b\u30b7\u30fc\u30c8\u76e3\u8996').item.json.code || '';\nconst errors = [];\nif (code.includes('var ')) errors.push({type:'Major', msg:'Avoid var; use let/const.'});\nif (code.includes('console.log')) errors.push({type:'Minor', msg:'Remove console.log in production.'});\nconst open = (code.match(/\\{/g)||[]).length;\nconst close = (code.match(/\\}/g)||[]).length;\nif (open !== close) errors.push({type:'Critical', msg:`Brace imbalance: {=${open}} }=${close}`});\nconst score = Math.max(0, 10 - errors.length * 2);\nreturn [{ json: { code, lintErrors: errors, lintScore: score }}];\n"
      },
      "typeVersion": 1
    },
    {
      "id": "8850c4ee-5296-4a3c-aaca-d6bd9d4bdde2",
      "name": "Format Review Output",
      "type": "n8n-nodes-base.function",
      "position": [
        176,
        0
      ],
      "parameters": {
        "functionCode": "const out = $json.output || '';\nconst lint = $('Lint Check').first().json || {};\nconst errors = lint.lintErrors || [];\nconst counts = { Critical:0, Major:0, Minor:0 };\nerrors.forEach(e=>{ if (counts[e.type]!==undefined) counts[e.type]++; });\nlet overall =  Number((lint.lintScore || 0));\nconst m = out.match(/\\{\\\"overall_score\\\"\\s*:\\s*([0-9.]+)\\}/);\nif (m) overall = (overall + Number(m[1]))/2;\nreturn [{ json: { reviewed: out, lintSummary: counts, lintScore: lint.lintScore||0, overallScore: Number(overall.toFixed(2)) } }];\n"
      },
      "typeVersion": 1
    },
    {
      "id": "a04d8968-8941-440f-bce7-21f42eca1771",
      "name": "Aggregate Review Stats",
      "type": "n8n-nodes-base.function",
      "position": [
        320,
        0
      ],
      "parameters": {
        "functionCode": "const s = $json.lintSummary || {Critical:0, Major:0, Minor:0};\nconst overall = $json.overallScore || 0;\nreturn [{ json: { summary: s, overall, summaryText: `Critical:${s.Critical} Major:${s.Major} Minor:${s.Minor} / Score:${overall}` } }];\n"
      },
      "typeVersion": 1
    },
    {
      "id": "80ece42d-3536-4880-9a12-b745204e40b0",
      "name": "Post Review Summary",
      "type": "n8n-nodes-base.slack",
      "position": [
        832,
        0
      ],
      "parameters": {
        "text": "={{ `\u2705 Code Review Completed\\n${$json.summaryText}` }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "<YOUR_SLACK_CHANNEL_ID>",
          "cachedResultName": "code-reviews"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "9eb4292c-e03e-4e00-bbf5-8d6b19fb78e2",
      "name": "Template Overview (Advanced)",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1264,
        -368
      ],
      "parameters": {
        "width": 520,
        "height": 1120,
        "content": "## Advanced Code Review Automation (AI + Lint + Slack)\n\n### Who\u2019s it for\nFor software engineers, QA teams, and tech leads who want to **automate intelligent code reviews** with both AI-driven suggestions and rule-based linting \u2014 all managed in Google Sheets with instant Slack summaries.\n\n### How it works\nThis workflow performs a two-layer review system:\n1. **Lint Check**: Runs a lightweight static analysis to find common issues (e.g., use of `var`, `console.log`, unbalanced braces).\n2. **AI Review**: Sends valid code to Gemini AI, which provides human-like review feedback with severity classification (`Critical`, `Major`, `Minor`) and visual highlights (red/orange tags).\n3. **Formatter**: Combines lint and AI results, calculating an overall score (0\u201310).\n4. **Aggregator**: Summarizes results for quick comparison.\n5. **Google Sheets Writer**: Appends results to your review log.\n6. **Slack Notification**: Posts a concise summary (e.g., number of issues and average score) to your team\u2019s channel.\n\n### How to set up\n1. Connect **Google Sheets** and **Slack** credentials in n8n.\n2. Replace placeholders (`<YOUR_SPREADSHEET_ID>`, `<YOUR_SHEET_GID_OR_NAME>`, `<YOUR_SLACK_CHANNEL_ID>`).\n3. Adjust the AI review prompt or lint rules as needed.\n4. Activate the workflow \u2014 reviews will start automatically whenever new code is added to the sheet.\n\n### Requirements\n- Google Sheets and Slack integrations enabled\n- A configured AI node (Gemini, OpenAI, or compatible)\n- Proper permissions to write to your target Google Sheet\n\n### How to customize\n- Add more linting rules (naming conventions, spacing, forbidden APIs)\n- Extend the AI prompt for project-specific guidelines\n- Customize the Slack message formatting\n- Export analytics to a dashboard (e.g., Notion or Data Studio)\n\n### Why it\u2019s valuable\nThis workflow brings **realistic, team-oriented AI-assisted code review** to n8n \u2014 combining the speed of automated linting with the nuance of human-style feedback. It saves time, improves code quality, and keeps your team\u2019s review history transparent and centralized."
      },
      "typeVersion": 1
    },
    {
      "id": "3198d5f4-06c1-43a4-8e88-a76c9c67eff9",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        96,
        176
      ],
      "parameters": {
        "content": "Add coding conventions and design documents as tools."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "64769cf0-2122-4615-a487-6ea7fc3dc2b1",
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Format Review Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lint Check": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Review Output": {
      "main": [
        [
          {
            "node": "Aggregate Review Stats",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Review Stats": {
      "main": [
        [
          {
            "node": "\u30ec\u30d3\u30e5\u30fc\u7d50\u679c\u6574\u5f62",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "\u30ec\u30d3\u30e5\u30fc\u7d50\u679c\u6574\u5f62": {
      "main": [
        [
          {
            "node": "\u30ec\u30d3\u30e5\u30fc\u7d50\u679c\u66f8\u304d\u8fbc\u307f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u30b3\u30fc\u30c9\u5165\u529b\u30b7\u30fc\u30c8\u76e3\u8996": {
      "main": [
        [
          {
            "node": "Lint Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u30ec\u30d3\u30e5\u30fc\u7d50\u679c\u66f8\u304d\u8fbc\u307f": {
      "main": [
        [
          {
            "node": "Post Review Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet in Google Sheets": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  }
}