AutomationFlowsAI & RAG › AI Github Repository Publisher

AI Github Repository Publisher

AI GitHub Repository Publisher. Uses formTrigger, dataTable, gmail, chainLlm. Event-driven trigger; 31 nodes.

Event trigger★★★★★ complexityAI-powered31 nodesForm TriggerData TableGmailChain LlmOpenAI ChatOutput Parser StructuredHTTP RequestGitHub
AI & RAG Trigger: Event Nodes: 31 Complexity: ★★★★★ AI nodes: yes Added:

This workflow follows the Chainllm → Form Trigger recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "AI GitHub Repository Publisher",
  "nodes": [
    {
      "parameters": {
        "formTitle": "AI GitHub Repository Publisher",
        "formDescription": "Submit a project to analyze, secure, prepare and publish to GitHub. Upload a .json file containing an array of { \"path\": \"...\", \"content\": \"...\" } objects.",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Project Name",
              "requiredField": true
            },
            {
              "fieldLabel": "Project Files (JSON)",
              "fieldType": "file",
              "multipleFiles": false,
              "acceptFileTypes": ".json",
              "requiredField": true
            },
            {
              "fieldLabel": "Repository Name"
            },
            {
              "fieldLabel": "Repository Description"
            },
            {
              "fieldLabel": "Repository Visibility",
              "fieldType": "dropdown",
              "fieldOptions": {
                "values": [
                  {
                    "option": "Private"
                  },
                  {
                    "option": "Public"
                  }
                ]
              },
              "requiredField": true
            },
            {
              "fieldLabel": "Approver Email",
              "fieldType": "email",
              "requiredField": true
            }
          ]
        },
        "options": {}
      },
      "id": "c5080f22-3d38-44a0-a236-ba10d729d151",
      "name": "Project Intake Form",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.6,
      "position": [
        0,
        368
      ]
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first();\nconst json = item.json || {};\nconst binary = item.binary || {};\n\nconst visibility = String(json[\"Repository Visibility\"] || \"Private\");\n\nconst base = {\n  project_name: String(json[\"Project Name\"] || \"\").trim(),\n  repo_name_input: String(json[\"Repository Name\"] || \"\").trim(),\n  repo_description_input: String(json[\"Repository Description\"] || \"\").trim(),\n  visibility: visibility,\n  is_private: visibility.toLowerCase() !== \"public\",\n  approver_email: String(json[\"Approver Email\"] || \"\").trim(),\n};\n\nfunction fail(msg) {\n  return [{\n    json: Object.assign({}, base, {\n      files: [],\n      file_count: 0,\n      input_valid: false,\n      error_message: msg,\n      status: \"failed\",\n      error_type: \"missing_project_files\",\n    }),\n  }];\n}\n\n// 1. Make sure a file was uploaded\nconst binaryKeys = Object.keys(binary);\n\nif (binaryKeys.length === 0) {\n  return fail(\n    \"No file was uploaded. Please upload a JSON project file.\"\n  );\n}\n\nconst propName = binaryKeys[0];\nconst bin = binary[propName] || {};\n\n// 2. Validate uploaded file type\nconst fileName = String(bin.fileName || \"\").toLowerCase();\nconst fileExt = String(bin.fileExtension || \"\").toLowerCase();\nconst mime = String(bin.mimeType || \"\").toLowerCase();\n\nconst isJsonFile =\n  fileName.endsWith(\".json\") ||\n  fileExt === \"json\" ||\n  mime.includes(\"json\");\n\nif (!isJsonFile) {\n  return fail(\n    \"The uploaded file is not a JSON file. Please upload a .json file.\"\n  );\n}\n\n// 3. Read binary file as text\nlet text;\n\ntry {\n  const buffer = await this.helpers.getBinaryDataBuffer(0, propName);\n  text = buffer.toString(\"utf8\");\n} catch (e) {\n  try {\n    text = Buffer.from(bin.data || \"\", \"base64\").toString(\"utf8\");\n  } catch (e2) {\n    return fail(\n      \"Could not read the uploaded file: \" +\n      (e && e.message ? e.message : String(e))\n    );\n  }\n}\n\n// 4. Parse JSON\nlet parsed;\n\ntry {\n  parsed = JSON.parse(text);\n} catch (e) {\n  return fail(\n    \"The uploaded file does not contain valid JSON: \" +\n    (e && e.message ? e.message : String(e))\n  );\n}\n\n// 5. Normalize uploaded content into files[]\nlet files = [];\n\n// ----------------------------------------------------\n// FORMAT A:\n// Array of { path, content }\n// ----------------------------------------------------\n\nif (Array.isArray(parsed)) {\n\n  if (parsed.length === 0) {\n    return fail(\n      \"The uploaded JSON array is empty.\"\n    );\n  }\n\n  for (let i = 0; i < parsed.length; i++) {\n    const f = parsed[i];\n\n    if (\n      !f ||\n      typeof f !== \"object\" ||\n      Array.isArray(f)\n    ) {\n      return fail(\n        \"Invalid item at index \" +\n        i +\n        \": each element must be an object with path and content.\"\n      );\n    }\n\n    if (\n      typeof f.path !== \"string\" ||\n      f.path.trim() === \"\"\n    ) {\n      return fail(\n        \"Invalid item at index \" +\n        i +\n        \": missing or empty path.\"\n      );\n    }\n\n    if (\n      f.content === undefined ||\n      f.content === null\n    ) {\n      return fail(\n        \"Invalid item at index \" +\n        i +\n        \": missing content.\"\n      );\n    }\n  }\n\n  files = parsed.map((f) => ({\n    path: String(f.path).trim(),\n    content:\n      typeof f.content === \"string\"\n        ? f.content\n        : JSON.stringify(f.content, null, 2),\n  }));\n\n}\n\n// ----------------------------------------------------\n// FORMAT B:\n// Raw n8n workflow export\n//\n// {\n//   \"name\": \"...\",\n//   \"nodes\": [...],\n//   \"connections\": {...}\n// }\n// ----------------------------------------------------\n\nelse if (\n  parsed &&\n  typeof parsed === \"object\" &&\n  !Array.isArray(parsed) &&\n  Array.isArray(parsed.nodes)\n) {\n\n  // Use workflow name first.\n  // Fall back to uploaded filename.\n  let workflowName =\n    String(parsed.name || \"\").trim() ||\n    String(bin.fileName || \"n8n-workflow\")\n      .replace(/\\.json$/i, \"\")\n      .trim();\n\n  // Create GitHub-safe filename\n  let safeName = workflowName\n    .replace(/&/g, \"and\")\n    .replace(/[^A-Za-z0-9._-]+/g, \"-\")\n    .replace(/-+/g, \"-\")\n    .replace(/^[._-]+|[._-]+$/g, \"\");\n\n  if (!safeName) {\n    safeName = \"n8n-workflow\";\n  }\n\n  files = [\n    {\n      path: safeName + \".json\",\n      content: JSON.stringify(parsed, null, 2),\n    },\n  ];\n\n}\n\n// ----------------------------------------------------\n// Unsupported JSON structure\n// ----------------------------------------------------\n\nelse {\n  return fail(\n    \"Unsupported JSON structure. Upload either a raw n8n workflow export or an array of { path, content } objects.\"\n  );\n}\n\n// 6. Final validation\nif (!files.length) {\n  return fail(\n    \"No valid project files were found in the uploaded JSON.\"\n  );\n}\n\n// 7. Return normalized output\nreturn [{\n  json: Object.assign({}, base, {\n    files: files,\n    file_count: files.length,\n    input_valid: true,\n    error_message: \"\",\n    status: \"ready\",\n    error_type: \"\",\n  }),\n}];"
      },
      "id": "45a78486-4241-4a10-8fd4-7ae9fe4267be",
      "name": "Normalize Input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        224,
        368
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.input_valid }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "0b05ec21-6ac5-4dd1-b8d7-dc3ad537f1d1",
      "name": "Valid Project Files?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        448,
        368
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst files = data.files || [];\nconst paths = files.map(f => f.path.toLowerCase());\nfunction has(name){ return paths.some(p => p === name || p.endsWith(\"/\" + name)); }\nfunction hasExt(ext){ return paths.some(p => p.endsWith(ext)); }\nfunction read(name){ const f = files.find(x => x.path.toLowerCase() === name || x.path.toLowerCase().endsWith(\"/\" + name)); return f ? f.content : \"\"; }\nlet language = \"Unknown\";\nif (has(\"package.json\")) language = \"JavaScript/TypeScript\";\nelse if (has(\"requirements.txt\") || has(\"pyproject.toml\") || hasExt(\".py\")) language = \"Python\";\nelse if (has(\"go.mod\")) language = \"Go\";\nelse if (has(\"pom.xml\") || has(\"build.gradle\")) language = \"Java\";\nelse if (has(\"cargo.toml\")) language = \"Rust\";\nelse if (hasExt(\".rb\")) language = \"Ruby\";\nelse if (hasExt(\".php\")) language = \"PHP\";\nlet framework = \"None detected\";\nlet dependencies = [];\nconst pkgRaw = read(\"package.json\");\nif (pkgRaw) {\n  try {\n    const pkg = JSON.parse(pkgRaw);\n    dependencies = Object.keys(pkg.dependencies || {}).concat(Object.keys(pkg.devDependencies || {}));\n    if (dependencies.includes(\"next\")) framework = \"Next.js\";\n    else if (dependencies.includes(\"react\")) framework = \"React\";\n    else if (dependencies.includes(\"express\")) framework = \"Express\";\n    else if (dependencies.includes(\"vue\")) framework = \"Vue\";\n    else if (dependencies.includes(\"@nestjs/core\")) framework = \"NestJS\";\n  } catch (e) {}\n}\nconst reqRaw = read(\"requirements.txt\");\nif (reqRaw) {\n  dependencies = dependencies.concat(reqRaw.split(\"\\n\").map(l => l.trim()).filter(l => l && !l.startsWith(\"#\")));\n  const low = reqRaw.toLowerCase();\n  if (low.includes(\"django\")) framework = \"Django\";\n  else if (low.includes(\"flask\")) framework = \"Flask\";\n  else if (low.includes(\"fastapi\")) framework = \"FastAPI\";\n}\nlet entry = \"Unknown\";\nconst candidates = [\"index.js\",\"index.ts\",\"main.py\",\"app.py\",\"server.js\",\"main.go\",\"src/index.js\",\"src/main.ts\",\"app.js\"];\nfor (const c of candidates) { if (paths.includes(c)) { entry = c; break; } }\nlet envVars = [];\nconst envRaw = read(\".env\") || read(\".env.example\");\nif (envRaw) { envVars = envRaw.split(\"\\n\").map(l => l.split(\"=\")[0].trim()).filter(k => k && !k.startsWith(\"#\")); }\nlet projectType = \"library\";\nif ([\"Next.js\",\"React\",\"Vue\"].includes(framework)) projectType = \"web-frontend\";\nelse if ([\"Express\",\"NestJS\",\"Django\",\"Flask\",\"FastAPI\"].includes(framework)) projectType = \"web-backend\";\nelse if (entry !== \"Unknown\") projectType = \"application\";\nconst analysis = {\n  project_name: data.project_name,\n  project_type: projectType,\n  language: language,\n  framework: framework,\n  entry_point: entry,\n  dependencies: dependencies.slice(0, 60),\n  environment_variables: envVars,\n  readme_exists: has(\"readme.md\") || has(\"readme\"),\n  gitignore_exists: has(\".gitignore\"),\n};\nreturn [{ json: { ...data, analysis } }];"
      },
      "id": "87314fa5-8c11-443a-9a9f-3fe3125ac51a",
      "name": "Project Analysis",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        672,
        240
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst files = data.files || [];\nconst excludePatterns = [\n  /(^|\\/)\\.env(\\..*)?$/i, /(^|\\/)node_modules\\//i, /(^|\\/)\\.venv\\//i, /(^|\\/)venv\\//i,\n  /(^|\\/)__pycache__\\//i, /\\.pyc$/i, /(^|\\/)\\.git\\//i, /\\.log$/i, /(^|\\/)\\.ds_store$/i,\n  /(^|\\/)dist\\//i, /(^|\\/)build\\//i, /\\.(pem|key|p12|pfx)$/i, /id_rsa/i, /(^|\\/)\\.cache\\//i,\n  /\\.sqlite3?$/i, /\\.dump$/i, /credentials\\.json$/i, /service.?account.*\\.json$/i, /secrets?\\.(json|ya?ml)$/i,\n];\nconst secretContent = [\n  { name: \"AWS access key\", re: /AKIA[0-9A-Z]{16}/ },\n  { name: \"Private key block\", re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },\n  { name: \"Hardcoded credential assignment\", re: /(api[_-]?key|secret|token|passwd|password)\\s*[:=]\\s*['\"][^'\"]{8,}/i },\n  { name: \"Bearer token\", re: /bearer\\s+[A-Za-z0-9\\-._~+\\/]{20,}/i },\n  { name: \"Slack token\", re: /xox[baprs]-[0-9A-Za-z-]{10,}/ },\n  { name: \"Google API key\", re: /AIza[0-9A-Za-z\\-_]{35}/ },\n];\nconst excluded = [];\nconst safe = [];\nconst warnings = [];\nlet hasSecrets = false;\nfor (const f of files) {\n  const pathMatch = excludePatterns.find(re => re.test(f.path));\n  if (pathMatch) { excluded.push({ path: f.path, reason: \"excluded path/type\" }); continue; }\n  const hit = secretContent.find(s => s.re.test(f.content || \"\"));\n  if (hit) {\n    hasSecrets = true;\n    excluded.push({ path: f.path, reason: \"possible secret: \" + hit.name });\n    warnings.push(\"Possible \" + hit.name + \" detected in \" + f.path);\n    continue;\n  }\n  safe.push({ path: f.path, content: f.content });\n}\nreturn [{ json: { ...data, excluded_files: excluded, safe_files: safe, security_warnings: warnings, has_secrets: hasSecrets } }];"
      },
      "id": "bc831bab-5f99-4181-b864-b5f68736d94e",
      "name": "Security Validation",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        896,
        240
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.has_secrets }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      },
      "id": "5a49c46c-8827-4348-92fc-0803f4ea961e",
      "name": "Secrets Detected?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1120,
        240
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "status",
              "value": "manual_review",
              "type": "string"
            },
            {
              "id": "a2",
              "name": "error_type",
              "value": "secret_detected",
              "type": "string"
            },
            {
              "id": "a3",
              "name": "error_message",
              "value": "=Possible secrets detected. Publishing halted for manual review. Warnings: {{ JSON.stringify($json.security_warnings) }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "id": "f12d589c-28e4-46be-9dac-79ee06f276e5",
      "name": "Route: Manual Review",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.5,
      "position": [
        3040,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const j = $input.first().json;\nfunction safe(fn, def){ try { return fn(); } catch (e) { return def; } }\nconst projectName = safe(() => $(\"Normalize Input\").item.json.project_name, j.project_name || \"\");\nconst approver = safe(() => $(\"Normalize Input\").item.json.approver_email, \"\");\nconst repoName = safe(() => $(\"Merge AI Output\").item.json.repository_name, j.repository_name || \"\");\nconst excluded = safe(() => $(\"Security Validation\").item.json.excluded_files.map(f => f.path), []);\nconst warnings = safe(() => $(\"Security Validation\").item.json.security_warnings, []);\nreturn [{ json: {\n  execution_id: $execution.id,\n  project_name: projectName,\n  approver_email: approver,\n  repository_name: repoName,\n  repository_url: \"\",\n  status: j.status || \"failed\",\n  error_type: j.error_type || \"unknown_error\",\n  error_message: j.error_message || \"Unspecified error\",\n  files_uploaded: [],\n  files_excluded: excluded,\n  warnings: warnings,\n} }];"
      },
      "id": "d3498602-dd91-49b5-9168-62bccbde0597",
      "name": "Build Failure Log",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3264,
        320
      ]
    },
    {
      "parameters": {
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "KuAcmnMiC6MtzpD3",
          "cachedResultName": "github_publish_log"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "execution_id": "={{ $json.execution_id }}",
            "project_name": "={{ $json.project_name }}",
            "repository_name": "={{ $json.repository_name }}",
            "repository_url": "={{ $json.repository_url }}",
            "status": "={{ $json.status }}",
            "files_uploaded": "={{ JSON.stringify($json.files_uploaded) }}",
            "files_excluded": "={{ JSON.stringify($json.files_excluded) }}",
            "warnings": "={{ JSON.stringify(($json.warnings || []).concat([$json.error_type + \": \" + $json.error_message])) }}",
            "created_at": "={{ $now.toISO() }}"
          },
          "schema": [
            {
              "id": "execution_id",
              "displayName": "execution_id",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "project_name",
              "displayName": "project_name",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "repository_name",
              "displayName": "repository_name",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "repository_url",
              "displayName": "repository_url",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "status",
              "displayName": "status",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "files_uploaded",
              "displayName": "files_uploaded",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "files_excluded",
              "displayName": "files_excluded",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "warnings",
              "displayName": "warnings",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "type": "dateTime",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            }
          ]
        },
        "options": {}
      },
      "id": "e6629376-39f1-40ea-aebe-ed9db099e612",
      "name": "Log Failure",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        3488,
        320
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Build Failure Log\").item.json.approver_email }}",
        "subject": "=GitHub publish {{ $(\"Build Failure Log\").item.json.status }}: {{ $(\"Build Failure Log\").item.json.project_name }}",
        "message": "=<h2>Publishing did not complete</h2>\n<p><b>Project:</b> {{ $(\"Build Failure Log\").item.json.project_name }}</p>\n<p><b>Status:</b> {{ $(\"Build Failure Log\").item.json.status }}</p>\n<p><b>Reason:</b> {{ $(\"Build Failure Log\").item.json.error_type }} \u2014 {{ $(\"Build Failure Log\").item.json.error_message }}</p>\n<p><b>Excluded files:</b> {{ ($(\"Build Failure Log\").item.json.files_excluded || []).join(\", \") }}</p>",
        "options": {}
      },
      "id": "cbe79d0e-f35d-4aca-be40-462801577274",
      "name": "Notify Failure",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        3712,
        320
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=You are a senior open-source maintainer preparing a software project for publishing to GitHub.\n\nYour job is to generate repository metadata and a PROJECT-SPECIFIC README based strictly on the supplied project analysis and safe file list.\n\nDo NOT generate a generic README template.\n\nPROJECT NAME:\n{{ $json.project_name }}\n\nUSER-PROVIDED REPO NAME:\n{{ $json.repo_name_input }}\n\nUSER-PROVIDED DESCRIPTION:\n{{ $json.repo_description_input }}\n\nPROJECT ANALYSIS:\n{{ JSON.stringify($json.analysis) }}\n\nSAFE FILE PATHS:\n{{ JSON.stringify($json.safe_files.map(f => f.path)) }}\n\nREADME ALREADY EXISTS:\n{{ $json.analysis.readme_exists }}\n\nGITIGNORE ALREADY EXISTS:\n{{ $json.analysis.gitignore_exists }}\n\n## Repository Metadata\n\nGenerate:\n\n* `repository_name`\n\n  * Use the user-provided repository name when available.\n  * Otherwise generate a valid kebab-case GitHub repository name based on the actual project.\n\n* `description`\n\n  * Maximum 120 characters.\n  * Describe the actual purpose of the project.\n\n* `topics`\n\n  * Generate 3-8 lowercase GitHub topics.\n  * Topics must be supported by the detected technologies, integrations, or project purpose.\n\n* `project_summary`\n\n  * One concise paragraph explaining what the project actually does.\n\n* `tech_stack`\n\n  * Include only technologies, frameworks, APIs, platforms, databases, AI models, or services supported by the analysis.\n\n## README Generation\n\nGenerate `readme_content` as a complete GitHub README.md in Markdown.\n\nThe README MUST be specific to this project.\n\nUse information from `PROJECT ANALYSIS` to explain the actual architecture, processing flow, integrations, and behavior.\n\nWhen the project is a workflow or automation project, identify and explain, when supported:\n\n* Trigger or input source\n* Main processing stages\n* Routing or conditional logic\n* APIs and external integrations\n* AI or LLM components\n* Data extraction or transformation\n* Storage or database destinations\n* Notifications or outputs\n* Important validation or normalization steps\n\nDo NOT simply produce generic statements such as:\n\n\"This project provides an efficient and scalable solution.\"\n\nInstead explain what THIS project actually receives, processes, routes, analyzes, stores, or sends.\n\n### Required README sections\n\n# Project Title\n\n## Summary\n\nExplain the real purpose of the project and the problem it automates.\n\n## How It Works\n\nDescribe the actual processing flow using information found in the project analysis.\n\nWhen enough information is available, include a simple text flow such as:\n\nInput \u2192 Processing \u2192 Decision/Route \u2192 Output\n\nDo not invent stages that are not supported by the analysis.\n\n## Key Features\n\nList only features directly supported by the project analysis.\n\n## Tech Stack\n\nList only detected technologies and integrations.\n\n## Setup Instructions\n\nProvide setup instructions only for requirements that can reasonably be inferred from the analysis.\n\nFor credentials or external services, say that the appropriate credentials must be configured.\n\nNever include actual credential values, IDs, API keys, tokens, webhook IDs, email addresses, chat IDs, or secrets.\n\nDo NOT invent environment variables, commands, package installation steps, dependencies, ports, or configuration files that are not supported by the project.\n\nFor an n8n workflow, setup may include importing the workflow JSON and configuring the detected service credentials.\n\n## Usage\n\nExplain how the actual project is triggered and what result/output the user should expect.\n\n## Security Notes\n\nWhen applicable, mention that credentials and secrets should not be committed to the repository.\n\n## Limitations\n\nInclude limitations only when they can be reasonably identified from the supplied analysis.\n\n## .gitignore Generation\n\nGenerate `gitignore_content` appropriate for the detected project type.\n\nIf the project is primarily an exported n8n workflow and no traditional programming runtime is detected, keep the `.gitignore` minimal rather than inventing Node.js or Python dependencies.\n\nCommon sensitive files such as `.env`, private keys, and local credential files may be excluded when appropriate.\n\n## Critical Rules\n\n* Base all claims on PROJECT ANALYSIS or SAFE FILE PATHS.\n* Do not invent functionality.\n* Do not invent dependencies.\n* Do not invent installation commands.\n* Do not invent environment variables.\n* Do not expose credentials or identifiers.\n* Do not claim integrations that are not detected.\n* Do not describe planned features as existing features.\n* Prefer specific descriptions over generic software-development language.\n* If information is unavailable, omit it rather than guessing.\n* Return ONLY structured JSON matching the required output schema.\n",
        "hasOutputParser": true,
        "batching": {}
      },
      "id": "466fe085-4137-4f10-9e6b-7bb132b5698c",
      "name": "AI Repo Preparation",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        1344,
        432
      ]
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5-mini"
        },
        "builtInTools": {},
        "options": {}
      },
      "id": "8486c5c3-82b6-4f51-af5e-27e2e23e5e4f",
      "name": "Preparation Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        1344,
        656
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsonSchemaExample": "{\"repository_name\":\"my-project\",\"description\":\"A short repository description\",\"topics\":[\"nodejs\",\"automation\"],\"project_summary\":\"One paragraph summary\",\"tech_stack\":[\"Node.js\",\"Express\"],\"readme_content\":\"# My Project\\n\\n...\",\"gitignore_content\":\"node_modules/\\n.env\"}"
      },
      "id": "f5110bf8-8147-4b5c-9d7a-525923bc5c56",
      "name": "Repo Metadata Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "typeVersion": 1.3,
      "position": [
        1488,
        656
      ]
    },
    {
      "parameters": {
        "jsCode": "const ai = ($(\"AI Repo Preparation\").item.json.output) || {};\nconst norm = $(\"Normalize Input\").item.json;\nconst analysis = $(\"Project Analysis\").item.json.analysis || {};\nconst sec = $(\"Security Validation\").item.json;\nconst repoName = String(norm.repo_name_input || ai.repository_name || norm.project_name || \"\").trim();\nconst description = String(norm.repo_description_input || ai.description || \"\").trim();\nreturn [{ json: {\n  project_name: norm.project_name,\n  is_private: norm.is_private,\n  approver_email: norm.approver_email,\n  analysis: analysis,\n  safe_files: sec.safe_files || [],\n  excluded_files: sec.excluded_files || [],\n  security_warnings: sec.security_warnings || [],\n  repository_name: repoName,\n  description: description,\n  topics: Array.isArray(ai.topics) ? ai.topics : [],\n  project_summary: String(ai.project_summary || \"\"),\n  tech_stack: Array.isArray(ai.tech_stack) ? ai.tech_stack : [],\n  readme_content: String(ai.readme_content || \"\"),\n  gitignore_content: String(ai.gitignore_content || \"\"),\n} }];"
      },
      "id": "b08de021-ea03-4531-9f38-79bdfe23b303",
      "name": "Merge AI Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1696,
        432
      ]
    },
    {
      "parameters": {
        "jsCode": "const d = $input.first().json;\nconst errors = [];\nconst nameRe = /^[A-Za-z0-9._-]+$/;\nif (!d.repository_name || !nameRe.test(d.repository_name)) errors.push(\"Invalid or missing repository_name\");\nif (!d.readme_content || d.readme_content.length < 20) errors.push(\"README content missing or too short\");\nif (!Array.isArray(d.topics)) errors.push(\"topics must be an array\");\nif (!Array.isArray(d.tech_stack)) errors.push(\"tech_stack must be an array\");\nif (!Array.isArray(d.safe_files) || d.safe_files.length === 0) errors.push(\"No safe files to publish\");\nconst prohibited = /(^|\\/)\\.env(\\..*)?$|(^|\\/)node_modules\\/|\\.(pem|key|p12|pfx)$|id_rsa/i;\nconst leaked = (d.safe_files || []).filter(f => prohibited.test(f.path));\nif (leaked.length) errors.push(\"Prohibited files present in safe list: \" + leaked.map(f => f.path).join(\", \"));\nconst validation_ok = errors.length === 0;\nreturn [{ json: { ...d, validation_ok, validation_errors: errors } }];"
      },
      "id": "bfd944f0-cad9-484f-b87a-09404ddbc390",
      "name": "Deterministic Validation",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1920,
        432
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.validation_ok }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      },
      "id": "95d8b6ab-6a5f-47a0-9317-3f0bb2ec9e8e",
      "name": "AI Output Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        2144,
        432
      ]
    },
    {
      "parameters": {
        "operation": "sendAndWait",
        "sendTo": "={{ $json.approver_email }}",
        "subject": "=Approve GitHub publish: {{ $json.repository_name }}",
        "message": "=<h2>Repository Publish Preview</h2>\n<p><b>Repository:</b> {{ $json.repository_name }} ({{ $json.is_private ? \"private\" : \"public\" }})</p>\n<p><b>Description:</b> {{ $json.description }}</p>\n<p><b>Tech stack:</b> {{ $json.tech_stack.join(\", \") }}</p>\n<p><b>Topics:</b> {{ $json.topics.join(\", \") }}</p>\n<p><b>Files to upload ({{ $json.safe_files.length }}):</b> {{ $json.safe_files.map(f => f.path).join(\", \") }}</p>\n<p><b>Files excluded ({{ $json.excluded_files.length }}):</b> {{ $json.excluded_files.map(f => f.path).join(\", \") }}</p>\n<p><b>Security warnings:</b> {{ $json.security_warnings.length ? $json.security_warnings.join(\"; \") : \"none\" }}</p>\n<h3>README preview</h3>\n<pre>{{ $json.readme_content.slice(0, 1200) }}</pre>",
        "approvalOptions": {
          "values": {
            "approvalType": "double",
            "approveLabel": "Publish",
            "disapproveLabel": "Reject"
          }
        },
        "options": {}
      },
      "id": "a013208e-8a17-4ff3-93d8-4e36d96fb032",
      "name": "Human Approval",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        2368,
        336
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.data.approved }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      },
      "id": "e7403747-0d62-4fb5-91d7-e5dfc954ce3d",
      "name": "Approved?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        2592,
        336
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.github.com/user/repos",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ name: $(\"Merge AI Output\").item.json.repository_name, description: $(\"Merge AI Output\").item.json.description, private: $(\"Merge AI Output\").item.json.is_private, auto_init: false }) }}",
        "options": {}
      },
      "id": "c97aebc9-f5e3-4fd9-998e-27141f28f36d",
      "name": "Create GitHub Repo",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        2816,
        320
      ],
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "jsCode": "const meta = $(\"Merge AI Output\").item.json;\nconst repo = $(\"Create GitHub Repo\").item.json;\nconst owner = (repo.owner && repo.owner.login) ? repo.owner.login : \"\";\nconst repoName = repo.name || meta.repository_name;\nconst repoUrl = repo.html_url || \"\";\nconst out = [];\nconst seen = {};\nfor (const f of (meta.safe_files || [])) {\n  seen[f.path.toLowerCase()] = true;\n  out.push({ path: f.path, content: f.content });\n}\nif (!seen[\"readme.md\"] && meta.readme_content) out.push({ path: \"README.md\", content: meta.readme_content });\nif (!seen[\".gitignore\"] && meta.gitignore_content) out.push({ path: \".gitignore\", content: meta.gitignore_content });\nreturn out.map(f => ({ json: { path: f.path, content: f.content, owner_login: owner, repo_name: repoName, repo_url: repoUrl } }));"
      },
      "id": "343ba6e0-2d91-43ed-95c6-605a0bc21eb4",
      "name": "Prepare Upload List",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3040,
        272
      ]
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "=https://api.github.com/repos/{{ $(\"Create GitHub Repo\").item.json.owner.login }}/{{ $(\"Create GitHub Repo\").item.json.name }}/topics",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/vnd.github+json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ names: $(\"Merge AI Output\").item.json.topics }) }}",
        "options": {}
      },
      "id": "62dc45f2-cdd5-4dce-95ce-1fac67feb7c1",
      "name": "Apply Topics",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        3488,
        512
      ],
      "executeOnce": true,
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "url": "=https://api.github.com/repos/{{ $(\"Create GitHub Repo\").item.json.owner.login }}/{{ $(\"Create GitHub Repo\").item.json.name }}/contents/",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "githubApi",
        "options": {}
      },
      "id": "2edc9aeb-d9ae-4c58-a508-79786df1c1c3",
      "name": "Verify Repo",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        3712,
        512
      ],
      "executeOnce": true,
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const meta = $(\"Merge AI Output\").item.json;\nconst repo = $(\"Create GitHub Repo\").item.json;\nlet remote = [];\ntry { const r = $(\"Verify Repo\").all(); remote = r.map(i => i.json).flat(); } catch (e) { remote = []; }\nconst remoteNames = remote.map(x => (x && x.name) ? String(x.name).toLowerCase() : \"\").filter(Boolean);\nconst uploaded = $(\"Prepare Upload List\").all().map(i => i.json.path);\nconst readmeExists = remoteNames.includes(\"readme.md\") || uploaded.map(p => p.toLowerCase()).includes(\"readme.md\");\nconst excluded = (meta.excluded_files || []).map(f => f.path);\nconst leaked = remoteNames.filter(n => /^\\.env|id_rsa|\\.(pem|key)$/.test(n));\nconst warnings = (meta.security_warnings || []).slice();\nif (leaked.length) warnings.push(\"Sensitive file may have been uploaded: \" + leaked.join(\", \"));\nconst success = !!(repo && repo.html_url) && uploaded.length > 0 && leaked.length === 0;\nreturn [{ json: {\n  success: success,\n  repository_name: repo.name || meta.repository_name,\n  repository_url: repo.html_url || \"\",\n  commit_status: uploaded.length > 0 ? \"committed\" : \"no_files\",\n  uploaded_files: uploaded,\n  excluded_files: excluded,\n  warnings: warnings,\n  readme_exists: readmeExists,\n  project_name: meta.project_name,\n  status: success ? \"published\" : \"failed\",\n} }];"
      },
      "id": "782f03c2-d219-4140-92d8-1ee5a9abe8e0",
      "name": "Publishing Validation",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3936,
        512
      ]
    },
    {
      "parameters": {
        "dataTableId": {
          "__rl": true,
          "mode": "id",
          "value": "KuAcmnMiC6MtzpD3",
          "cachedResultName": "github_publish_log"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "execution_id": "={{ $execution.id }}",
            "project_name": "={{ $json.project_name }}",
            "repository_name": "={{ $json.repository_name }}",
            "repository_url": "={{ $json.repository_url }}",
            "status": "={{ $json.status }}",
            "files_uploaded": "={{ JSON.stringify($json.uploaded_files) }}",
            "files_excluded": "={{ JSON.stringify($json.excluded_files) }}",
            "warnings": "={{ JSON.stringify($json.warnings) }}",
            "created_at": "={{ $now.toISO() }}"
          },
          "schema": [
            {
              "id": "execution_id",
              "displayName": "execution_id",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "project_name",
              "displayName": "project_name",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "repository_name",
              "displayName": "repository_name",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "repository_url",
              "displayName": "repository_url",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "status",
              "displayName": "status",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "files_uploaded",
              "displayName": "files_uploaded",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "files_excluded",
              "displayName": "files_excluded",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "warnings",
              "displayName": "warnings",
              "type": "string",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "type": "dateTime",
              "canBeUsedToMatch": true,
              "display": true,
              "required": false,
              "defaultMatch": false
            }
          ]
        },
        "options": {}
      },
      "id": "631dc864-b231-4fb4-8504-ce6e4a09869c",
      "name": "Log Success",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1.1,
      "position": [
        4160,
        512
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Merge AI Output\").item.json.approver_email }}",
        "subject": "=Published: {{ $json.repository_name }}",
        "message": "=<h2>Repository published successfully</h2>\n<p><b>Repository:</b> <a href=\"{{ $json.repository_url }}\">{{ $json.repository_url }}</a></p>\n<p><b>Status:</b> {{ $json.commit_status }}</p>\n<p><b>Uploaded files:</b> {{ $json.uploaded_files.join(\", \") }}</p>\n<p><b>Excluded files:</b> {{ $json.excluded_files.join(\", \") }}</p>\n<p><b>Warnings:</b> {{ $json.warnings.length ? $json.warnings.join(\"; \") : \"none\" }}</p>",
        "options": {}
      },
      "id": "dae23ca0-ba16-43e5-b8e9-46bfb402d6e3",
      "name": "Notify Success",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        4384,
        512
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "resource": "file",
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.owner_login }}"
        },
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "={{ $json.repo_name }}"
        },
        "filePath": "={{ $json.path }}",
        "fileContent": "={{ $json.content }}",
        "commitMessage": "=Add {{ $json.path }}"
      },
      "id": "fe2552dc-ff67-402d-a7b9-e2f4d2473887",
      "name": "Upload File",
      "type": "n8n-nodes-base.github",
      "typeVersion": 1.1,
      "position": [
        3488,
        704
      ],
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "0664a82c-e59e-47a4-8183-ac013a6f36cc",
      "name": "Loop Files",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        3264,
        608
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "status",
              "value": "failed",
              "type": "string"
            },
            {
              "id": "a2",
              "name": "error_type",
              "value": "github_repo_create_failed",
              "type": "string"
            },
            {
              "id": "a3",
              "name": "error_message",
              "value": "=GitHub repository creation failed (auth failure, repo already exists, or rate limit). Detail: {{ JSON.stringify($json.error || $json) }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "id": "972c16b1-371a-4ee3-8f86-da2e463d469d",
      "name": "Err: Repo Create Failed",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.5,
      "position": [
        3040,
        464
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "status",
              "value": "rejected",
              "type": "string"
            },
            {
              "id": "a2",
              "name": "error_type",
              "value": "human_rejected",
              "type": "string"
            },
            {
              "id": "a3",
              "name": "error_message",
              "value": "Publishing was rejected by the approver.",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "id": "98afd44a-f373-470c-991b-d6cd1acee087",
      "name": "Err: Rejected",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.5,
      "position": [
        3040,
        656
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "status",
              "value": "failed",
              "type": "string"
            },
            {
              "id": "a2",
              "name": "error_type",
              "value": "invalid_ai_output",
              "type": "string"
            },
            {
              "id": "a3",
              "name": "error_message",
              "value": "=AI output failed validation: {{ JSON.stringify($json.validation_errors) }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "id": "7fa4ff34-3c58-4f72-8e0e-6e294523c36b",
      "name": "Err: Invalid AI Output",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.5,
      "position": [
        3040,
        848
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "status",
              "value": "failed",
              "type": "string"
            },
            {
              "id": "a2",
              "name": "error_type",
              "value": "missing_project_files",
              "type": "string"
            },
            {
              "id": "a3",
              "name": "error_message",
              "value": "={{ $json.error_message || \"No valid project files were found in the uploaded JSON file.\" }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "id": "6dce4893-23a4-47f1-8451-86275560984d",
      "name": "Err: Missing Files",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.5,
      "position": [
        3040,
        1072
      ]
    }
  ],
  "connections": {
    "Project Intake Form": {
      "main": [
        [
          {
            "node": "Normalize Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Input": {
      "main": [
        [
          {
            "node": "Valid Project Files?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Valid Project Files?": {
      "main": [
        [
          {
            "node": "Project Analysis",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Err: Missing Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Project Analysis": {
      "main": [
        [
          {
            "node": "Security Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Security Validation": {
      "main": [
        [
          {
            "node": "Secrets Detected?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Secrets Detected?": {
      "main": [
        [
          {
            "node": "Route: Manual Review",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "AI Repo Preparation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route: Manual Review": {
      "main": [
        [
          {
            "node": "Build Failure Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Failure Log": {
      "main": [
        [
          {
            "node": "Log Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Failure": {
      "main": [
        [
          {
            "node": "Notify Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Repo Preparation": {
      "main": [
        [
          {
            "node": "Merge AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparation Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Repo Preparation",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Repo Metadata Parser": {
      "ai_outputParser": [
        [
          {
            "node": "AI Repo Preparation",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Merge AI Output": {
      "main": [
        [
          {
            "node": "Deterministic Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deterministic Validation": {
      "main": [
        [
          {
            "node": "AI Output Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Output Valid?": {
      "main": [
        [
          {
            "node": "Human Approval",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Err: Invalid AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Human Approval": {
      "main": [
        [
          {
            "node": "Approved?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Approved?": {
      "main": [
        [
          {
            "node": "Create GitHub Repo",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Err: Rejected",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create GitHub Repo": {
      "main": [
        [
          {
            "node": "Prepare Upload List",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Err: Repo Create Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Upload List": {
      "main": [
        [
          {
            "node": "Loop Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apply Topics": {
      "main": [
        [
          {
            "node": "Verify Repo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verify Repo": {
      "main": [
        [
          {
            "node": "Publishing Validation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publishing Validation": {
      "main": [
        [
          {
            "node": "Log Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Success": {
      "main": [
        [
          {
            "node": "Notify Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload File": {
      "main": [
        [
          {
            "node": "Loop Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Files": {
      "main": [
        [
          {
            "node": "Apply Topics",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Upload File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Err: Repo Create Failed": {
      "main": [
        [
          {
            "node": "Build Failure Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Err: Rejected": {
      "main": [
        [
          {
            "node": "Build Failure Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Err: Invalid AI Output": {
      "main": [
        [
          {
            "node": "Build Failure Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Err: Missing Files": {
      "main": [
        [
          {
            "node": "Build Failure Log",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "f907a238-2996-43ca-8d03-5653f5eae482",
  "nodeGroups": [],
  "id": "Ewr5tFn1RW0E4U4F",
  "tags": []
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

AI GitHub Repository Publisher. Uses formTrigger, dataTable, gmail, chainLlm. Event-driven trigger; 31 nodes.

Source: https://github.com/markt7383-lgtm/ai-github-repository-publisher/blob/main/AI-GitHub-Repository-Publisher.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This workflow is perfect for: Agile development teams and project managers who need to quickly set up Jira projects Product managers who want to convert feature ideas into structured user stories and

Form Trigger, OpenAI Chat, Output Parser Structured +5
AI & RAG

This workflow automates end-to-end contract and invoice management using AI intelligence. It processes proposals through intelligent contract generation, approval workflows, and automated invoicing. O

Form Trigger, Data Table, Agent +4
AI & RAG

Automates SaaS operations by consolidating user management, AI-driven support triage, analytics, and billing into one unified system. User signups flow through registration, support requests route via

Form Trigger, Data Table, Agent +7
AI & RAG

The workflow runs every hour with a randomized delay of 5–20 minutes to help distribute load. It records the exact date and time a lead is emailed so you can track outreach. Follow-ups are automatical

Google Sheets, Agent, OpenAI Chat +5
AI & RAG

This workflow is perfect for graphic designers, creative agencies, marketing teams, or freelancers who regularly use AI-generated images in their projects. It's specifically beneficial for teams that

Google Sheets, Google Drive, HTTP Request +5