internal/skills/defaults/skills/pipeline-builder/SKILL.md
Design, create, and safely edit Emerald pipelines. Use this skill when an LLM needs to produce valid nodes/edges JSON for pipeline tools or reason about existing workflows.
npx skillsauth add FlameInTheDark/emerald pipeline-builderInstall 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.
Use this skill whenever the task is to create, inspect, update, or delete Emerald pipelines.
This app stores pipelines as React Flow-style JSON. Good pipelines here are small, explicit, and easy to debug. Prefer a working first version over an overcomplicated graph.
create_pipeline and update_pipelinedraft unless activation is clearly requestedlist_pipelinesincludeDefinition: truepipelineId or exact pipelineNamelogic:return when the pipeline is meant to return structured data to a caller or sub-pipeline tool.draft unless the user explicitly wants it active now.A pipeline definition uses:
nodes: array of node objectsedges: array of edge objectsviewportNode shape:
{
"id": "trigger-manual-1",
"position": { "x": 120, "y": 220 },
"data": {
"label": "Manual Trigger",
"type": "trigger:manual",
"config": {},
"enabled": true
}
}
Edge shape:
{
"id": "edge-trigger-http",
"source": "trigger-manual-1",
"target": "action-http-1"
}
Tool edge from an agent to a tool node:
{
"id": "edge-agent-tool-list",
"source": "llm-agent-1",
"sourceHandle": "tool",
"target": "tool-pipeline-list-1"
}
This is the only valid way to connect a tool:* node.
logic:return is limited to one per pipelinetool:* nodes can only be used by llm:agentllm:agent node's bottom tool handletool:* node with a normal edgetool handle to target anything except a tool:* nodelogic:return ends the pipeline and should not be treated like a normal fan-out nodelogic:condition, branch handles are:
truefalselogic:switch, each condition needs a stable config.conditions[].id
sourceHandlesourceHandle: "default"Triggers:
trigger:manualtrigger:crontrigger:webhooktrigger:channel_messageActions:
action:httpaction:shell_commandaction:luaaction:proxmox_list_nodesaction:proxmox_list_workloadsaction:vm_startaction:vm_stopaction:vm_cloneaction:kubernetes_api_resourcesaction:kubernetes_list_resourcesaction:kubernetes_get_resourceaction:kubernetes_apply_manifestaction:kubernetes_patch_resourceaction:kubernetes_delete_resourceaction:kubernetes_scale_resourceaction:kubernetes_rollout_restartaction:kubernetes_rollout_statusaction:kubernetes_pod_logsaction:kubernetes_pod_execaction:kubernetes_eventsaction:channel_send_messageaction:channel_send_and_waitaction:pipeline_getaction:pipeline_runLogic:
logic:conditionlogic:switchlogic:mergelogic:aggregatelogic:sortlogic:limitlogic:remove_duplicateslogic:summarizelogic:returnLLM:
llm:promptllm:agentTool nodes:
tool:httptool:shell_commandtool:proxmox_list_nodestool:proxmox_list_workloadstool:vm_starttool:vm_stoptool:vm_clonetool:pipeline_listtool:pipeline_gettool:pipeline_createtool:pipeline_updatetool:pipeline_deletetool:pipeline_runtool:kubernetes_api_resourcestool:kubernetes_list_resourcestool:kubernetes_get_resourcetool:kubernetes_apply_manifesttool:kubernetes_patch_resourcetool:kubernetes_delete_resourcetool:kubernetes_scale_resourcetool:kubernetes_rollout_restarttool:kubernetes_rollout_statustool:kubernetes_pod_logstool:kubernetes_pod_exectool:kubernetes_eventstool:channel_send_and_waitCommon Kubernetes node config shape:
clusterId is requirednamespace is optional and falls back to the cluster default namespaceapiVersion plus either kind or resourcenamelabelSelector and/or fieldSelectoraction:kubernetes_apply_manifest and tool:kubernetes_apply_manifest use manifest, optional fieldManager, and optional forceStandard Kubernetes node outputs include:
clusterIdclusterNamecontextdefaultNamespacenamespaceresourceRef for operations that resolve a concrete resource or resource kindOperation payloads then add fields such as:
resources for API discoveryitems and count for list/apply/events operationsitem for get/patch/delete/scale/restart operationsstatus for rollout statuslogs for pod logsresult for pod execRead-heavy Kubernetes nodes:
action:kubernetes_api_resourcesaction:kubernetes_list_resourcesaction:kubernetes_get_resourceaction:kubernetes_rollout_statusaction:kubernetes_pod_logsaction:kubernetes_eventstool:kubernetes_* variantsMutating Kubernetes nodes:
action:kubernetes_apply_manifestaction:kubernetes_patch_resourceaction:kubernetes_delete_resourceaction:kubernetes_scale_resourceaction:kubernetes_rollout_restartaction:kubernetes_pod_exectool:kubernetes_* variantsManual trigger:
{ "label": "Manual Trigger", "type": "trigger:manual", "config": {}, "enabled": true }
HTTP request:
{
"label": "HTTP Request",
"type": "action:http",
"config": {
"url": "http://127.0.0.1:8080/api/v1/health",
"method": "GET",
"headers": {},
"body": ""
},
"enabled": true
}
Kubernetes list resources:
{
"label": "K8s List Resources",
"type": "action:kubernetes_list_resources",
"config": {
"clusterId": "k8s-cluster-id",
"namespace": "production",
"apiVersion": "apps/v1",
"kind": "Deployment",
"resource": "",
"labelSelector": "app=api",
"fieldSelector": "",
"allNamespaces": false,
"limit": 50
},
"enabled": true
}
Kubernetes apply manifest:
{
"label": "K8s Apply Manifest",
"type": "action:kubernetes_apply_manifest",
"config": {
"clusterId": "k8s-cluster-id",
"namespace": "production",
"manifest": "apiVersion: apps/v1\\nkind: Deployment\\nmetadata:\\n name: api\\nspec:\\n replicas: 3",
"fieldManager": "emerald",
"force": false
},
"enabled": true
}
Run pipeline tool:
{
"label": "Run Pipeline Tool",
"type": "tool:pipeline_run",
"config": {
"pipelineId": "pipeline-target-id",
"toolName": "run_support_pipeline",
"toolDescription": "Run the support pipeline and return its output.",
"allowModelPipelineId": false,
"arguments": [
{
"name": "ticket",
"description": "Ticket id to pass into the called pipeline as arguments.ticket",
"required": true
}
]
},
"enabled": true
}
Important: custom tool:pipeline_run arguments are delivered inside the called pipeline as arguments.<name>, so downstream templates can use values like {{arguments.ticket}}.
Condition:
{
"label": "Condition",
"type": "logic:condition",
"config": {
"expression": "input.response.status == \"ok\""
},
"enabled": true
}
Switch:
{
"label": "Switch",
"type": "logic:switch",
"config": {
"conditions": [
{
"id": "healthy",
"label": "Healthy",
"expression": "input.status == \"ok\""
},
{
"id": "busy",
"label": "Busy",
"expression": "input.load > 0.8"
}
]
},
"enabled": true
}
Return:
{
"label": "Return",
"type": "logic:return",
"config": {
"value": "{{input}}"
},
"enabled": true
}
LLM prompt:
{
"label": "LLM Prompt",
"type": "llm:prompt",
"config": {
"providerId": "",
"prompt": "Context:\\n{{input.nodes}}\\n\\nSummarize the cluster.",
"model": "",
"temperature": 0.7,
"max_tokens": 1024
},
"enabled": true
}
LLM agent:
{
"label": "LLM Agent",
"type": "llm:agent",
"config": {
"providerId": "",
"prompt": "User request:\\n{{input.message.content}}",
"model": "",
"temperature": 0.7,
"max_tokens": 1024,
"enableSkills": true
},
"enabled": true
}
Run pipeline action:
{
"label": "Run Pipeline",
"type": "action:pipeline_run",
"config": {
"pipelineId": "TARGET_PIPELINE_ID",
"params": "{\"request\":\"{{input.message.content}}\"}"
},
"enabled": true
}
Run pipeline tool:
{
"label": "Run Support Pipeline",
"type": "tool:pipeline_run",
"config": {
"pipelineId": "TARGET_PIPELINE_ID",
"toolName": "run_support_pipeline",
"toolDescription": "Run the support pipeline and return its structured result.",
"allowModelPipelineId": false
},
"enabled": true
}
Template syntax is for text/config fields:
{{input}}{{input.nodes}}{{input.nodes[0].status}}{{secret.api_token}}{{$('action-http-1').response.status_code}}Cross-node selectors only resolve after the referenced node has already executed in the current run. Missing paths or unavailable node IDs fail template rendering.
Expression syntax is for logic:condition and logic:switch expressions:
input.status == "ok"input.response.status_code >= 200 && input.response.status_code < 300Do not put template braces inside Expr expressions unless the specific field is documented as a text template.
When updating an existing pipeline:
list_pipelines and includeDefinition: truesourceHandleaction:lua when sort, limit, dedupe, or summary nodes already match the needlogic:return for structured outputstrigger-manual-1, action-http-1, logic-return-1selected, dragging, or measuredThis is a strong default when the user wants a simple pipeline that fetches something and returns it:
{
"name": "Check Health",
"description": "Fetch the health endpoint and return the response.",
"status": "draft",
"nodes": [
{
"id": "trigger-manual-1",
"position": { "x": 120, "y": 220 },
"data": {
"label": "Manual Trigger",
"type": "trigger:manual",
"config": {},
"enabled": true
}
},
{
"id": "action-http-1",
"position": { "x": 380, "y": 220 },
"data": {
"label": "HTTP Request",
"type": "action:http",
"config": {
"url": "http://127.0.0.1:8080/api/v1/health",
"method": "GET",
"headers": {},
"body": ""
},
"enabled": true
}
},
{
"id": "logic-return-1",
"position": { "x": 650, "y": 220 },
"data": {
"label": "Return",
"type": "logic:return",
"config": {
"value": "{{input}}"
},
"enabled": true
}
}
],
"edges": [
{
"id": "edge-trigger-http",
"source": "trigger-manual-1",
"target": "action-http-1"
},
{
"id": "edge-http-return",
"source": "action-http-1",
"target": "logic-return-1"
}
],
"viewport": {
"x": 0,
"y": 0,
"zoom": 1
}
}
When building an agent pipeline:
tool handleTypical flow:
trigger:channel_messagellm:agentaction:channel_send_messageConnected tools:
tool:pipeline_listtool:pipeline_runtool:pipeline_createtool:pipeline_updatetool:kubernetes_list_resourcestool:kubernetes_get_resourcetool:kubernetes_pod_logstool:kubernetes_eventsOnly add mutating Kubernetes tools such as tool:kubernetes_apply_manifest or tool:kubernetes_delete_resource when the request clearly requires cluster changes.
Use:
logic:condition for one yes/no splitlogic:switch for multiple named brancheslogic:merge when you want to merge upstream objectslogic:aggregate when you want collected upstream arrays and metadatalogic:sort when you need deterministic ordering of an array already in the payloadlogic:limit when you only need the first N items from a listlogic:remove_duplicates when repeated entries should collapse before later stepslogic:summarize when downstream steps need aggregate metrics instead of raw rowsaction:lua when the transformation is too custom for the built-in logic nodeslogic:return when the pipeline is used as a callable unitdraftReturn node for inspectable outputsWhen the user asks for a pipeline, return or submit a complete, valid structure rather than vague pseudocode.
devops
Guide for Emerald template interpolation, runtime context lookup, and pipeline parameter passing. Use when writing prompts, config fields, JSON params, or explaining how `input`, `arguments`, and other template values resolve.
devops
Reference for valid Emerald node and edge topology, branching handles, and live-edit safety rules. Use when reasoning about graph structure, adding or removing nodes, reconnecting edges, or validating pipeline edits.
development
Compact reference for important Emerald node families, common config shapes, and when to use each category. Use when choosing nodes, understanding existing nodes, or mapping a user request to pipeline components.
documentation
Guide for `action:lua` nodes, including how runtime data is exposed, how `input` works, Lua table conversion, and how return values become downstream output. Use when creating or explaining Lua nodes.