{
  "name": "Load Github Repo (Single)",
  "nodes": [
    {
      "parameters": {
        "formTitle": "Repo Loader",
        "formDescription": "Load select workflows from your chosen GitHub repo. ",
        "formFields": {
          "values": [
            {
              "fieldName": "Repo Owner",
              "fieldLabel": "Repo Owner",
              "fieldType": "dropdown",
              "defaultValue": "Granite-Marketing",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Granite-Marketing"
                  },
                  {
                    "option": "sanindooo"
                  }
                ]
              },
              "requiredField": true
            }
          ]
        },
        "options": {
          "appendAttribution": false,
          "buttonLabel": "Find Repos",
          "path": "github",
          "customCss": ":root {\n\t--font-family: 'Open Sans', sans-serif;\n\t--font-weight-normal: 400;\n\t--font-weight-bold: 600;\n\t--font-size-body: 12px;\n\t--font-size-label: 14px;\n\t--font-size-test-notice: 12px;\n\t--font-size-input: 14px;\n\t--font-size-header: 20px;\n\t--font-size-paragraph: 14px;\n\t--font-size-link: 12px;\n\t--font-size-error: 12px;\n\t--font-size-html-h1: 28px;\n\t--font-size-html-h2: 20px;\n\t--font-size-html-h3: 16px;\n\t--font-size-html-h4: 14px;\n\t--font-size-html-h5: 12px;\n\t--font-size-html-h6: 10px;\n\t--font-size-subheader: 14px;\n\n\t/* Colors - Granite Marketing Dark Theme */\n\t--color-background: #171717;\n\t--color-test-notice-text: #fbbf24;\n\t--color-test-notice-bg: #1f1f1f;\n\t--color-test-notice-border: #404040;\n\t--color-card-bg: #1e1e1e;\n\t--color-card-border: #404040;\n\t--color-card-shadow: rgba(0, 0, 0, 0.3);\n\t--color-link: #a5a5a5;\n\t--color-header: #f5f5f5;\n\t--color-label: #d4d4d4;\n\t--color-input-border: #404040;\n\t--color-input-text: #e5e5e5;\n\t--color-focus-border: #4ade80;\n\t--color-submit-btn-bg: #4ade80;\n\t--color-submit-btn-text: #171717;\n\t--color-error: #f87171;\n\t--color-required: #4ade80;\n\t--color-clear-button-bg: #525252;\n\t--color-html-text: #d4d4d4;\n\t--color-html-link: #4ade80;\n\t--color-header-subtext: #a5a5a5;\n\n\t/* Border Radii - Matching Granite Marketing */\n\t--border-radius-card: 12px;\n\t--border-radius-input: 6px;\n\t--border-radius-clear-btn: 50%;\n\t--card-border-radius: 12px;\n\n\t/* Spacing */\n\t--padding-container-top: 24px;\n\t--padding-card: 24px;\n\t--padding-test-notice-vertical: 12px;\n\t--padding-test-notice-horizontal: 24px;\n\t--margin-bottom-card: 16px;\n\t--padding-form-input: 12px;\n\t--card-padding: 24px;\n\t--card-margin-bottom: 16px;\n\n\t/* Dimensions */\n\t--container-width: 448px;\n\t--submit-btn-height: 48px;\n\t--checkbox-size: 18px;\n\n\t/* Others */\n\t--box-shadow-card: 0px 4px 16px 0px var(--color-card-shadow);\n\t--opacity-placeholder: 0.5;\n}"
        }
      },
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.4,
      "position": [
        208,
        960
      ],
      "id": "ea4d5b25-dba9-4e40-8f18-2a8d8fc34f50",
      "name": "On form submission",
      "webhookId": "650a9241-9387-4597-8c01-36e9ddb42e8d"
    },
    {
      "parameters": {
        "resource": "organization",
        "owner": {
          "__rl": true,
          "value": "={{ $json[\"Repo Owner\"] }}",
          "mode": "name"
        },
        "returnAll": true
      },
      "type": "n8n-nodes-base.github",
      "typeVersion": 1.1,
      "position": [
        432,
        960
      ],
      "id": "f51d81d9-4d2b-4e95-90d1-992e1a43f95f",
      "name": "Get repositories for an organization",
      "webhookId": "2e582785-7dcd-4ba3-8540-5adfd01b4f81",
      "credentials": {
        "githubApi": {
          "id": "tMtthd2awjIjzu7F",
          "name": "Stephen GitHub account"
        }
      }
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "options": {}
      },
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        880,
        960
      ],
      "id": "6760dc7c-d639-4522-891c-671d9d16d012",
      "name": "Aggregate"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "0e1b7526-faf5-4001-ba27-3005836cc58f",
              "name": "repo_name",
              "value": "={{ $json.name }}",
              "type": "string"
            },
            {
              "id": "b2205659-8d2f-4006-a116-b0330792a4d2",
              "name": "repo_url",
              "value": "={{ $json.full_name }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        656,
        960
      ],
      "id": "8ed8461c-5264-4224-b0bf-69203cd1a13f",
      "name": "Set Name and URL"
    },
    {
      "parameters": {
        "resource": "file",
        "operation": "get",
        "owner": {
          "__rl": true,
          "value": "={{ $('On form submission').first().json[\"Repo Owner\"] }}",
          "mode": "name"
        },
        "repository": {
          "__rl": true,
          "value": "={{ $('Select Repos').first().json.Radio }}",
          "mode": "name"
        },
        "filePath": "={{ $json.path }}",
        "additionalParameters": {}
      },
      "type": "n8n-nodes-base.github",
      "typeVersion": 1.1,
      "position": [
        3120,
        656
      ],
      "id": "7b8d3811-e079-4464-b530-c1a150f8588e",
      "name": "Get a file",
      "webhookId": "a10e7d8d-798d-4388-ae36-8109fdb0f6fa",
      "credentials": {
        "githubApi": {
          "id": "tMtthd2awjIjzu7F",
          "name": "Stephen GitHub account"
        }
      }
    },
    {
      "parameters": {
        "operation": "create",
        "workflowObject": "={{ $json.toJsonString() }}",
        "requestOptions": {}
      },
      "type": "n8n-nodes-base.n8n",
      "typeVersion": 1,
      "position": [
        4464,
        736
      ],
      "id": "f77480f7-6a7d-4312-aad5-756390e3625e",
      "name": "Create a workflow",
      "credentials": {
        "n8nApi": {
          "id": "bwFt15fTGCe0JsbT",
          "name": "Stephen n8n account"
        }
      }
    },
    {
      "parameters": {
        "operation": "fromJson",
        "options": {}
      },
      "type": "n8n-nodes-base.extractFromFile",
      "typeVersion": 1.1,
      "position": [
        3344,
        656
      ],
      "id": "837a2ab6-ddc5-4589-8525-836f4539600b",
      "name": "Extract JSON From File"
    },
    {
      "parameters": {
        "maxItems": 100
      },
      "type": "n8n-nodes-base.limit",
      "typeVersion": 1,
      "position": [
        3792,
        656
      ],
      "id": "d5902df9-7fab-4b27-b6b7-0b9ea8e64265",
      "name": "Limit"
    },
    {
      "parameters": {
        "defineForm": "json",
        "jsonOutput": "=[\n  {\n    \"fieldLabel\": \"Radio\",\n    \"fieldType\": \"radio\",\n    \"fieldOptions\": {\n      \"values\": [\n        {{ $json.options }}\n      ]\n    }\n  }\n]\n",
        "options": {
          "formTitle": "Select Repos",
          "buttonLabel": "Select Repos",
          "customCss": ":root {\n\t--font-family: 'Open Sans', sans-serif;\n\t--font-weight-normal: 400;\n\t--font-weight-bold: 600;\n\t--font-size-body: 12px;\n\t--font-size-label: 14px;\n\t--font-size-test-notice: 12px;\n\t--font-size-input: 14px;\n\t--font-size-header: 20px;\n\t--font-size-paragraph: 14px;\n\t--font-size-link: 12px;\n\t--font-size-error: 12px;\n\t--font-size-html-h1: 28px;\n\t--font-size-html-h2: 20px;\n\t--font-size-html-h3: 16px;\n\t--font-size-html-h4: 14px;\n\t--font-size-html-h5: 12px;\n\t--font-size-html-h6: 10px;\n\t--font-size-subheader: 14px;\n\n\t/* Colors - Granite Marketing Dark Theme */\n\t--color-background: #171717;\n\t--color-test-notice-text: #fbbf24;\n\t--color-test-notice-bg: #1f1f1f;\n\t--color-test-notice-border: #404040;\n\t--color-card-bg: #1e1e1e;\n\t--color-card-border: #404040;\n\t--color-card-shadow: rgba(0, 0, 0, 0.3);\n\t--color-link: #a5a5a5;\n\t--color-header: #f5f5f5;\n\t--color-label: #d4d4d4;\n\t--color-input-border: #404040;\n\t--color-input-text: #e5e5e5;\n\t--color-focus-border: #4ade80;\n\t--color-submit-btn-bg: #4ade80;\n\t--color-submit-btn-text: #171717;\n\t--color-error: #f87171;\n\t--color-required: #4ade80;\n\t--color-clear-button-bg: #525252;\n\t--color-html-text: #d4d4d4;\n\t--color-html-link: #4ade80;\n\t--color-header-subtext: #a5a5a5;\n\n\t/* Border Radii - Matching Granite Marketing */\n\t--border-radius-card: 12px;\n\t--border-radius-input: 6px;\n\t--border-radius-clear-btn: 50%;\n\t--card-border-radius: 12px;\n\n\t/* Spacing */\n\t--padding-container-top: 24px;\n\t--padding-card: 24px;\n\t--padding-test-notice-vertical: 12px;\n\t--padding-test-notice-horizontal: 24px;\n\t--margin-bottom-card: 16px;\n\t--padding-form-input: 12px;\n\t--card-padding: 24px;\n\t--card-margin-bottom: 16px;\n\n\t/* Dimensions */\n\t--container-width: 448px;\n\t--submit-btn-height: 48px;\n\t--checkbox-size: 18px;\n\n\t/* Others */\n\t--box-shadow-card: 0px 4px 16px 0px var(--color-card-shadow);\n\t--opacity-placeholder: 0.5;\n}\n\n.multiselect {\n  max-height: 300px;\n  overflow: scroll;\n}"
        }
      },
      "type": "n8n-nodes-base.form",
      "typeVersion": 2.4,
      "position": [
        1328,
        960
      ],
      "id": "4fd0edff-4695-4895-a0c7-f0e47728f39b",
      "name": "Select Repos",
      "webhookId": "c4d78021-dfe6-40c7-97f9-58f01ac39c72"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "d33b92dd-84d7-4847-919d-5f90c6c3293d",
              "name": "name",
              "value": "={{ $('Get Target Repo(s) Data').item.json.name }}",
              "type": "string"
            },
            {
              "id": "4e1ee2d5-28f3-4654-b756-0b2a8fc2caa2",
              "name": "data",
              "value": "={{ $json.data }}",
              "type": "object"
            },
            {
              "id": "b4e396eb-ea62-4ef0-add6-fcb588f767e2",
              "name": "is_empty",
              "value": "={{ $json.data.isEmpty() }}",
              "type": "boolean"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        3568,
        656
      ],
      "id": "f5505d6b-67cf-4960-9aff-a3552709b563",
      "name": "Set Name and Data"
    },
    {
      "parameters": {
        "operation": "completion",
        "completionTitle": "Results",
        "completionMessage": "={{ $json.data.map(item =>\n`${item.is_empty === true ? item.name + ' - Failed ❌' : item.name + ' - Success! ✅'}`\n).join('\\n\\n') }}",
        "options": {
          "customCss": ":root {\n\t--font-family: 'Open Sans', sans-serif;\n\t--font-weight-normal: 400;\n\t--font-weight-bold: 600;\n\t--font-size-body: 12px;\n\t--font-size-label: 14px;\n\t--font-size-test-notice: 12px;\n\t--font-size-input: 14px;\n\t--font-size-header: 20px;\n\t--font-size-paragraph: 14px;\n\t--font-size-link: 12px;\n\t--font-size-error: 12px;\n\t--font-size-html-h1: 28px;\n\t--font-size-html-h2: 20px;\n\t--font-size-html-h3: 16px;\n\t--font-size-html-h4: 14px;\n\t--font-size-html-h5: 12px;\n\t--font-size-html-h6: 10px;\n\t--font-size-subheader: 14px;\n\n\t/* Colors - Granite Marketing Dark Theme */\n\t--color-background: #171717;\n\t--color-test-notice-text: #fbbf24;\n\t--color-test-notice-bg: #1f1f1f;\n\t--color-test-notice-border: #404040;\n\t--color-card-bg: #1e1e1e;\n\t--color-card-border: #404040;\n\t--color-card-shadow: rgba(0, 0, 0, 0.3);\n\t--color-link: #a5a5a5;\n\t--color-header: #f5f5f5;\n\t--color-label: #d4d4d4;\n\t--color-input-border: #404040;\n\t--color-input-text: #e5e5e5;\n\t--color-focus-border: #4ade80;\n\t--color-submit-btn-bg: #4ade80;\n\t--color-submit-btn-text: #171717;\n\t--color-error: #f87171;\n\t--color-required: #4ade80;\n\t--color-clear-button-bg: #525252;\n\t--color-html-text: #d4d4d4;\n\t--color-html-link: #4ade80;\n\t--color-header-subtext: #a5a5a5;\n\n\t/* Border Radii - Matching Granite Marketing */\n\t--border-radius-card: 12px;\n\t--border-radius-input: 6px;\n\t--border-radius-clear-btn: 50%;\n\t--card-border-radius: 12px;\n\n\t/* Spacing */\n\t--padding-container-top: 24px;\n\t--padding-card: 24px;\n\t--padding-test-notice-vertical: 12px;\n\t--padding-test-notice-horizontal: 24px;\n\t--margin-bottom-card: 16px;\n\t--padding-form-input: 12px;\n\t--card-padding: 24px;\n\t--card-margin-bottom: 16px;\n\n\t/* Dimensions */\n\t--container-width: 448px;\n\t--submit-btn-height: 48px;\n\t--checkbox-size: 18px;\n\n\t/* Others */\n\t--box-shadow-card: 0px 4px 16px 0px var(--color-card-shadow);\n\t--opacity-placeholder: 0.5;\n}\n\n.multiselect {\n  max-height: 300px;\n  overflow: scroll;\n}"
        }
      },
      "type": "n8n-nodes-base.form",
      "typeVersion": 2.4,
      "position": [
        5136,
        656
      ],
      "id": "827befc6-7fea-43a7-a924-ec9bae1a9587",
      "name": "Results",
      "webhookId": "c4f8c2e4-a72c-4335-a3ac-60d2ecf75a21"
    },
    {
      "parameters": {
        "jsCode": "return [{\n  json: {\n    pendingPaths: [\"\"],   // start at repo root\n    currentPath: \"\",\n    allFiles: []\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1552,
        960
      ],
      "id": "5f208ba7-f966-45ea-9107-6ab2be7eaf5d",
      "name": "Initialise Loop State"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "bac603f6-afb6-4a02-a898-1d8899268a6c",
              "leftValue": "={{$json.pendingPaths.length > 0}}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1776,
        960
      ],
      "id": "b7757bb6-6ff2-406a-bb16-5c87d697f007",
      "name": "If Has Next Page"
    },
    {
      "parameters": {
        "jsCode": "const state = $json;\n\nstate.currentPath = state.pendingPaths.shift();\n\nreturn [{ json: state }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2000,
        960
      ],
      "id": "ae5dc527-5303-41fa-abcb-909674bfd68f",
      "name": "Set Current Path"
    },
    {
      "parameters": {
        "jsCode": "const state = $('Set Current Path').all()[0].json;\n\nstate.pendingPaths ??= [];\nstate.allFiles ??= [];\n\n// get ALL items from the GitHub node\nconst entries = $input.all().map(i => i.json);\n\nfor (const entry of entries) {\n  if (entry.type === \"dir\") {\n    state.pendingPaths.push(entry.path);\n  } else if (entry.type === \"file\" && entry.name.endsWith(\".json\")) {\n    state.allFiles.push(entry);\n  }\n}\n\nreturn [{ json: state }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2448,
        1040
      ],
      "id": "6585d68f-f119-4b3a-a792-66c5e59ad045",
      "name": "Process Directory Contents"
    },
    {
      "parameters": {
        "resource": "file",
        "operation": "list",
        "owner": {
          "__rl": true,
          "value": "={{ $('On form submission').first().json[\"Repo Owner\"] }}",
          "mode": "name"
        },
        "repository": {
          "__rl": true,
          "value": "={{ $('Select Repos').first().json.Radio }}",
          "mode": "name"
        },
        "filePath": "={{ $json.currentPath || '' }}"
      },
      "type": "n8n-nodes-base.github",
      "typeVersion": 1.1,
      "position": [
        2224,
        960
      ],
      "id": "10e59977-e5fc-4c8b-a82e-774f12a72fdc",
      "name": "List Files",
      "webhookId": "7b4d68f4-4791-4a6b-ac78-48f9de7f3d95",
      "credentials": {
        "githubApi": {
          "id": "tMtthd2awjIjzu7F",
          "name": "Stephen GitHub account"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        2000,
        656
      ],
      "id": "5651aa75-d2ad-47fe-a6b5-522e719c47ca",
      "name": "No Operation, do nothing"
    },
    {
      "parameters": {
        "fieldToSplitOut": "allFiles",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2672,
        560
      ],
      "id": "4fa186b9-9405-493e-a357-a619874ad181",
      "name": "Split Out All Workflows"
    },
    {
      "parameters": {
        "defineForm": "json",
        "jsonOutput": "=[\n  {\n    \"fieldLabel\": \"Checkboxes\",\n    \"fieldType\": \"checkbox\",\n    \"fieldOptions\": {\n      \"values\": [\n        {{ $json.options }}\n      ]\n    }\n  }\n]\n",
        "options": {
          "formTitle": "Create Workflows",
          "buttonLabel": "Create Workflows",
          "customCss": ":root {\n\t--font-family: 'Open Sans', sans-serif;\n\t--font-weight-normal: 400;\n\t--font-weight-bold: 600;\n\t--font-size-body: 12px;\n\t--font-size-label: 14px;\n\t--font-size-test-notice: 12px;\n\t--font-size-input: 14px;\n\t--font-size-header: 20px;\n\t--font-size-paragraph: 14px;\n\t--font-size-link: 12px;\n\t--font-size-error: 12px;\n\t--font-size-html-h1: 28px;\n\t--font-size-html-h2: 20px;\n\t--font-size-html-h3: 16px;\n\t--font-size-html-h4: 14px;\n\t--font-size-html-h5: 12px;\n\t--font-size-html-h6: 10px;\n\t--font-size-subheader: 14px;\n\n\t/* Colors - Granite Marketing Dark Theme */\n\t--color-background: #171717;\n\t--color-test-notice-text: #fbbf24;\n\t--color-test-notice-bg: #1f1f1f;\n\t--color-test-notice-border: #404040;\n\t--color-card-bg: #1e1e1e;\n\t--color-card-border: #404040;\n\t--color-card-shadow: rgba(0, 0, 0, 0.3);\n\t--color-link: #a5a5a5;\n\t--color-header: #f5f5f5;\n\t--color-label: #d4d4d4;\n\t--color-input-border: #404040;\n\t--color-input-text: #e5e5e5;\n\t--color-focus-border: #4ade80;\n\t--color-submit-btn-bg: #4ade80;\n\t--color-submit-btn-text: #171717;\n\t--color-error: #f87171;\n\t--color-required: #4ade80;\n\t--color-clear-button-bg: #525252;\n\t--color-html-text: #d4d4d4;\n\t--color-html-link: #4ade80;\n\t--color-header-subtext: #a5a5a5;\n\n\t/* Border Radii - Matching Granite Marketing */\n\t--border-radius-card: 12px;\n\t--border-radius-input: 6px;\n\t--border-radius-clear-btn: 50%;\n\t--card-border-radius: 12px;\n\n\t/* Spacing */\n\t--padding-container-top: 24px;\n\t--padding-card: 24px;\n\t--padding-test-notice-vertical: 12px;\n\t--padding-test-notice-horizontal: 24px;\n\t--margin-bottom-card: 16px;\n\t--padding-form-input: 12px;\n\t--card-padding: 24px;\n\t--card-margin-bottom: 16px;\n\n\t/* Dimensions */\n\t--container-width: 448px;\n\t--submit-btn-height: 48px;\n\t--checkbox-size: 18px;\n\n\t/* Others */\n\t--box-shadow-card: 0px 4px 16px 0px var(--color-card-shadow);\n\t--opacity-placeholder: 0.5;\n}\n\n.multiselect {\n  max-height: 300px;\n  overflow: scroll;\n}"
        }
      },
      "type": "n8n-nodes-base.form",
      "typeVersion": 2.4,
      "position": [
        2448,
        752
      ],
      "id": "a918dd82-2617-44b7-bc1b-b2140a18fcb1",
      "name": "Select Workflow(s)",
      "webhookId": "c4d78021-dfe6-40c7-97f9-58f01ac39c72"
    },
    {
      "parameters": {
        "fieldToSplitOut": "Checkboxes",
        "options": {}
      },
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        2672,
        752
      ],
      "id": "ad2b011a-129f-41e3-9f83-82d3d0640843",
      "name": "Split Out Selected Workflow(s)"
    },
    {
      "parameters": {
        "mode": "combine",
        "advanced": true,
        "mergeByFields": {
          "values": [
            {
              "field1": "name",
              "field2": "Checkboxes"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        2896,
        656
      ],
      "id": "0de121d0-e786-4b70-9c81-15d80d3ea3df",
      "name": "Get Target Repo(s) Data"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "/**\n * Build an n8n-API-safe workflow payload\n * using a strict allowlist (NOT a denylist).\n */\n\n// --------------------\n// Helpers\n// --------------------\n\nfunction pick(obj, allowedKeys) {\n  if (!obj || typeof obj !== 'object') return {};\n  return Object.fromEntries(\n    Object.entries(obj).filter(([key]) => allowedKeys.includes(key))\n  );\n}\n\nfunction cleanNode(node) {\n  return {\n    id: node.id,\n    name: node.name,\n    type: node.type,\n    typeVersion: node.typeVersion,\n    position: node.position,\n    parameters: node.parameters ?? {},\n    credentials: node.credentials ?? {},\n    disabled: node.disabled ?? false,\n    notes: node.notes,\n    notesInFlow: node.notesInFlow,\n  };\n}\n\n// --------------------\n// ENTRY POINT\n// --------------------\n\nconst source = $json.data;\n\n// ---- Top-level allowlist ----\nconst workflow = {\n  name: source.name,\n  nodes: source.nodes.map(cleanNode),\n  connections: source.connections,\n};\n\n// ---- Settings allowlist ----\nconst ALLOWED_SETTINGS = [\n  'timezone',\n  'executionTimeout',\n  'saveExecutionProgress',\n  'saveManualExecutions',\n  'errorWorkflowId',\n];\n\nif (source.settings) {\n  const cleanedSettings = pick(source.settings, ALLOWED_SETTINGS);\n  if (Object.keys(cleanedSettings).length > 0) {\n    workflow.settings = cleanedSettings;\n  }\n}\n\n// ---- Optional staticData ----\nif (source.staticData) {\n  workflow.staticData = source.staticData;\n}\n\nreturn workflow;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        4240,
        736
      ],
      "id": "1ffb37a4-7a6a-4baa-a6e4-78694423b80e",
      "name": "Strip Incompatible API Fields"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        4688,
        656
      ],
      "id": "fcafaeae-46fe-4793-8e83-e22ac7491471",
      "name": "Merge Success and Failed Workflows"
    },
    {
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "options": {}
      },
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        4912,
        656
      ],
      "id": "fda0b3fa-f908-463c-b956-e75dbd9cc342",
      "name": "Aggregate1"
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json.data;\n\nconst options = data\n  .map(item => `{\n  \"option\": \"${item.repo_name}\"}`)\n  .join(\",\");\n\nreturn {\n  json: {\n    options,\n  },\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1104,
        960
      ],
      "id": "2c9ae850-fdc7-489c-8622-f13904bfe76b",
      "name": "Create JSON Repo Options"
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json.allFiles;\n\nconst options = data\n  .map(item => `{\n  \"option\": \"${item.name}\"}`)\n  .join(\",\");\n\nreturn {\n  json: {\n    options,\n  },\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2224,
        752
      ],
      "id": "b833f9ef-035b-4de7-b744-9f14317b8fc4",
      "name": "Create JSON Workflow Options",
      "executeOnce": false
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "c8bc4d4e-6282-4676-87cc-b40e0f02848b",
              "leftValue": "={{ $json.data }}",
              "rightValue": "",
              "operator": {
                "type": "object",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        4016,
        656
      ],
      "id": "a9ef9e01-70b0-46b0-a2f5-013faada87ef",
      "name": "Found Matching Workflow?"
    },
    {
      "parameters": {
        "content": "## Configure Repo Owner\n\nSet the repository owner using the form field provided.\n\nDo not change the field’s label, name, or type. Only update the default value and available options, unless you intend to modify all related variables throughout the workflow.",
        "height": 464,
        "width": 640
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        160,
        784
      ],
      "id": "4313d534-1416-45b1-86de-ae6f0af8c985",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Create Repository Form Data Structure\n\nBuilds the JSON structure required for the repository selection form.\n\nThis section formats the list of available repositories into the structure, allowing the user to dynamically choose which repositories to process.\n\nThe form must be configured in JSON format to support dynamic options.",
        "height": 528,
        "width": 640,
        "color": 2
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        832,
        720
      ],
      "id": "22b1e9e2-38f2-4fb0-aed4-af669a67b25a",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## Loop Through Folders (Simple Explanation)\nThese nodes allow you to loop through and get up regardless of how nested your folder structure is in the order to find all the workflows. \n\n### Steps:\n#### Initialise State:\n```\nreturn [{\n  json: {\n    pendingPaths: [\"\"],   // start at repo root\n    currentPath: \"\",\n    allFiles: []\n  }\n}];\n```\n\nWhat the state allows us to do is, for each folder inside of GitHub, we're able to keep track of any files as well as any other folders. So any files get added to the allFiles array. And any folders get added to the pendingPaths. And then we just use some JavaScript to update the value of our current path, which we add into the GitHub note. \n\n#### Set current path:\n```\n[\n  {\n    \"pendingPaths\": [\n      \"2025\",\n      \"2026\"\n    ],\n    \"currentPath\": \"\",\n    \"allFiles\": [\n      {\n        \"name\": \"README.md\",\n        \"path\": \"README.md\",\n        \"sha\": \"3972337440c5bc299754e633def996\",\n        \"size\": 32,\n        \"url\": \"https://api.github.com/repos/\",\n        \"html_url\": \"https://github.com/\",\n        \"git_url\": \"https://api.github.com/repos/Granite-Marketing/\",\n        \"download_url\": \"https://raw.githubusercontent.com/\",\n        \"type\": \"file\",\n        \"_links\": {\n          \"self\": \"https://api.github.com/repos/README.md?ref=main\",\n          \"git\": \"https://api.github.com/repos/3972337440c5bc299754e633def996cb65cea96c\",\n          \"html\": \"https://github.com/blob/main/README.md\"\n        }\n      }\n    ]\n  }\n]\n```\n\nIn the above example, you can see that we're in the root of the directory where you have the \"Readme\" and then we have two folders. \n\n```\nconst state = $json;\nstate.currentPath = state.pendingPaths.shift();\nreturn [{ json: state }];\n```\n\nWe set the currentPath by using JavaScript shift() method, which just removes the first element from an array and stores it inside of a value. In our example from above, the current path will now become 2025. And if any other folders are found within that 2025 folder, they will get added to the pending path.\n\n### Logic to update state:\n```\nconst state = $('Set Current Path').all()[0].json;\n\nstate.pendingPaths ??= [];\nstate.allFiles ??= [];\n\n// get ALL items from the GitHub node\nconst entries = $input.all().map(i => i.json);\n\nfor (const entry of entries) {\n  if (entry.type === \"dir\") {\n    state.pendingPaths.push(entry.path);\n  } else if (entry.type === \"file\") {\n    state.allFiles.push(entry);\n  }\n}\n\nreturn [{ json: state }];\n```\n\nAs described, this is the logic that updates the state. It basically checks to see if the file that is encountered is either a directory or a file and depending on whether it's a directory or a file is going to add it to the correct location within the state. ",
        "height": 1616,
        "width": 1072,
        "color": 2
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1520,
        1312
      ],
      "id": "c92e003d-d5c8-42a1-a6a4-ee446ce1d3df",
      "name": "Sticky Note2"
    },
    {
      "parameters": {
        "content": "## Loop Through Nested Folders (Deep Explanation)\n\nThis section implements a queue-based folder traversal inside a single workflow execution.\n\nNo recursion.\nNo re-triggering.\nNo cross-execution aggregation.\n\nThe loop is controlled entirely through a single state object.\n\n### State Structure\n```\nreturn [{\n  json: {\n    pendingPaths: [\"\"],   // root folder\n    currentPath: \"\",\n    allFiles: []\n  }\n}];\n```\nMeaning\n\t•\tpendingPaths → Queue of folders left to process\n\t•\tcurrentPath → Folder currently being fetched\n\t•\tallFiles → Accumulated files discovered so far\n\nThe loop continues while:\n\n`pendingPaths.length > 0`\n\n### Step 1 — Select Next Folder\n```\nconst state = $json;\nstate.currentPath = state.pendingPaths.shift();\nreturn [{ json: state }];\n```\n\n* Removes the next folder from the queue\n* Assigns it to currentPath\n* Ensures folders are processed one at a time\n\nThis converts folder traversal into controlled iteration.\n\n### Step 2 — Fetch Folder Contents (GitHub Node)\n\nGitHub returns items with:\n```\n{\n  \"type\": \"file\" | \"dir\",\n  \"path\": \"...\"\n}\n```\nEach item is processed and merged back into state.\n\n### Step 3 — Update State\n```\nconst state = $('Set Current Path').all()[0].json;\n\nstate.pendingPaths ??= [];\nstate.allFiles ??= [];\n\nconst entries = $input.all().map(i => i.json);\n\nfor (const entry of entries) {\n  if (entry.type === \"dir\") {\n    state.pendingPaths.push(entry.path);\n  } else if (entry.type === \"file\") {\n    state.allFiles.push(entry);\n  }\n}\nreturn [{ json: state }];\n```\nBehavior\n\t•\tDirectories → appended to pendingPaths\n\t•\tFiles → appended to allFiles\n\t•\tState returned as a single item\n\nThe loop repeats until no folders remain.\n\n### Termination Condition:\n\nWhen:\n\n`pendingPaths.length === 0`\n\nTraversal is complete.\n\nAt this point:\n\nallFiles = every file in the repository\n\nOnly then do we fan out downstream using Split Out.\n\n### Architectural Summary\n\nThis pattern:\n\t•\tUses a queue (breadth-first traversal)\n\t•\tKeeps execution single-threaded\n\t•\tPrevents node fan-out\n\t•\tAvoids recursion entirely\n\t•\tGuarantees the “finished” branch executes once\n\nThe loop is data-driven, not execution-driven.",
        "height": 2064,
        "width": 656,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        832,
        1312
      ],
      "id": "0039fd01-041c-41aa-a7f2-83f5c3b0214a",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "content": "## Create Workflow File Form Data Structure\n\nThis section processes the allFiles array and prepares the file selection form.\n\nIt:\n* Splits out all files discovered in the repository\n* Builds the JSON structure required for the SELECT_WORKFLOWS form field\n* Allows the user to select specific workflow files to copy\n* Uses a Merge node to match selected files back to the original allFiles array by name\n\nThis ensures only user-selected workflow files continue downstream.",
        "height": 656,
        "width": 1104,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1936,
        272
      ],
      "id": "ab037aab-0572-403f-9cc5-1a2ab9037894",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "content": "## Format Workflows\n\nThis section determines whether a matching workflow file was found by checking if the returned file content is empty or missing.\n\nIt then:\n\n- Strips out any fields incompatible with the n8n API  \n- Constructs a valid workflow payload  \n- Creates or updates the workflow accordingly  \n\nFinally, it merges the results from both found and non-found cases to ensure a consistent downstream output.",
        "height": 592,
        "width": 880,
        "color": 6
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        3952,
        368
      ],
      "id": "e87d89f1-5535-4da1-9f8b-ae15dd5eeb0e",
      "name": "Sticky Note5"
    },
    {
      "parameters": {
        "content": "## Structure Success Message\n\nFormats the final success or error output for each processed file.\n\nThis section uses a simple `map()` function to transform the results into a clean, consistent structure, making it easy to understand which workflows were created, updated, skipped, or failed.",
        "height": 560,
        "width": 464,
        "color": 4
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        4880,
        400
      ],
      "id": "03eed1f4-1d14-4957-9845-9cd32d04447c",
      "name": "Sticky Note6"
    }
  ],
  "pinData": {},
  "connections": {
    "On form submission": {
      "main": [
        [
          {
            "node": "Get repositories for an organization",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get repositories for an organization": {
      "main": [
        [
          {
            "node": "Set Name and URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Create JSON Repo Options",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Name and URL": {
      "main": [
        [
          {
            "node": "Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a file": {
      "main": [
        [
          {
            "node": "Extract JSON From File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract JSON From File": {
      "main": [
        [
          {
            "node": "Set Name and Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limit": {
      "main": [
        [
          {
            "node": "Found Matching Workflow?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Select Repos": {
      "main": [
        [
          {
            "node": "Initialise Loop State",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create a workflow": {
      "main": [
        [
          {
            "node": "Merge Success and Failed Workflows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Name and Data": {
      "main": [
        [
          {
            "node": "Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Results": {
      "main": [
        []
      ]
    },
    "Execute Workflow": {
      "main": [
        [
          {
            "node": "Limit1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Initialise Loop State": {
      "main": [
        [
          {
            "node": "If Has Next Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Has Next Page": {
      "main": [
        [
          {
            "node": "Set Current Path",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Current Path": {
      "main": [
        [
          {
            "node": "List Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Directory Contents": {
      "main": [
        [
          {
            "node": "If Has Next Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "List Files": {
      "main": [
        [
          {
            "node": "Process Directory Contents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "No Operation, do nothing": {
      "main": [
        [
          {
            "node": "Create JSON Workflow Options",
            "type": "main",
            "index": 0
          },
          {
            "node": "Split Out All Workflows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out All Workflows": {
      "main": [
        [
          {
            "node": "Get Target Repo(s) Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Select Workflow(s)": {
      "main": [
        [
          {
            "node": "Split Out Selected Workflow(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out Selected Workflow(s)": {
      "main": [
        [
          {
            "node": "Get Target Repo(s) Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get Target Repo(s) Data": {
      "main": [
        [
          {
            "node": "Get a file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Strip Incompatible API Fields": {
      "main": [
        [
          {
            "node": "Create a workflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Success and Failed Workflows": {
      "main": [
        [
          {
            "node": "Aggregate1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate1": {
      "main": [
        [
          {
            "node": "Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create JSON Repo Options": {
      "main": [
        [
          {
            "node": "Select Repos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create JSON Workflow Options": {
      "main": [
        [
          {
            "node": "Select Workflow(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Found Matching Workflow?": {
      "main": [
        [
          {
            "node": "Strip Incompatible API Fields",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge Success and Failed Workflows",
            "type": "main",
            "index": 1
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false,
    "timeSavedMode": "fixed",
    "errorWorkflow": "UisvlfUaFcbSJoN4",
    "binaryMode": "separate",
    "timeSavedPerExecution": 5
  },
  "versionId": "fd6e45f6-b72e-45ed-a834-10e30398f3f6",
  "meta": {
    "instanceId": "91ff29cf289b94d2caa8a7a4821e53d150affc29af69fb5d8a62fce13e322667"
  },
  "id": "8qy4KXLrI0Wfhmpz",
  "tags": [
    {
      "updatedAt": "2026-02-10T16:54:20.430Z",
      "createdAt": "2026-02-10T16:54:20.430Z",
      "id": "WiPfwUZYcO7Er2qC",
      "name": "Template"
    }
  ]
}