.claude/skills/adopt-workflow/SKILL.md
Adopt a server-only n8n workflow into the local codebase. Use this skill whenever: the user wants to "adopt", "implement", "migrate", or "bring in" a workflow from the n8n server; the user references a workflow from the "To Implement" section of workflow-catalog.md; or the user asks to create a local .js implementation of a server workflow. Takes a workflow name as an argument (kebab-case, no extension). Also trigger when the user says things like "let's do the next workflow" or "adopt appearances-management".
npx skillsauth add eliasisrael/n8n adopt-workflowInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill takes a workflow that exists only on the n8n server (as a JSON snapshot in server/) and creates a local .js implementation in workflows/ that, when built, produces output identical to the server original.
Before starting, read LESSONS.md — it contains critical information about n8n parameter formats, node quirks, and import compatibility that will save you from common mistakes.
The skill takes one argument: the workflow name in kebab-case (no .js or .json extension). Example: adopt-workflow appearances-management
If no argument is provided, ask the user which workflow to adopt. You can reference workflow-catalog.md for the list of workflows marked "To Implement".
Refresh the server snapshot to make sure you're working against the current live version:
op run --env-file=.env.tpl -- node pull-workflows.js --force
Then confirm the target file exists in server/. The server filename may not exactly match the workflow name — check workflow-catalog.md for known filename mismatches.
Read the JSON file from server/<workflow-name>.json. Understand:
name field) — your .js file must produce this exact namename, type, typeVersion, position, parameters, credentials, disabled, onError, and any other top-level node properties (retryOnFail, maxTries, waitBetweenTries, alwaysOutputData, executeOnce, notesInFlow, notes)ai_languageModel, etc.), multi-output nodes, and multi-input nodesexecutionOrder, errorWorkflow, callerPolicy, timezone, etc.Pay special attention to:
__rl: true with mode and value)conditions.conditions[], propertiesUi.propertyValues[], rules.values[])id and name)webhookId values on webhook nodes (must match for URL stability)n8n-nodes-base.stickyNote) — include them as nodesCreate workflows/<workflow-name>.js using the project's helpers:
import { createWorkflow, createNode, connect } from '../lib/workflow.js';
Guidelines:
import/export defaultexport default createWorkflow(...) callposition: [x, y] in opts to match the server layout exactlyid in opts matching the server node IDs, so the workflow can be pushed back without creating duplicate nodes$('Node Name'))typeVersion matching the serveronError, retryOnFail, maxTries, waitBetweenTries, alwaysOutputData, executeOnce, notesInFlow, notes, assign them directly on the node object after createNode() returns (e.g., node.onError = 'continueRegularOutput')createNode using type n8n-nodes-base.stickyNote and include width and height in parametersop run --env-file=.env.tpl -- node build.js --workflow <workflow-name>
This writes output/<workflow-name>.json.
Compare the built output against the server original. Write and run a comparison script or use inline diffing. Focus on functional equivalence:
Must match exactly:
name, type, typeVersionposition (x, y coordinates)parameters (deep equality, including nested structures)credentials (if any)disabled, onError, retryOnFail, maxTries, waitBetweenTries, alwaysOutputData, executeOnce, notesInFlow, notes, webhookIdnamesettingstagsOK to differ:
id values (generated UUIDs)id, versionId, createdAt, updatedAt, activemeta, staticData, pinData (envelope metadata)If mismatches are found:
.js sourceCommon issues to watch for (see LESSONS.md for details):
__rl: true wrapper on resource locator fields (databaseId, pageId, workflowId)typeValidation: 'strict' in Filter/IF node conditionssingleValue: true missing on unary operators (exists, notEmpty, true)options: {} on nodes that require itrichText: false on rich_text properties in propertiesUiRepeat the build-compare-fix loop until there are zero functional differences. If you cannot resolve a mismatch after 5 iterations, stop and report the specific issue to the user.
Once the built output matches the server original, show the user:
.js file pathThen ask for permission to:
push-workflows.jsDo NOT commit or push without explicit user approval.
After committing, update workflow-catalog.md to move the workflow from "To Implement" to "Implemented Locally".
Here's a useful inline comparison approach:
node -e "
const fs = require('fs');
const server = JSON.parse(fs.readFileSync('server/<name>.json', 'utf8'));
const built = JSON.parse(fs.readFileSync('output/<name>.json', 'utf8'));
// Compare nodes
const sNodes = [...server.nodes].sort((a,b) => a.name.localeCompare(b.name));
const bNodes = [...built.nodes].sort((a,b) => a.name.localeCompare(b.name));
console.log('Node count:', sNodes.length, 'vs', bNodes.length);
for (let i = 0; i < Math.max(sNodes.length, bNodes.length); i++) {
const s = sNodes[i], b = bNodes[i];
if (!s) { console.log('EXTRA built node:', b?.name); continue; }
if (!b) { console.log('MISSING built node:', s?.name); continue; }
if (s.name !== b.name) console.log('Name mismatch:', s.name, 'vs', b.name);
if (s.type !== b.type) console.log(s.name, 'type:', s.type, 'vs', b.type);
if (s.typeVersion !== b.typeVersion) console.log(s.name, 'typeVersion:', s.typeVersion, 'vs', b.typeVersion);
if (JSON.stringify(s.position) !== JSON.stringify(b.position)) console.log(s.name, 'position:', s.position, 'vs', b.position);
if (JSON.stringify(s.parameters) !== JSON.stringify(b.parameters)) console.log(s.name, 'PARAMETERS DIFFER');
if (JSON.stringify(s.credentials) !== JSON.stringify(b.credentials)) console.log(s.name, 'CREDENTIALS DIFFER');
}
// Compare connections
if (JSON.stringify(server.connections) !== JSON.stringify(built.connections)) {
console.log('CONNECTIONS DIFFER');
}
console.log('Done.');
"
For deeper parameter diffing, use JSON.stringify(s.parameters, null, 2) and compare line by line.
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.