{
  "name": "Detect and merge duplicate GitHub issues with Claude AI",
  "nodes": [
    {
      "id": "2a2b3c4d-0001-4b1b-9b1b-000000000001",
      "name": "When Issue Opened in GitHub",
      "type": "n8n-nodes-base.githubTrigger",
      "position": [
        2176,
        1712
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_OWNER"
        },
        "events": [
          "issues"
        ],
        "repository": {
          "__rl": true,
          "mode": "list",
          "value": "REPLACE_WITH_REPO"
        }
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2a2b3c4d-0002-4b1b-9b1b-000000000002",
      "name": "Filter Opened Issues",
      "type": "n8n-nodes-base.code",
      "position": [
        2400,
        1712
      ],
      "parameters": {
        "jsCode": "// Only continue for newly opened issues - GitHub's webhook also fires for edited/closed/labeled etc.\n// Handles both the real GitHub Trigger shape (flat) and a generic Webhook node's shape (nested under .body),\n// since the generic Webhook node is the easiest way to test this without GitHub credentials.\nconst raw = $input.first().json;\nconst payload = raw.body || raw;\nconst action = payload.action;\nconst issue = payload.issue || {};\n\nif (action !== 'opened') {\n  return [];\n}\n\nreturn [{\n  json: {\n    issueNumber: issue.number,\n    title: issue.title || '(no title)',\n    bodyText: issue.body || '(no description provided)',\n    author: issue.user?.login || 'unknown',\n    url: issue.html_url,\n    owner: payload.repository?.owner?.login,\n    repo: payload.repository?.name\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2a2b3c4d-0008-4b1b-9b1b-000000000008",
      "name": "Fetch Open GitHub Issues",
      "type": "n8n-nodes-base.github",
      "position": [
        2640,
        1712
      ],
      "parameters": {
        "limit": 30,
        "owner": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $json.owner }}"
        },
        "resource": "repository",
        "operation": "getIssues",
        "returnAll": false,
        "repository": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $json.repo }}"
        },
        "getRepositoryIssuesFilters": {
          "state": "open"
        }
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1,
      "alwaysOutputData": true
    },
    {
      "id": "2a2b3c4d-0009-4b1b-9b1b-000000000009",
      "name": "Prepare Issue Data for AI",
      "type": "n8n-nodes-base.code",
      "position": [
        2864,
        1712
      ],
      "parameters": {
        "jsCode": "// Build a short text block of other currently-open issues for Claude to compare the new issue against,\n// excluding the new issue itself (in case of any list/webhook timing overlap).\nconst prior = $('Filter Opened Issues').first().json;\nconst candidates = $input.all()\n  .map((item) => item.json)\n  .filter((i) => typeof i.number === 'number' && i.number !== prior.issueNumber);\n\nconst candidateBlock = candidates.length === 0\n  ? 'No other open issues to compare against.'\n  : candidates.map((i) => `#${i.number}: ${i.title}\\n${(i.body || '(no description)').slice(0, 300)}`).join('\\n\\n');\n\nreturn [{ json: { ...prior, candidateBlock } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2a2b3c4d-0003-4b1b-9b1b-000000000003",
      "name": "Post to Claude Duplicate Check",
      "type": "n8n-nodes-base.httpRequest",
      "maxTries": 4,
      "position": [
        3088,
        1712
      ],
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "method": "POST",
        "jsonBody": "={{ { \"model\": \"claude-sonnet-4-5\", \"max_tokens\": 800, \"messages\": [ { \"role\": \"user\", \"content\": \"You are triaging a new GitHub issue, including checking whether it is a duplicate/related report of an existing open issue. Respond with ONLY a raw JSON object (no markdown fences, no commentary) matching exactly this shape:\\n{\\\"labels\\\": [\\\"...\\\"], \\\"needsRepro\\\": true|false, \\\"comment\\\": \\\"...\\\", \\\"duplicateOf\\\": <issue number or null>, \\\"mergeSummary\\\": \\\"...\\\"}\\n\\nSTEP 1 - Duplicate check: compare the NEW issue below against the EXISTING OPEN ISSUES list. Set duplicateOf to an existing issue's number if they are about the same specific problem, request, or complaint - i.e. a maintainer would reasonably close one as a duplicate of the other. This applies to ANY kind of issue (a bug, a feature request, or a general report/complaint), not only software bugs. Match even when the wording is different or each describes a different symptom or example of the same underlying thing. Only avoid matching when two issues merely share a broad area or topic but are actually about different problems. If more than one existing issue matches, pick the lowest-numbered (oldest) one. If nothing matches, set duplicateOf to null.\\n\\nIf duplicateOf is set, mergeSummary must be 1-2 sentences describing what NEW, useful information this report adds beyond what the original issue already says (e.g. a different OS/browser/version it occurs on, a new error message or stack trace detail, an extra repro step, a workaround the reporter found). Phrase it as a contribution to add to the original issue's thread, written in third person (e.g. 'Also occurs on...', 'Additionally reports...'). If duplicateOf is null, set mergeSummary to an empty string.\\n\\nSTEP 2 - Label rules (still required even if this is a duplicate): choose 1 to 3 labels from this fixed set only, never invent new label names: bug, enhancement, question, documentation, needs-repro.\\n- bug: describes something broken or behaving incorrectly.\\n- enhancement: requests a new feature or improvement.\\n- question: is asking how to do something rather than reporting a problem.\\n- documentation: is about missing/incorrect docs.\\n- needs-repro: include this IN ADDITION to bug whenever needsRepro below is true.\\n\\nneedsRepro rules: set true only if the issue is a bug report AND it is missing enough information to reproduce it - e.g. no steps to reproduce, no expected vs actual behavior, no environment/version info, no error message/stack trace where one would be expected. If it already includes clear repro steps, or it isn't a bug report at all, set needsRepro to false. If duplicateOf is set, still evaluate this normally - it is only used if the issue turns out not to be a duplicate.\\n\\ncomment rules: if needsRepro is true, write a short, friendly comment (2-4 sentences) asking the author for the specific missing pieces - reference what's actually missing from THIS issue rather than a generic template. If needsRepro is false, set comment to an empty string.\\n\\nNEW issue title: \" + $json.title + \"\\n\\nNEW issue body:\\n\" + $json.bodyText + \"\\n\\nEXISTING OPEN ISSUES (compare against these for duplicateOf):\\n\" + $json.candidateBlock } ] } }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "waitBetweenTries": 20000
    },
    {
      "id": "2a2b3c4d-0004-4b1b-9b1b-000000000004",
      "name": "Extract Data from Claude JSON",
      "type": "n8n-nodes-base.code",
      "position": [
        3312,
        1712
      ],
      "parameters": {
        "jsCode": "// Claude is instructed to return raw JSON, but strip code fences defensively in case it doesn't comply\nconst response = $input.first().json;\nconst raw = response.content?.[0]?.text || '{}';\nconst cleaned = raw.trim().replace(/^```(json)?/i, '').replace(/```$/, '').trim();\n\nlet parsed;\ntry {\n  parsed = JSON.parse(cleaned);\n} catch (e) {\n  parsed = { labels: [], needsRepro: false, comment: '', duplicateOf: null, mergeSummary: '' };\n}\n\nconst prior = $('Filter Opened Issues').first().json;\n\nreturn [{\n  json: {\n    ...prior,\n    labels: Array.isArray(parsed.labels) ? parsed.labels : [],\n    needsRepro: !!parsed.needsRepro,\n    comment: parsed.comment || '',\n    duplicateOf: (typeof parsed.duplicateOf === 'number') ? parsed.duplicateOf : null,\n    mergeSummary: parsed.mergeSummary || ''\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2a2b3c4d-0010-4b1b-9b1b-000000000010",
      "name": "Check If Duplicate Issue",
      "type": "n8n-nodes-base.if",
      "position": [
        3520,
        1712
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2a2b3c4d-cond-0002",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.duplicateOf !== null && $json.duplicateOf !== undefined }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2a2b3c4d-0011-4b1b-9b1b-000000000011",
      "name": "Tag Issue as Duplicate",
      "type": "n8n-nodes-base.github",
      "position": [
        3744,
        1472
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $json.owner }}"
        },
        "labels": "={{ $json.labels.concat(['duplicate']) }}",
        "resource": "issue",
        "operation": "edit",
        "repository": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $json.repo }}"
        },
        "issueNumber": "={{ $json.issueNumber }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "2a2b3c4d-0012-4b1b-9b1b-000000000012",
      "name": "Notify Duplicate in Comments",
      "type": "n8n-nodes-base.github",
      "position": [
        3968,
        1472
      ],
      "parameters": {
        "body": "=Thanks for the report, @{{ $('Extract Data from Claude JSON').item.json.author }}! This looks related to #{{ $('Extract Data from Claude JSON').item.json.duplicateOf }}, so I'm linking it there to keep all the details on the same report instead of tracking it separately here. Feel free to add anything else on that issue.",
        "owner": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Extract Data from Claude JSON').item.json.owner }}"
        },
        "resource": "issue",
        "operation": "createComment",
        "repository": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Extract Data from Claude JSON').item.json.repo }}"
        },
        "issueNumber": "={{ $('Extract Data from Claude JSON').item.json.issueNumber }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "2a2b3c4d-0013-4b1b-9b1b-000000000013",
      "name": "Retrieve Original GitHub Issue",
      "type": "n8n-nodes-base.github",
      "position": [
        4192,
        1472
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Extract Data from Claude JSON').item.json.owner }}"
        },
        "resource": "issue",
        "operation": "get",
        "repository": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Extract Data from Claude JSON').item.json.repo }}"
        },
        "issueNumber": "={{ $('Extract Data from Claude JSON').item.json.duplicateOf }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "2a2b3c4d-0014-4b1b-9b1b-000000000014",
      "name": "Update Original Issue Summary",
      "type": "n8n-nodes-base.code",
      "position": [
        4400,
        1472
      ],
      "parameters": {
        "jsCode": "// Append (or update) a tracked summary block at the bottom of the ORIGINAL issue's body,\n// so the running count and merged details survive even if this workflow runs many times.\n// A hidden HTML-comment marker stores the structured data; everything between\n// DUPLICATE_TRACKER_START/END is regenerated fresh on every run rather than stacking duplicate blocks.\nconst original = $input.first().json;\nconst newDetail = $('Extract Data from Claude JSON').first().json.mergeSummary;\nconst reporter = $('Extract Data from Claude JSON').first().json.author;\nconst newIssueNumber = $('Extract Data from Claude JSON').first().json.issueNumber;\n\nconst body = original.body || '';\nconst startMarker = '<!-- DUPLICATE_TRACKER_START -->';\nconst endMarker = '<!-- DUPLICATE_TRACKER_END -->';\nconst blockRegex = new RegExp(`${startMarker}[\\\\s\\\\S]*?${endMarker}`);\nconst dataRegex = /<!-- DUPLICATE_TRACKER_DATA: ([\\s\\S]*?) -->/;\n\nlet details = [];\nconst dataMatch = body.match(dataRegex);\nif (dataMatch) {\n  try {\n    details = JSON.parse(dataMatch[1]).details || [];\n  } catch (e) {\n    details = [];\n  }\n}\n\nif (newDetail) {\n  details.push(`#${newIssueNumber} (@${reporter}): ${newDetail}`);\n}\n\nconst count = details.length + 1; // +1 for the original reporter\nconst detailLines = details.length > 0 ? details.map((d) => `- ${d}`).join('\\n') : '- (no additional details yet)';\nconst newBlock = `${startMarker}\\n---\\n**\\ud83d\\udd01 Reported by ${count} user(s) so far.** Additional details collected from other reports:\\n${detailLines}\\n<!-- DUPLICATE_TRACKER_DATA: ${JSON.stringify({ details })} -->\\n${endMarker}`;\n\nconst newBody = blockRegex.test(body) ? body.replace(blockRegex, newBlock) : `${body}\\n\\n${newBlock}`;\n\nreturn [{\n  json: {\n    owner: original.owner?.login || $('Extract Data from Claude JSON').first().json.owner,\n    repo: original.repository?.name || $('Extract Data from Claude JSON').first().json.repo,\n    originalIssueNumber: original.number,\n    newBody,\n    mergeSummary: newDetail,\n    newIssueNumber,\n    reporter\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2a2b3c4d-0015-4b1b-9b1b-000000000015",
      "name": "Revise Original Issue",
      "type": "n8n-nodes-base.github",
      "position": [
        4624,
        1472
      ],
      "parameters": {
        "body": "={{ $json.newBody }}",
        "owner": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $json.owner }}"
        },
        "resource": "issue",
        "operation": "edit",
        "repository": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $json.repo }}"
        },
        "issueNumber": "={{ $json.originalIssueNumber }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "2a2b3c4d-0016-4b1b-9b1b-000000000016",
      "name": "Add Merge Info to Comments",
      "type": "n8n-nodes-base.github",
      "position": [
        4848,
        1472
      ],
      "parameters": {
        "body": "=\ud83d\udccc Another report came in (#{{ $('Update Original Issue Summary').item.json.newIssueNumber }} by @{{ $('Update Original Issue Summary').item.json.reporter }}) with additional detail:\n\n{{ $('Update Original Issue Summary').item.json.mergeSummary }}",
        "owner": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Update Original Issue Summary').item.json.owner }}"
        },
        "resource": "issue",
        "operation": "createComment",
        "repository": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Update Original Issue Summary').item.json.repo }}"
        },
        "issueNumber": "={{ $('Update Original Issue Summary').item.json.originalIssueNumber }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "2a2b3c4d-0005-4b1b-9b1b-000000000005",
      "name": "Assign AI Labels to Issue",
      "type": "n8n-nodes-base.github",
      "position": [
        3744,
        1952
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $json.owner }}"
        },
        "labels": "={{ $json.labels }}",
        "resource": "issue",
        "operation": "edit",
        "repository": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $json.repo }}"
        },
        "issueNumber": "={{ $json.issueNumber }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "2a2b3c4d-0006-4b1b-9b1b-000000000006",
      "name": "Determine Need for Repro Info",
      "type": "n8n-nodes-base.if",
      "position": [
        3968,
        1952
      ],
      "parameters": {
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2a2b3c4d-cond-0001",
              "operator": {
                "type": "boolean",
                "operation": "equal"
              },
              "leftValue": "={{ $('Extract Data from Claude JSON').item.json.needsRepro }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2a2b3c4d-0007-4b1b-9b1b-000000000007",
      "name": "Request Repro Steps in Comments",
      "type": "n8n-nodes-base.github",
      "position": [
        4192,
        1872
      ],
      "parameters": {
        "body": "={{ $('Extract Data from Claude JSON').item.json.comment }}",
        "owner": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Extract Data from Claude JSON').item.json.owner }}"
        },
        "resource": "issue",
        "operation": "createComment",
        "repository": {
          "__rl": true,
          "mode": "expression",
          "value": "={{ $('Extract Data from Claude JSON').item.json.repo }}"
        },
        "issueNumber": "={{ $('Extract Data from Claude JSON').item.json.issueNumber }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "8d8c943c-ffef-4dff-9ec5-3f271262b6f4",
      "name": "Sticky - Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1568,
        1312
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Detect and merge duplicate GitHub issues with Claude AI\n\n### How it works\n\nThis workflow triggers when a GitHub issue webhook is received, filters for newly opened issues, and gathers other open issues for comparison. It sends the new issue plus candidate issues to Claude to classify the issue, suggest labels, and determine whether it is a duplicate. Based on Claude\u2019s JSON response, it either marks and links duplicate issues or applies AI-selected labels and optionally asks the reporter for reproduction steps.\n\n### Setup steps\n\n- Configure the GitHub Trigger with the target repository and webhook permissions for issue events.\n- Configure GitHub credentials for reading issues, adding labels, updating issue bodies, and posting comments.\n- Add Anthropic API credentials or headers to the Claude HTTP Request node, including the API key and required Anthropic version header.\n- Create the needs-repro label in your repository (see the warning note) - the rest (bug, enhancement, question, documentation, duplicate) are GitHub defaults.\n- Review the Claude prompt and JSON parsing expectations so the returned fields match the downstream duplicate, label, and reproduction-info conditions.\n\n### Customization\n\nCustomize the Claude prompt to change label taxonomy, duplicate-confidence rules, and when reproduction steps should be requested. You can also adjust the duplicate summary block format that is appended to the original issue."
      },
      "typeVersion": 1
    },
    {
      "id": "5daceab7-409b-426f-8b23-91e68cac0103",
      "name": "Sticky - Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2128,
        1552
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Receive new issue\n\nStarts from the GitHub issue webhook and keeps only newly opened issue events while extracting the fields needed downstream."
      },
      "typeVersion": 1
    },
    {
      "id": "dd23620b-a039-4f7d-a6fc-80c8e0afaf91",
      "name": "Sticky - Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2592,
        1552
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Gather comparison issues\n\nFetches currently open GitHub issues and formats them into a concise candidate list for Claude to compare against the newly opened issue."
      },
      "typeVersion": 1
    },
    {
      "id": "032f76d2-278a-42ae-8172-3f94c6cc141d",
      "name": "Sticky - Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3040,
        1552
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 320,
        "content": "## Analyze and branch\n\nCalls Claude for triage and duplicate detection, parses the returned JSON defensively, and routes execution based on whether the new issue is a duplicate."
      },
      "typeVersion": 1
    },
    {
      "id": "34c60e42-a6cf-4340-aa1e-a310b5a78951",
      "name": "Sticky - Section 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3696,
        1312
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Notify duplicate issue\n\nOn the duplicate branch, labels the newly opened issue as a duplicate and comments on it with a notice pointing to the original issue."
      },
      "typeVersion": 1
    },
    {
      "id": "c83fc04d-2095-407a-83c9-459010db55d2",
      "name": "Sticky - Section 5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4144,
        1344
      ],
      "parameters": {
        "color": 7,
        "width": 848,
        "height": 304,
        "content": "## Update original issue\n\nRetrieves the original issue, builds an updated body that tracks merged duplicate information, updates the original issue, and posts a comment noting the merged context."
      },
      "typeVersion": 1
    },
    {
      "id": "11bcbfad-a181-49f4-87c6-f5c725322296",
      "name": "Sticky - Section 6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3696,
        1712
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 400,
        "content": "## Label and request details\n\nOn the non-duplicate branch, applies Claude-suggested labels, checks whether reproduction information is missing, and comments to request repro steps when needed."
      },
      "typeVersion": 1
    },
    {
      "id": "4e6cb185-2c50-496e-bbb7-a0431655e17d",
      "name": "Sticky - Warning",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4480,
        1808
      ],
      "parameters": {
        "color": 4,
        "width": 360,
        "height": 200,
        "content": "## \u26a0\ufe0f Create the needs-repro label\nneeds-repro must exist in your repo before the first run, or the labeling step errors. The other labels (bug, enhancement, question, documentation, duplicate) are GitHub defaults."
      },
      "typeVersion": 1
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Filter Opened Issues": {
      "main": [
        [
          {
            "node": "Fetch Open GitHub Issues",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Revise Original Issue": {
      "main": [
        [
          {
            "node": "Add Merge Info to Comments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tag Issue as Duplicate": {
      "main": [
        [
          {
            "node": "Notify Duplicate in Comments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check If Duplicate Issue": {
      "main": [
        [
          {
            "node": "Tag Issue as Duplicate",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Assign AI Labels to Issue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Open GitHub Issues": {
      "main": [
        [
          {
            "node": "Prepare Issue Data for AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Assign AI Labels to Issue": {
      "main": [
        [
          {
            "node": "Determine Need for Repro Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Issue Data for AI": {
      "main": [
        [
          {
            "node": "Post to Claude Duplicate Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Issue Opened in GitHub": {
      "main": [
        [
          {
            "node": "Filter Opened Issues",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Duplicate in Comments": {
      "main": [
        [
          {
            "node": "Retrieve Original GitHub Issue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Determine Need for Repro Info": {
      "main": [
        [
          {
            "node": "Request Repro Steps in Comments",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Extract Data from Claude JSON": {
      "main": [
        [
          {
            "node": "Check If Duplicate Issue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Original Issue Summary": {
      "main": [
        [
          {
            "node": "Revise Original Issue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post to Claude Duplicate Check": {
      "main": [
        [
          {
            "node": "Extract Data from Claude JSON",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Original GitHub Issue": {
      "main": [
        [
          {
            "node": "Update Original Issue Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}