{
  "name": "Claude Comment Handler",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "claude-comment-webhook",
        "options": {}
      },
      "id": "comment-webhook",
      "name": "GitLab Comment Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Process GitLab comment webhook and detect @claude mentions\nconst webhookData = $('GitLab Comment Webhook').first().json.body;\n\n// Only process issue comment events\nif (webhookData.object_kind !== 'note' || webhookData.object_attributes?.noteable_type !== 'Issue') {\n  return [];\n}\n\nconst comment = webhookData.object_attributes.note;\nconst author = webhookData.user;\nconst issue = webhookData.issue;\n\n// Detect @claude mentions\nconst hasClaudeCode = comment.includes('@claude-code');\nconst hasClaudePlan = comment.includes('@claude-plan');\n\nif (!hasClaudeCode && !hasClaudePlan) {\n  return []; // No Claude mention, skip processing\n}\n\n// Extract the command and context\nconst commandType = hasClaudeCode ? 'code' : 'plan';\nconst commandText = comment.replace(/@claude-(code|plan)\\s*/, '').trim();\n\n// Build context\nconst modeInstruction = commandType === 'code' \n  ? 'IMPLEMENTATION MODE: Please analyze the requirement and implement the solution. If the requirement is unclear or too complex, respond with a detailed implementation plan instead. IMPORTANT: work from the main branch unless the user specifically requests a different starting branch.'\n  : 'GUIDANCE MODE: Please provide helpful guidance and directly answer the specific question or request in the comment. Focus on being practical and concise rather than creating extensive documentation.';\n\nconst prompt = `GitLab Issue Command Request\n\nOriginal Issue:\nTitle: ${issue.title}\nDescription: ${issue.description}\nAuthor: ${author.name}\nURL: ${issue.url}\n\nComment from ${author.name}:\n${commandText}\n\nCommand Type: @claude-${commandType}\n\n${modeInstruction}`;\n\nreturn [{\n  json: {\n    prompt: prompt,\n    promptEscaped: prompt.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"'\\\"'\\\"'\"),\n    commandType: commandType,\n    commandText: commandText,\n    commandTextShort: commandText.length > 200 ? commandText.substring(0, 200) + '...' : commandText,\n    issueTitle: issue.title,\n    issueId: issue.iid,\n    issueDescription: issue.description,\n    projectId: webhookData.project.id,\n    projectName: webhookData.project.name,\n    projectPath: webhookData.project.path,\n    commentId: webhookData.object_attributes.id,\n    commentAuthor: author.name,\n    commentUrl: webhookData.object_attributes.url,\n  }\n}];"
      },
      "id": "process-comment",
      "name": "Process Comment",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Generate GitHub-style branch name\nconst processComment = $('Process Comment').item.json;\n\n// Sanitize issue title for branch name\nconst sanitizedTitle = processComment.issueTitle\n  .toLowerCase()\n  .replace(/[^a-z0-9]+/g, '-')\n  .replace(/^-+|-+$/g, '')\n  .substring(0, 50);\n\n// Create branch name following GitHub conventions\nconst branchName = `feature/claude-${processComment.issueId}-${sanitizedTitle}-${processComment.commentId}`;\n\n// Create sanitized commit message\nconst commitMessage = processComment.commandText\n  .replace(/[\"'`\\\\]/g, '')\n  .replace(/\\n/g, ' ')\n  .substring(0, 100)\n  .trim();\n\n// Pass through all data from Process Comment plus the new branch name and commit message\nreturn [{\n  json: {\n    ...processComment,\n    branchName: branchName,\n    commitMessage: commitMessage\n  }\n}];"
      },
      "id": "generate-branch-name",
      "name": "Generate Branch Name",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        580,
        300
      ]
    },
    {
      "parameters": {
        "command": "=cd /workspace/{{ $('Generate Branch Name').item.json.projectName }} && echo '{{ $('Generate Branch Name').item.json.promptEscaped }}' | claude --dangerously-skip-permissions"
      },
      "id": "claude-analysis",
      "name": "Claude Analysis",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        800,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Process Claude response and determine next action\nconst claudeOutput = $json;\nconst commandType = $('Generate Branch Name').item.json.commandType;\n\n// Check for execution errors\nif (claudeOutput.exitCode !== 0) {\n  const errorMessage = `\u26a0\ufe0f **Claude Analysis Error**\\n\\nThere was an issue processing your @claude-${commandType} request.\\n\\nError: ${claudeOutput.stderr || 'Unknown error'}`;\n  return [{ json: { response: errorMessage, actionType: 'comment_only', success: false, error: true } }];\n}\n\nlet analysis = claudeOutput.stdout;\nif (!analysis || analysis.trim() === '') {\n  analysis = `\ud83e\udd16 **No Response Generated**\\n\\nI wasn't able to generate a meaningful response for your @claude-${commandType} request. Please provide more specific details.`;\n}\n\n// For @claude-plan, always just comment\nif (commandType === 'plan') {\n  const planResponse = `\ud83e\udd16 **Claude Guidance** _(Generated for @claude-plan)_\\n\\n${analysis}\\n\\n---\\n*Reply with @claude-plan for more guidance or @claude-code to implement specific parts*`;\n  return [{ json: { response: planResponse, actionType: 'comment_only', success: true, error: false } }];\n}\n\n// For @claude-code, check confidence indicators\nconst confidenceIndicators = ['here\\'s the implementation', 'here is the implementation', 'i can implement', 'here\\'s the code', 'here is the code', '```', 'class ', 'function ', 'const ', 'def ', 'widget ', 'component', 'import ', 'from '];\nconst uncertaintyIndicators = ['i need more information', 'could you clarify', 'what exactly', 'i\\'m not sure', 'can you provide', 'i need to know', 'could you specify', 'what do you mean by', 'i\\'d need to understand', 'questions:', 'follow-up questions', 'to implement this properly'];\n\nconst showsConfidence = confidenceIndicators.some(indicator => analysis.toLowerCase().includes(indicator.toLowerCase()));\nconst showsUncertainty = uncertaintyIndicators.some(indicator => analysis.toLowerCase().includes(indicator.toLowerCase()));\n\n// Try implementation if Claude shows confidence and isn't asking questions\nif (showsConfidence && !showsUncertainty) {\n  return [{ json: { response: analysis, responseEscaped: analysis.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"'\\\"'\\\"'\"), actionType: 'implement', success: true, error: false, implementationDetails: `\ud83e\udd16 **Auto-Implementation Started** _(Generated for @claude-code)_\\n\\n${analysis}\\n\\n---\\n*Proceeding with automatic implementation...*` } }];\n}\n\n// Otherwise, treat as follow-up questions\nconst followUpResponse = `\ud83e\udd16 **Follow-up Questions** _(Generated for @claude-code)_\\n\\n${analysis}\\n\\n---\\n*Please provide the additional details requested above, then use @claude-code again to proceed with implementation*`;\nreturn [{ json: { response: followUpResponse, actionType: 'comment_only', success: true, error: false } }];"
      },
      "id": "process-response",
      "name": "Process Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "condition1",
              "leftValue": "={{ $json.actionType }}",
              "rightValue": "implement",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "check-action-type",
      "name": "Check Action Type",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1120,
        300
      ]
    },
    {
      "parameters": {
        "command": "=cd /workspace/{{ $('Generate Branch Name').item.json.projectName }} && git checkout main && git pull origin main && git checkout -b {{ $('Generate Branch Name').item.json.branchName }} && git push -u origin {{ $('Generate Branch Name').item.json.branchName }}"
      },
      "id": "create-impl-branch",
      "name": "Create Implementation Branch",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        1340,
        200
      ]
    },
    {
      "parameters": {
        "command": "=cd /workspace/{{ $('Generate Branch Name').item.json.projectName }} && git config user.name \"{{ $('Generate Branch Name').item.json.commentAuthor }}\" && git config user.email \"claude-code@automated\" && echo '{{ $('Process Response').item.json.responseEscaped }} After implementing, please commit your changes with an appropriate commit message that describes what you implemented.' | claude --dangerously-skip-permissions"
      },
      "id": "implement-code",
      "name": "Implement Code",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        1560,
        200
      ]
    },
    {
      "parameters": {
        "command": "=cd /workspace/{{ $('Generate Branch Name').item.json.projectName }} && git push -u origin HEAD"
      },
      "id": "commit-impl",
      "name": "Commit Implementation",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        1780,
        200
      ]
    },
    {
      "parameters": {
        "command": "=cd /workspace/{{ $('Generate Branch Name').item.json.projectName }} && glab mr create --title 'Claude Implementation' --description 'Auto-implementation by Claude Code for issue #{{ $('Generate Branch Name').item.json.issueId }}' --source-branch {{ $('Generate Branch Name').item.json.branchName }} --target-branch main"
      },
      "id": "create-mr",
      "name": "Create Merge Request",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        2000,
        200
      ]
    },
    {
      "parameters": {
        "jsCode": "// Process implementation results and prepare comment\nconst commitOutput = $('Commit Implementation').item.json;\nconst glabOutput = $('Create Merge Request').item.json;\nconst processComment = $('Generate Branch Name').item.json;\nconst processResponse = $('Process Response').item.json;\n\n// Check if there were actually changes to commit\nif (commitOutput.stdout && commitOutput.stdout.includes('No changes to commit')) {\n  const noChangesComment = `\ud83e\udd16 **Implementation Analysis Complete**\\n\\nI analyzed your @claude-code request but didn't make any file changes. This could mean:\\n\\n- The implementation was provided as guidance/examples rather than actual code\\n- The requested changes already exist in the codebase\\n- The implementation required clarification before proceeding\\n\\n## Analysis provided:\\n\\n${processResponse.response}\\n\\n---\\n*If you need actual code changes, please use @claude-code with more specific implementation details*`;\n  \n  return [{ json: { comment: noChangesComment, issueId: processComment.issueId, projectName: processComment.projectName } }];\n}\n\n// Extract MR URL from glab output\nlet mrUrl = 'Merge request created';\nif (glabOutput.stdout) {\n  const urlMatch = glabOutput.stdout.match(/(https:\\/\\/[^\\s]+)/);\n  if (urlMatch) { \n    mrUrl = `[Claude Implementation MR](${urlMatch[1]})`;\n  }\n}\n\nconst comment = `\ud83e\udd16 **Implementation Complete**\\n\\nI've implemented your @claude-code request and created a merge request.\\n\\n**MR:** ${mrUrl}\\n**Branch:** \\`claude-${processComment.issueId}-${processComment.commentId}\\`\\n\\n## What was implemented:\\n\\n${processComment.commandText}\\n\\n${processResponse.implementationDetails || processResponse.response}\\n\\nPlease review the changes and merge if they meet your requirements.\\n\\n---\\n*Reply with @claude-plan for implementation guidance or @claude-code for more changes*`;\n\nreturn [{ json: { comment: comment, issueId: processComment.issueId, projectName: processComment.projectName } }];"
      },
      "id": "process-mr-output",
      "name": "Process MR Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2220,
        200
      ]
    },
    {
      "parameters": {
        "operation": "createComment",
        "owner": "={{ $('GitLab Comment Webhook').item.json.body.project.path_with_namespace.split(\"/\")[0] }}",
        "repository": "={{$json.projectName}}",
        "issueNumber": "={{$json.issueId}}",
        "body": "={{$json.comment}}"
      },
      "id": "post-mr-comment",
      "name": "Post MR Comment",
      "type": "n8n-nodes-base.gitlab",
      "typeVersion": 1,
      "position": [
        2440,
        200
      ],
      "credentials": {
        "gitlabApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "createComment",
        "owner": "={{ $('GitLab Comment Webhook').item.json.body.project.path_with_namespace.split(\"/\")[0] }}",
        "repository": "={{$('Generate Branch Name').item.json.projectName}}",
        "issueNumber": "={{$('Generate Branch Name').item.json.issueId}}",
        "body": "={{$('Process Response').item.json.response}}\n\n---\n*Reply with @claude-plan for more guidance or @claude-code to implement specific parts*"
      },
      "id": "post-plan-comment",
      "name": "Post Plan Comment",
      "type": "n8n-nodes-base.gitlab",
      "typeVersion": 1,
      "position": [
        1340,
        400
      ],
      "credentials": {
        "gitlabApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "GitLab Comment Webhook": {
      "main": [
        [
          {
            "node": "Process Comment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Comment": {
      "main": [
        [
          {
            "node": "Generate Branch Name",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Branch Name": {
      "main": [
        [
          {
            "node": "Claude Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude Analysis": {
      "main": [
        [
          {
            "node": "Process Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process Response": {
      "main": [
        [
          {
            "node": "Check Action Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Action Type": {
      "main": [
        [
          {
            "node": "Create Implementation Branch",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Post Plan Comment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Implementation Branch": {
      "main": [
        [
          {
            "node": "Implement Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Implement Code": {
      "main": [
        [
          {
            "node": "Commit Implementation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Commit Implementation": {
      "main": [
        [
          {
            "node": "Create Merge Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Merge Request": {
      "main": [
        [
          {
            "node": "Process MR Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process MR Output": {
      "main": [
        [
          {
            "node": "Post MR Comment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "tags": [
    {
      "name": "Claude Comments",
      "id": "claude-comments"
    },
    {
      "name": "AI Automation",
      "id": "ai-automation"
    }
  ]
}