{
  "name": "jump-section: Auto Fix Pipeline",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "pr-auto-fix",
        "responseMode": "onReceived",
        "responseData": "noData"
      },
      "id": "webhook-trigger",
      "name": "Webhook: PR Review Event",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1.1,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false
          },
          "conditions": [
            {
              "id": "action-check",
              "leftValue": "={{ $json.body.action }}",
              "rightValue": "submitted",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "gemini-check",
              "leftValue": "={{ $json.body.review.user.login }}",
              "rightValue": "gemini-code-assist[bot]",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "check-gemini-review",
      "name": "IF: Gemini \ub9ac\ubdf0\uc778\uc9c0 \ud655\uc778",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const body = $input.first().json.body;\nconst pr = body.pull_request;\nconst review = body.review;\nconst repo = body.repository;\n\nreturn [{\n  json: {\n    prNumber: pr.number,\n    prTitle: pr.title,\n    prUrl: pr.html_url,\n    headBranch: pr.head.ref,\n    headSha: pr.head.sha,\n    repoOwner: repo.owner.login,\n    repoName: repo.name,\n    reviewBody: review.body || '',\n    reviewState: review.state,\n    reviewId: review.id\n  }\n}];"
      },
      "id": "extract-info",
      "name": "PR \uc815\ubcf4 \ucd94\ucd9c",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        300
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://api.github.com/repos/{{ $json.repoOwner }}/{{ $json.repoName }}/pulls/{{ $json.prNumber }}/comments",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            },
            {
              "name": "X-GitHub-Api-Version",
              "value": "2022-11-28"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.GITHUB_TOKEN }}"
            }
          ]
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "per_page",
              "value": "50"
            }
          ]
        },
        "options": {}
      },
      "id": "get-review-comments",
      "name": "GitHub: \ub9ac\ubdf0 \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc870\ud68c",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        900,
        200
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://api.github.com/repos/{{ $('PR \uc815\ubcf4 \ucd94\ucd9c').first().json.repoOwner }}/{{ $('PR \uc815\ubcf4 \ucd94\ucd9c').first().json.repoName }}/pulls/{{ $('PR \uc815\ubcf4 \ucd94\ucd9c').first().json.prNumber }}/files",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            },
            {
              "name": "X-GitHub-Api-Version",
              "value": "2022-11-28"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.GITHUB_TOKEN }}"
            }
          ]
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "per_page",
              "value": "30"
            }
          ]
        },
        "options": {}
      },
      "id": "get-pr-files",
      "name": "GitHub: PR \ubcc0\uacbd \ud30c\uc77c \uc870\ud68c",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        900,
        400
      ]
    },
    {
      "parameters": {
        "jsCode": "const prInfo = $('PR \uc815\ubcf4 \ucd94\ucd9c').first().json;\nconst inlineComments = $('GitHub: \ub9ac\ubdf0 \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc870\ud68c').all().map(i => i.json);\nconst prFiles = $input.all().map(f => f.json);\n\n// Gemini \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \ud544\ud130\ub9c1\nconst geminiComments = inlineComments.filter(c =>\n  c.user && c.user.login === 'gemini-code-assist[bot]'\n);\n\n// \ucf54\uba58\ud2b8 \ud14d\uc2a4\ud2b8 \uc815\ub9ac\nconst commentsText = geminiComments.length > 0\n  ? geminiComments.map(c => `\ud83d\udccd ${c.path} (line ${c.original_line || c.line || '?'}):\\n${c.body}`).join('\\n\\n')\n  : '(\uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc5c6\uc74c)';\n\n// \ubcc0\uacbd \ud30c\uc77c \ubaa9\ub85d\nconst filePaths = prFiles.map(f => f.filename);\n\n// \ub9ac\ubdf0 \uc804\uccb4 \uc694\uc57d\nconst reviewSummary = [\n  `## Gemini \ub9ac\ubdf0 \uc694\uc57d\\n${prInfo.reviewBody || '(\uc5c6\uc74c)'}`,\n  `## \uc778\ub77c\uc778 \ucf54\uba58\ud2b8\\n${commentsText}`\n].join('\\n\\n');\n\nreturn [{\n  json: {\n    ...prInfo,\n    reviewSummary,\n    filePaths,\n    hasComments: geminiComments.length > 0 || prInfo.reviewBody.length > 0\n  }\n}];"
      },
      "id": "prepare-context",
      "name": "\ub9ac\ubdf0 \ucee8\ud14d\uc2a4\ud2b8 \uc900\ube44",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1120,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "conditions": [
            {
              "id": "has-comments",
              "leftValue": "={{ $json.hasComments }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true"
              }
            }
          ]
        }
      },
      "id": "check-has-comments",
      "name": "IF: \uc218\uc815\ud560 \ucf54\uba58\ud2b8 \uc788\ub294\uc9c0 \ud655\uc778",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1340,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// \ubcc0\uacbd\ub41c \ud30c\uc77c\ub4e4\uc744 \uc77d\uae30 \uc704\ud574 \uac01 \ud30c\uc77c \uacbd\ub85c\ub97c \uac1c\ubcc4 \uc544\uc774\ud15c\uc73c\ub85c \ubd84\ub9ac\nconst ctx = $input.first().json;\nreturn ctx.filePaths.map(path => ({\n  json: {\n    path,\n    branch: ctx.headBranch,\n    repoOwner: ctx.repoOwner,\n    repoName: ctx.repoName,\n    // \uc804\uccb4 \ucee8\ud14d\uc2a4\ud2b8\ub3c4 \ud568\uaed8 \uc804\ub2ec\n    _ctx: ctx\n  }\n}));"
      },
      "id": "split-files",
      "name": "\ud30c\uc77c \ubaa9\ub85d \ubd84\ub9ac",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1560,
        300
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://api.github.com/repos/{{ $json.repoOwner }}/{{ $json.repoName }}/contents/{{ $json.path }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            },
            {
              "name": "X-GitHub-Api-Version",
              "value": "2022-11-28"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.GITHUB_TOKEN }}"
            }
          ]
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "ref",
              "value": "={{ $json.branch }}"
            }
          ]
        },
        "options": {}
      },
      "id": "read-files",
      "name": "GitHub: \ud30c\uc77c \ub0b4\uc6a9 \uc77d\uae30",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1780,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const fileItems = $('GitHub: \ud30c\uc77c \ub0b4\uc6a9 \uc77d\uae30').all();\nconst splitItems = $('\ud30c\uc77c \ubaa9\ub85d \ubd84\ub9ac').all();\nconst ctx = splitItems[0].json._ctx;\n\nconst files = fileItems.map((item, i) => {\n  const content = Buffer.from((item.json.content || '').replace(/\\n/g, ''), 'base64').toString('utf8');\n  return {\n    path: item.json.path,\n    content,\n    sha: item.json.sha\n  };\n});\n\nconst filesContext = files.map(f =>\n  `### ${f.path}\\n\\`\\`\\`typescript\\n${f.content}\\n\\`\\`\\``\n).join('\\n\\n');\n\n// CI \uc2e4\ud328 \uc815\ubcf4 \uc870\ud68c\nconst token = $env.GITHUB_TOKEN;\nfunction ghGet(path) {\n  return new Promise((resolve, reject) => {\n    const https = require('https');\n    const req = https.request({\n      hostname: 'api.github.com',\n      path,\n      method: 'GET',\n      headers: {\n        'Authorization': `Bearer ${token}`,\n        'Accept': 'application/vnd.github+json',\n        'X-GitHub-Api-Version': '2022-11-28',\n        'User-Agent': 'n8n-jump-section'\n      }\n    }, (res) => {\n      let data = '';\n      res.on('data', chunk => data += chunk);\n      res.on('end', () => {\n        if (res.statusCode >= 400) reject(new Error(`HTTP ${res.statusCode}: ${data}`));\n        else resolve(JSON.parse(data));\n      });\n    });\n    req.setTimeout(15000, () => { req.destroy(); reject(new Error('Request timeout')); });\n    req.on('error', reject);\n    req.end();\n  });\n}\n\nlet ciSection = '';\ntry {\n  const checkData = await ghGet(`/repos/${ctx.repoOwner}/${ctx.repoName}/commits/${ctx.headSha}/check-runs?per_page=30`);\n  const failed = (checkData.check_runs || []).filter(c =>\n    c.conclusion === 'failure' || c.conclusion === 'timed_out'\n  );\n  if (failed.length > 0) {\n    const details = failed.map(c => {\n      const lines = [`### \u274c ${c.name}`];\n      if (c.output?.summary) lines.push(c.output.summary);\n      if (c.output?.text) lines.push(c.output.text.slice(0, 8000));\n      return lines.join('\\n');\n    }).join('\\n\\n');\n    ciSection = `## CI \uc2e4\ud328 \uc815\ubcf4\\n\\n${details}\\n\\n`;\n  }\n} catch (e) {\n  ciSection = `## CI \uc2e4\ud328 \uc815\ubcf4\\n\\n(\uc870\ud68c \uc2e4\ud328: ${e.message})\\n\\n`;\n}\n\nconst userPrompt = `## PR #${ctx.prNumber}: ${ctx.prTitle}\\n\ube0c\ub79c\uce58: \\`${ctx.headBranch}\\`\\n\\n## Gemini \ub9ac\ubdf0\\n${ctx.reviewSummary}\\n\\n${ciSection}---\\n\\n## \ud604\uc7ac \ucf54\ub4dc\\n\\n${filesContext}\\n\\n---\\n\\nGemini \ub9ac\ubdf0\ub97c \ubc18\uc601\ud558\uace0, CI \uc2e4\ud328\uac00 \uc788\ub2e4\uba74 \uc6d0\uc778\uc744 \ud30c\uc545\ud558\uc5ec \ud568\uaed8 \uc218\uc815\ud558\uc138\uc694.\\n\\n**\uc911\uc694**: CI \uc5d0\ub7ec \ubaa9\ub85d\uc5d0 \ubcf4\uc774\ub294 \ubaa8\ub4e0 \uc5d0\ub7ec\ub97c \uc774\ubc88 \ud55c \ubc88\uc5d0 \uc804\ubd80 \uc218\uc815\ud558\uc138\uc694. \ud558\ub098\ub9cc \uace0\uce58\uace0 \ub05d\ub0b4\uc9c0 \ub9c8\uc138\uc694. \uac01 \uc5d0\ub7ec\ub97c \ubd84\uc11d\ud574\uc11c \uc5f0\uad00\ub41c \ud30c\uc77c\uc744 \ubaa8\ub450 write_file\ub85c \uc81c\ucd9c\ud558\uc138\uc694. \ud0c0\ub2f9\ud55c \uc9c0\uc801\ub9cc \ubc18\uc601\ud558\uace0, \uc218\uc815\uc774 \ud544\uc694\ud55c \ubaa8\ub4e0 \ud30c\uc77c\uc744 write_file\ub85c \uc81c\ucd9c\ud558\uc138\uc694.`;\n\nconst geminiPayload = {\n  system_instruction: {\n    parts: [{ text: 'TypeScript \uc804\ubb38 \uac1c\ubc1c\uc790\uc785\ub2c8\ub2e4. Gemini\uc758 \ucf54\ub4dc \ub9ac\ubdf0 \ucf54\uba58\ud2b8\uc640 CI \uc2e4\ud328 \uc815\ubcf4\ub97c \ubc14\ud0d5\uc73c\ub85c \ucf54\ub4dc\ub97c \uc218\uc815\ud569\ub2c8\ub2e4.\\n\\n## \ud544\uc218 \uaddc\uce59\\n\\n### Prettier \ud3ec\ub9f7 (\ubc18\ub4dc\uc2dc \uc900\uc218 \u2014 format:check CI\uac00 \ub3cc\uace0 \uc788\uc74c)\\n- \ub4e4\uc5ec\uc4f0\uae30: \uc2a4\ud398\uc774\uc2a4 2\uce78\\n- \uc138\ubbf8\ucf5c\ub860: \ud56d\uc0c1 \ubd99\uc784\\n- \ub530\uc634\ud45c: \uc2f1\uae00\ucffc\ud2b8 (\\'\\')\\n- \ud55c \uc904 \ucd5c\ub300 \uae38\uc774: 100\uc790\\n- trailing comma: \ud56d\uc0c1 \ubd99\uc784 (\ud568\uc218 \uc778\uc790 \ub9c8\uc9c0\ub9c9 \ud3ec\ud568)\\n- \uc904\ubc14\uafc8: LF (\\\\n)\\n\\n### \ucf54\ub4dc \uc218\uc815\\n- Gemini \ucf54\uba58\ud2b8\uc5d0\uc11c \ud0c0\ub2f9\ud55c \uc9c0\uc801\ub9cc \uc218\uc815\\n- CI \uc2e4\ud328\uac00 \uc788\uc73c\uba74 \ubc18\ub4dc\uc2dc \uc6d0\uc778\uc744 \ud30c\uc545\ud558\uace0 **\ubaa8\ub4e0 \uc5d0\ub7ec\ub97c \ud55c \ubc88\uc5d0** \uc218\uc815 (format check, type error, test failure, build error \ub4f1) \u2014 \uc77c\ubd80\ub9cc \uc218\uc815\ud558\uace0 \ub05d\ub0b4\uc9c0 \ub9d0 \uac83\\n- \uae30\uc874 Public API \ud558\uc704 \ud638\ud658\uc131 \uc720\uc9c0\\n- \uc218\uc815\uc774 \ud544\uc694 \uc5c6\ub294 \ud30c\uc77c\uc740 write_file \ud638\ucd9c \uc0dd\ub7b5\\n- \uc6d0\uc778\uc774 \uba85\ud655\ud558\uace0 \uc218\uc815 \ubc29\ud5a5\uc774 \ud655\uc2e4\ud55c \uacbd\uc6b0\uc5d0\ub9cc write_file\ub85c \uc81c\ucd9c\ud558\uc138\uc694. \uc6d0\uc778\uc774 \ubd88\ubd84\uba85\ud558\uac70\ub098 \uc758\ub3c4\uce58 \uc54a\uc740 \ub3d9\uc791 \ubcc0\uacbd \uac00\ub2a5\uc131\uc774 \uc788\uc73c\uba74 \ud574\ub2f9 \ud30c\uc77c\uc740 \uac74\ub108\ub701\ub2c8\ub2e4.' }]\n  },\n  contents: [{ role: 'user', parts: [{ text: userPrompt }] }],\n  tools: [{\n    functionDeclarations: [{\n      name: 'write_file',\n      description: '\uc218\uc815\ub41c \ud30c\uc77c\uc758 \uc804\uccb4 \ub0b4\uc6a9\uc744 \uc81c\ucd9c\ud569\ub2c8\ub2e4.',\n      parameters: {\n        type: 'OBJECT',\n        properties: {\n          path: { type: 'STRING', description: '\ud30c\uc77c \uacbd\ub85c' },\n          content: { type: 'STRING', description: '\ud30c\uc77c \uc804\uccb4 \ub0b4\uc6a9' },\n          description: { type: 'STRING', description: '\ubcc0\uacbd \uc124\uba85' }\n        },\n        required: ['path', 'content', 'description']\n      }\n    }]\n  }],\n  toolConfig: { functionCallingConfig: { mode: 'ANY' } },\n  generationConfig: { maxOutputTokens: 16384 }\n};\n\nreturn [{\n  json: {\n    ...ctx,\n    files,\n    hasCiFailures: ciSection !== '',\n    geminiPayload\n  }\n}];"
      },
      "id": "decode-files",
      "name": "\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={{ $env.GEMINI_API_KEY }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ $json.geminiPayload }}",
        "options": {}
      },
      "id": "gemini-fix",
      "name": "Gemini: \ucf54\ub4dc \uc218\uc815",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2220,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst ctx = $('\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529').first().json;\n\nif (!response.candidates || response.candidates.length === 0) {\n  return [{ json: { noChanges: true, geminiError: response.promptFeedback || response.error || 'no candidates', ...ctx } }];\n}\n\nconst candidate = response.candidates[0];\nif (!candidate.content || !candidate.content.parts || candidate.finishReason === 'MALFORMED_FUNCTION_CALL') {\n  return [{ json: { noChanges: true, geminiError: candidate.finishReason || 'no content', ...ctx } }];\n}\n\nconst parts = candidate.content.parts;\nconst functionCalls = parts.filter(p => p.functionCall && p.functionCall.name === 'write_file');\n\nif (functionCalls.length === 0) {\n  // \uc218\uc815 \ud544\uc694 \uc5c6\uc74c\n  return [{ json: { noChanges: true, ...ctx } }];\n}\n\nconst fixedFiles = functionCalls.map(p => {\n  const original = ctx.files.find(f => f.path === p.functionCall.args.path);\n  return {\n    path: p.functionCall.args.path,\n    content: p.functionCall.args.content,\n    description: p.functionCall.args.description,\n    sha: original?.sha || null\n  };\n});\n\nconst filesSummary = fixedFiles.map(f => `\u2022 \\`${f.path}\\`: ${f.description}`).join('\\n');\n\nreturn [{\n  json: {\n    noChanges: false,\n    fixedFiles,\n    filesSummary,\n    prNumber: ctx.prNumber,\n    prTitle: ctx.prTitle,\n    prUrl: ctx.prUrl,\n    headBranch: ctx.headBranch,\n    repoOwner: ctx.repoOwner,\n    repoName: ctx.repoName\n  }\n}];"
      },
      "id": "parse-fix",
      "name": "\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2440,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "conditions": [
            {
              "id": "has-changes",
              "leftValue": "={{ $json.noChanges }}",
              "rightValue": false,
              "operator": {
                "type": "boolean",
                "operation": "false"
              }
            }
          ]
        }
      },
      "id": "check-has-changes",
      "name": "IF: \uc218\uc815\ub41c \ud30c\uc77c \uc788\ub294\uc9c0 \ud655\uc778",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2660,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json;\nif (data.noChanges || !data.fixedFiles || data.fixedFiles.length === 0) return [];\nconst fixedFiles = data.fixedFiles.filter(f => f.path && f.content && f.content.trim().length > 0);\nif (fixedFiles.length === 0) return [];\nconst { headBranch, repoOwner, repoName, filesSummary } = data;\nconst token = $env.GITHUB_TOKEN;\n\nconst headers = {\n  'Authorization': `Bearer ${token}`,\n  'Accept': 'application/vnd.github+json',\n  'X-GitHub-Api-Version': '2022-11-28',\n  'Content-Type': 'application/json',\n  'User-Agent': 'n8n-jump-section'\n};\n\nconst baseUrl = `https://api.github.com/repos/${repoOwner}/${repoName}`;\n\nfunction request(method, url, body) {\n  return new Promise((resolve, reject) => {\n    const https = require('https');\n    const path = url.replace('https://api.github.com', '');\n    const options = { hostname: 'api.github.com', path, method, headers };\n    const req = https.request(options, (res) => {\n      let d = '';\n      res.on('data', chunk => d += chunk);\n      res.on('end', () => {\n        if (res.statusCode >= 400) reject(new Error(`HTTP ${res.statusCode}: ${d}`));\n        else resolve(JSON.parse(d));\n      });\n    });\n    req.setTimeout(15000, () => { req.destroy(); reject(new Error('Request timeout')); });\n    req.on('error', reject);\n    if (body) req.write(JSON.stringify(body));\n    req.end();\n  });\n}\n\n// 1. HEAD SHA\nconst refData = await request('GET', `${baseUrl}/git/refs/heads/${headBranch}`);\nconst baseSha = refData.object.sha;\n\n// 2. \ud604\uc7ac \ud2b8\ub9ac SHA\nconst commitData = await request('GET', `${baseUrl}/git/commits/${baseSha}`);\nconst baseTreeSha = commitData.tree.sha;\n\n// 3. \uc0c8 \ud2b8\ub9ac \uc0dd\uc131 (\ubaa8\ub4e0 \ud30c\uc77c \ud55c\ubc88\uc5d0)\nconst newTree = await request('POST', `${baseUrl}/git/trees`, {\n  base_tree: baseTreeSha,\n  tree: fixedFiles.map(f => ({ path: f.path, mode: '100644', type: 'blob', content: f.content }))\n});\n\n// 4. \ucee4\ubc0b \uc0dd\uc131\nconst newCommit = await request('POST', `${baseUrl}/git/commits`, {\n  message: `fix: apply Gemini review suggestions\\n\\n${filesSummary}`,\n  tree: newTree.sha,\n  parents: [baseSha]\n});\n\n// 5. \ube0c\ub79c\uce58 ref \uc5c5\ub370\uc774\ud2b8\nawait request('PATCH', `${baseUrl}/git/refs/heads/${headBranch}`, { sha: newCommit.sha });\n\nreturn [{ json: { ...data, commitSha: newCommit.sha } }];"
      },
      "id": "commit-fixes",
      "name": "GitHub: Trees API \ucee4\ubc0b",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2880,
        200
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.github.com/repos/{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.repoOwner }}/{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.repoName }}/issues/{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.prNumber }}/comments",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            },
            {
              "name": "X-GitHub-Api-Version",
              "value": "2022-11-28"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.GITHUB_TOKEN }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"body\": \"## \ud83e\udd16 AI \uc790\ub3d9 \uc218\uc815 \uc644\ub8cc\\n\\nGemini \ub9ac\ubdf0 \ucf54\uba58\ud2b8\ub97c \ubc18\uc601\ud574\uc11c \ucf54\ub4dc\ub97c \uc218\uc815\ud588\uc2b5\ub2c8\ub2e4.\\n\\n### \uc218\uc815\ub41c \ud30c\uc77c\\n{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.filesSummary }}\\n\\n---\\n*Gemini AI\uac00 \ub9ac\ubdf0\ub97c \ubd84\uc11d\ud558\uc5ec \uc790\ub3d9 \uc218\uc815\ud588\uc2b5\ub2c8\ub2e4. \ubcc0\uacbd\uc0ac\ud56d\uc744 \ud655\uc778\ud574\uc8fc\uc138\uc694.*\"\n}",
        "options": {}
      },
      "id": "post-pr-comment",
      "name": "GitHub: PR \ucf54\uba58\ud2b8 \uac8c\uc2dc",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3100,
        400
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.DISCORD_WEBHOOK_URL }}",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"embeds\": [\n    {\n      \"title\": \"{{ $('\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529').first().json.hasCiFailures ? '\ud83d\udd27 Gemini \ub9ac\ubdf0 + CI \uc2e4\ud328 \uc218\uc815 \uc644\ub8cc' : '\ud83d\udd27 Gemini \ub9ac\ubdf0 \uae30\ubc18 \uc790\ub3d9 \uc218\uc815 \uc644\ub8cc' }}\",\n      \"description\": \"[PR #{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.prNumber }}: {{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.prTitle }}]({{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.prUrl }})\",\n      \"color\": 15105570,\n      \"fields\": [\n        {\n          \"name\": \"\uc218\uc815\ub41c \ud30c\uc77c\",\n          \"value\": \"{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.filesSummary }}\",\n          \"inline\": false\n        },\n        {\n          \"name\": \"\ube0c\ub79c\uce58\",\n          \"value\": \"`{{ $('\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1').first().json.headBranch }}`\",\n          \"inline\": true\n        },\n        {\n          \"name\": \"CI \uc2e4\ud328 \ud3ec\ud568\",\n          \"value\": \"{{ $('\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529').first().json.hasCiFailures ? '\u2705 CI \uc2e4\ud328 \uc815\ubcf4 \ubc18\uc601\ub428' : '\uc5c6\uc74c' }}\",\n          \"inline\": true\n        }\n      ],\n      \"footer\": { \"text\": \"jump-section AI Pipeline \u2022 Gemini \uc790\ub3d9 \uc218\uc815\" },\n      \"timestamp\": \"{{ new Date().toISOString() }}\"\n    }\n  ]\n}",
        "options": {}
      },
      "id": "discord-fix",
      "name": "Discord: \uc790\ub3d9 \uc218\uc815 \uc54c\ub9bc",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3320,
        300
      ]
    }
  ],
  "connections": {
    "Webhook: PR Review Event": {
      "main": [
        [
          {
            "node": "IF: Gemini \ub9ac\ubdf0\uc778\uc9c0 \ud655\uc778",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF: Gemini \ub9ac\ubdf0\uc778\uc9c0 \ud655\uc778": {
      "main": [
        [
          {
            "node": "PR \uc815\ubcf4 \ucd94\ucd9c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "PR \uc815\ubcf4 \ucd94\ucd9c": {
      "main": [
        [
          {
            "node": "GitHub: \ub9ac\ubdf0 \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub: \ub9ac\ubdf0 \uc778\ub77c\uc778 \ucf54\uba58\ud2b8 \uc870\ud68c": {
      "main": [
        [
          {
            "node": "GitHub: PR \ubcc0\uacbd \ud30c\uc77c \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub: PR \ubcc0\uacbd \ud30c\uc77c \uc870\ud68c": {
      "main": [
        [
          {
            "node": "\ub9ac\ubdf0 \ucee8\ud14d\uc2a4\ud2b8 \uc900\ube44",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ub9ac\ubdf0 \ucee8\ud14d\uc2a4\ud2b8 \uc900\ube44": {
      "main": [
        [
          {
            "node": "IF: \uc218\uc815\ud560 \ucf54\uba58\ud2b8 \uc788\ub294\uc9c0 \ud655\uc778",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF: \uc218\uc815\ud560 \ucf54\uba58\ud2b8 \uc788\ub294\uc9c0 \ud655\uc778": {
      "main": [
        [
          {
            "node": "\ud30c\uc77c \ubaa9\ub85d \ubd84\ub9ac",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud30c\uc77c \ubaa9\ub85d \ubd84\ub9ac": {
      "main": [
        [
          {
            "node": "GitHub: \ud30c\uc77c \ub0b4\uc6a9 \uc77d\uae30",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub: \ud30c\uc77c \ub0b4\uc6a9 \uc77d\uae30": {
      "main": [
        [
          {
            "node": "\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud30c\uc77c \ub0b4\uc6a9 \ub514\ucf54\ub529": {
      "main": [
        [
          {
            "node": "Gemini: \ucf54\ub4dc \uc218\uc815",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini: \ucf54\ub4dc \uc218\uc815": {
      "main": [
        [
          {
            "node": "\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\uc218\uc815 \uacb0\uacfc \ud30c\uc2f1": {
      "main": [
        [
          {
            "node": "IF: \uc218\uc815\ub41c \ud30c\uc77c \uc788\ub294\uc9c0 \ud655\uc778",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF: \uc218\uc815\ub41c \ud30c\uc77c \uc788\ub294\uc9c0 \ud655\uc778": {
      "main": [
        [
          {
            "node": "GitHub: Trees API \ucee4\ubc0b",
            "type": "main",
            "index": 0
          },
          {
            "node": "GitHub: PR \ucf54\uba58\ud2b8 \uac8c\uc2dc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub: Trees API \ucee4\ubc0b": {
      "main": [
        [
          {
            "node": "Discord: \uc790\ub3d9 \uc218\uc815 \uc54c\ub9bc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub: PR \ucf54\uba58\ud2b8 \uac8c\uc2dc": {
      "main": [
        [
          {
            "node": "Discord: \uc790\ub3d9 \uc218\uc815 \uc54c\ub9bc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "jump-section"
    },
    {
      "name": "auto-fix"
    }
  ]
}