{ "id": "7kAZyLHOpYKg4riN", "name": "gemini-cli-vscode", "nodes": [ { "parameters": { "jsCode": "// Defaults and Input Parsing\nconst input = items[0].json;\n\n// 1. Prompt Handling (Escape quotes)\nconst rawPrompt = input.prompt || \"\";\nconst safePrompt = rawPrompt.replace(/\"/g, '\\\\\"');\n\n// 2. Defaults\nconst model = input.model || \"gemini-3-flash-preview\";\nconst approvalMode = input['approval-mode'] || \"yolo\";\nconst startDir = input['starting-directory'] || \"~/projects/\";\n\n// 3. Construct Command\n// START: Base command with flags FIRST (model, approval-mode)\nconst geminiPath = \"/home/b3nw/.npm-global/bin/gemini\";\nlet cmd = `${geminiPath} --model \"${model}\" --approval-mode \"${approvalMode}\"`;\n\n// Handle Allowed MCP Servers\n// Logic: If input is provided and valid, use it. Otherwise, force \"\" to disable all.\nlet mcpInput = input['allowed-mcp-server-names'];\nlet mcpString = \"\";\n\n// Normalize input to a string (handle array or CSV string)\nif (mcpInput !== undefined && mcpInput !== null) {\n mcpString = Array.isArray(mcpInput) ? mcpInput.join(\" \") : mcpInput.toString().split(\",\").join(\" \");\n}\n\n// Append flag: Use the list if we have one, otherwise explicitly pass \"\"\nif (mcpString && mcpString.trim().length > 0) {\n cmd += ` --allowed-mcp-server-names ${mcpString}`;\n} else {\n // This ensures we explicitly disable MCP tools when no specific servers are named\n cmd += ` --allowed-mcp-server-names \"\"`;\n}\n\n// Handle Include Directories\nlet incDirInput = input['include-directories'];\nif (incDirInput) {\n const incDirList = Array.isArray(incDirInput) ? incDirInput.join(\",\") : incDirInput;\n if (incDirList.trim().length > 0) {\n cmd += ` --include-directories ${incDirList}`;\n }\n}\n\n// Handle Resume\nif (input.resume) {\n cmd += ` --resume \"${input.resume}\"`;\n}\n\n// Force json output\ncmd += ` --output-format json`;\n\n// END: Append the prompt LAST as the positional argument\ncmd += ` \"${safePrompt}\"`;\n\nreturn [\n {\n json: {\n command: cmd,\n starting_directory: startDir\n }\n }\n];" }, "id": "61fecf15-8117-4a65-98ee-d3b019d2b89e", "name": "Construct Command", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 464, 32 ] }, { "parameters": { "authentication": "privateKey", "command": "={{ $json.command }}", "cwd": "={{ $json.starting_directory }}" }, "id": "4bfebf4e-2390-405e-ae3d-8ce8ff6ddde4", "name": "Run Gemini via SSH", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [ 688, 32 ], "credentials": { "sshPrivateKey": { "id": "S2dcVMjrpg0I0kdV", "name": "vscode-dev.local.ben.io" } } }, { "parameters": { "inputSource": "jsonExample", "jsonExample": "{\n \"prompt\": \"Describe the current directory\",\n \"starting-directory\": \"~/projects/\",\n \"allowed-mcp-server-names\": \"\",\n \"resume\": \"\",\n \"approval-mode\": \"yolo\",\n \"model\": \"gemini-3-flash-preview\",\n \"include-directories\": []\n}" }, "type": "n8n-nodes-base.executeWorkflowTrigger", "typeVersion": 1.1, "position": [ 224, 32 ], "id": "6c73c52e-377a-44d3-9265-ade1ec3c0cf7", "name": "When Executed by Another Workflow" }, { "parameters": { "jsCode": "// Get raw inputs\nconst inputItem = items[0].json;\nconst rawOutput = inputItem.stdout;\nconst rawError = inputItem.stderr || \"\";\n\ntry {\n // 1. Parse the main JSON output\n const parsed = JSON.parse(rawOutput);\n\n // 2. Add code and signal to the 'stats' object\n // We ensure parsed.stats exists, then merge the new fields in\n parsed.stats = {\n ...parsed.stats,\n exit_code: inputItem.code,\n signal: inputItem.signal\n };\n\n // 3. Clean up Response (Flatten newlines to spaces)\n if (parsed.response && typeof parsed.response === 'string') {\n parsed.response = parsed.response.replace(/\\n+/g, ' ').trim();\n }\n\n // 4. Clean up Logs (Split into an array for better readability)\n // This removes the \\n characters and creates a clean list of strings\n const cleanLogs = rawError.split('\\n').filter(line => line.trim() !== '');\n\n return [{\n json: {\n ...parsed,\n cli_logs: cleanLogs\n }\n }];\n\n} catch (error) {\n // Fallback for parsing errors\n return [{\n json: {\n error: \"Failed to parse CLI output\",\n raw_output: rawOutput,\n // Still try to save the code/signal even on error\n stats: {\n exit_code: inputItem.code,\n signal: inputItem.signal\n },\n cli_logs: rawError.split('\\n')\n }\n }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 896, 32 ], "id": "b6372a48-5e24-4dc6-9b9b-692b6eed4ef4", "name": "Code in JavaScript" } ], "connections": { "Construct Command": { "main": [ [ { "node": "Run Gemini via SSH", "type": "main", "index": 0 } ] ] }, "When Executed by Another Workflow": { "main": [ [ { "node": "Construct Command", "type": "main", "index": 0 } ] ] }, "Run Gemini via SSH": { "main": [ [ { "node": "Code in JavaScript", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1" }, "triggerCount": 0, "versionId": "c77abb27-e648-4382-a9d5-49aa2f38e03d", "owner": { "type": "personal", "projectId": "FeLO36wNUAcn61Wj", "projectName": "Ben W ", "personalEmail": "admin@ben.io" }, "parentFolderId": "of8yoeyjjIAhYdnQ", "isArchived": false }