Agent skill

Node Generator

by dafthunk-com in dafthunk-com/dafthunk

Generate new workflow nodes with implementation, tests, and registry registration

MITAuto-check passedProductivity & Automation

Install Node Generator

skills CLI
$ npx skills add dafthunk-com/dafthunk --skill node-generator -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install dafthunk-com/dafthunk node-generator --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/dafthunk-com/dafthunk.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/node-generator .claude/skills/node-generator && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
node-generator
GitHub stars
134
Token cost
~2.5k tokens
SKILL.md length
521 words
Files
1
Skills in repo
3
Repo updated
First seen
Licence
MIT

At a glance

Generate new workflow nodes with implementation, tests, and registry registration

  • Works in 6 steps: Research and Define Requirements → Create Node Implementation → Create Test File → …
  • Tasks that involve Workflow automation
  • SKILL.md covers Step 1: Research and Define…, Step 2: Create Node…, Step 3: Create Test File and Step 4: Register the Node, plus 3 more sections
  • Calls pnpm

What it does

Node Generator is an agent skill from dafthunk-com/dafthunk. Generate new workflow nodes with implementation, tests, and registry registration

Its SKILL.md is about 2.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Productivity & Automation, covering Workflow automation. It works with React, Cloudflare, TypeScript and n8n. The repository describes itself as: A workflow execution platform built on top of the fantastic Cloudflare infrastructure. The licence is MIT.

When your agent uses it

  • Tasks that involve Workflow automation

Example prompts

  • “/node-generator”

Workflow steps

6 steps, taken from the step headings in SKILL.md.

  1. Research and Define Requirements
  2. Create Node Implementation
  3. Create Test File
  4. Register the Node
  5. Run Tests
  6. Summary

What it can do on your machine

Read from SKILL.md and the folder at commit 66be308. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • pnpm

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use pnpm, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Node Generator loads about 2.5k tokens when it runs. Until then it costs about 24 tokens; SKILL.md has 521 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~24
When it runs · the whole SKILL.md, loaded when a task matches
~2.5k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from dafthunk-com/dafthunk at commit 66be308, republished under its MIT licence (© dafthunk-com). 521 words, ~2,471 tokens.

Download SKILL.mdSave it as .claude/skills/node-generator/SKILL.md (or your agent's skills folder).
name
node-generator
description
Generate new workflow nodes with implementation, tests, and registry registration

Node Generator Skill

Generate workflow nodes for Dafthunk: research requirements, create implementation and tests, register in the node registry.

Step 1: Research and Define Requirements

When a user requests a new node, research first, then present a complete specification for confirmation.

Research the functionality:

  • If based on a library/API: Use WebSearch or WebFetch to find official documentation
  • Look for function signatures, parameters, return types, and examples
  • Check if the package exists in apps/api/package.json or search npm for the latest version

Check existing patterns:

  • Search packages/runtime/src/nodes/<category>/ for similar nodes
  • Examine 2-3 similar implementations to understand input/output patterns and validation approaches

Draft complete requirements:

  • Node purpose, category, name, and kebab-case ID
  • Inputs: names, types, descriptions, required/optional, defaults, repeated (from function signature/docs)
  • Outputs: primary outputs and metadata outputs (hidden: true for counts, flags, etc.)
  • Icon: appropriate lucide icon name
  • Tags: category + relevant keywords
  • Dependencies: package name and version if needed

Present for confirmation:

markdown
Based on [library/API/functionality], here's the proposed node:

**Name**: [Node Name]
**ID**: `node-id`
**Category**: category
**Icon**: icon-name

**Inputs**:
- `inputName` (type, required/optional): Description

**Outputs**:
- `outputName` (type): Description
- `metadata` (type, hidden): Description

**Dependencies**:
- package-name@^version

**Tags**: Category, Tag1, Tag2

Does this match your requirements?

Only ask for information you cannot reasonably infer or research. The goal is to present a complete, research-backed specification that the user only needs to approve or tweak.

Step 2: Create Node Implementation

File: packages/runtime/src/nodes/<category>/<node-id>.ts

typescript
import { NodeExecution, NodeType } from "@dafthunk/types";
import { ExecutableNode, NodeContext } from "../../runtime/node-types";

export class [NodeClassName]Node extends ExecutableNode {
  public static readonly nodeType: NodeType = {
    id: "[node-id]",
    name: "[Node Display Name]",
    type: "[node-id]",
    description: "[One-line description]",
    tags: ["Category", "Tag1", "Tag2"],
    icon: "[icon-name]",
    documentation: "[Detailed documentation]",
    inlinable: false,
    asTool: false,
    inputs: [
      {
        name: "[inputName]",
        type: "[type]",
        description: "[Description]",
        required: true,
        repeated: false,
      },
    ],
    outputs: [
      {
        name: "[outputName]",
        type: "[type]",
        description: "[Description]",
      },
    ],
  };

  public async execute(context: NodeContext): Promise<NodeExecution> {
    try {
      const { input1, optionalInput = "default" } = context.inputs;

      // Validate required inputs
      if (input1 === null || input1 === undefined) {
        return this.createErrorResult("Missing required input: input1");
      }

      if (typeof input1 !== "expectedType") {
        return this.createErrorResult(
          `Invalid input type for input1: expected expectedType, got ${typeof input1}`
        );
      }

      // Handle repeated inputs (arrays)
      if (Array.isArray(input1)) {
        for (let i = 0; i < input1.length; i++) {
          if (typeof input1[i] !== "string") {
            return this.createErrorResult(
              `Invalid input at position ${i}: expected string, got ${typeof input1[i]}`
            );
          }
        }
      }

      // Main logic
      const result = processInput(input1);

      return this.createSuccessResult({ output1: result });
    } catch (err) {
      const error = err as Error;
      return this.createErrorResult(`Error in [NodeName]: ${error.message}`);
    }
  }
}

Defensive programming checklist:

  • Validate null/undefined, then types, then ranges/constraints
  • Handle single values and arrays for repeated inputs
  • Use descriptive error messages with input names and types
  • Use nested try-catch for risky operations (parsing, external APIs)
  • Handle edge cases: empty arrays/strings, zero/negative numbers
  • Never let execute throw — every failure path returns createErrorResult. A thrown error still gets caught by the runtime, but the message loses the node's context, so the user sees a bare "fetch failed" with no clue which step broke.

Conventions enforced across all nodes:

RuleWhy
id === type, kebab-case, matching the filename minus -nodetype is the discriminator persisted in stored workflow graphs; renaming it breaks saved workflows
Descriptions (node, inputs, outputs) carry no trailing period when they are a single sentenceThe UI renders them inline; multi-sentence text keeps its punctuation
documentation only when it says something the description does notOtherwise it is noise on the node's docs page — omit it
public async execute(...), public static readonly nodeTypeMatches every other node; no implicit-visibility members
No any, anywherecontext.inputs is untyped, so narrow it at the top of execute with guards, not casts
Every node registered in cloudflare-node-registry.tsAn unregistered node is dead code the editor can never surface
Show full SKILL.md (133 more words)Show less

Shared helpers — reach for these before writing your own:

  • nodes/json/json-access.ts — typed traversal of parsed JSON (getAtPath, hasPath, readKey, writeKey, deepEqual, deepClone)
  • nodes/geo/geo-input.ts — GeoJSON and unit guards (isGeoJSON, isGeoJSONOf, isUnits, extractPosition)
  • nodes/image/execute-photon-operation.ts — Photon lifecycle with guaranteed resource cleanup
  • utils/zod.ts + a static inputSchema — the preferred validation style for new nodes; zodErrorMessage renders a readable failure

Step 3: Create Test File

File: packages/runtime/src/nodes/<category>/<node-id>.test.ts

typescript
import { Node } from "@dafthunk/types";
import { describe, expect, it } from "vitest";
import { NodeContext } from "../../runtime/node-types";
import { [NodeClassName]Node } from "./<node-id>";

describe("[NodeClassName]Node", () => {
  const createContext = (inputs: Record<string, unknown>): NodeContext => ({
    nodeId: "[node-id]",
    inputs,
    getIntegration: async () => { throw new Error("No integrations in test"); },
    env: {},
  } as unknown as NodeContext);

  it("should [perform expected operation]", async () => {
    const node = new [NodeClassName]Node({ nodeId: "[node-id]" } as unknown as Node);
    const result = await node.execute(createContext({ input1: "test value" }));

    expect(result.status).toBe("completed");
    expect(result.outputs?.output1).toBe("expected value");
  });

  it("should handle empty input", async () => {
    const node = new [NodeClassName]Node({ nodeId: "[node-id]" } as unknown as Node);
    const result = await node.execute(createContext({ input1: "" }));

    expect(result.status).toBe("completed");
  });

  it("should return error for missing input", async () => {
    const node = new [NodeClassName]Node({ nodeId: "[node-id]" } as unknown as Node);
    const result = await node.execute(createContext({}));

    expect(result.status).toBe("error");
    expect(result.error).toContain("Missing required input");
  });

  it("should return error for invalid type", async () => {
    const node = new [NodeClassName]Node({ nodeId: "[node-id]" } as unknown as Node);
    const result = await node.execute(createContext({ input1: 123 }));

    expect(result.status).toBe("error");
    expect(result.error).toContain("Invalid input type");
  });

  it("should handle array of inputs", async () => {
    const node = new [NodeClassName]Node({ nodeId: "[node-id]" } as unknown as Node);
    const result = await node.execute(createContext({ input1: ["val1", "val2"] }));

    expect(result.status).toBe("completed");
  });

  it("should return error for invalid element in array", async () => {
    const node = new [NodeClassName]Node({ nodeId: "[node-id]" } as unknown as Node);
    const result = await node.execute(createContext({ input1: ["valid", 123] }));

    expect(result.status).toBe("error");
    expect(result.error).toContain("position 1");
  });
});

Test coverage: Happy path, edge cases (empty/boundary values), error cases (missing/wrong types), array handling, type coercion (if applicable), domain-specific cases.

Step 4: Register the Node

File: apps/api/src/runtime/cloudflare-node-registry.ts

Add import (alphabetically within category):

typescript
import { [NodeClassName]Node } from "./<category>/<node-id>";

Register in constructor (alphabetically within category):

typescript
this.registerImplementation([NodeClassName]Node);

Step 5: Run Tests

bash
pnpm typecheck
pnpm --filter '@dafthunk/api' test <node-id>

Step 6: Summary

List files created, confirm registry registration, show test command, note any dependencies to install.

Common Patterns

Repeated inputs (single value or array):

typescript
if (typeof values === "string") { /* handle single */ }
if (Array.isArray(values)) { /* validate each element */ }

Number coercion:

typescript
const num = Number(input);
if (isNaN(num)) { return this.createErrorResult("Invalid number"); }

Optional inputs:

typescript
const { required, optional = "default" } = context.inputs;

External libraries:

typescript
try {
  const result = library.function(input);
} catch (err) {
  return this.createErrorResult(`Operation failed: ${(err as Error).message}`);
}

© dafthunk-com, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/node-generator of dafthunk-com/dafthunk.

Open the folder on GitHubat commit 66be308

Compare with similar skills

Node Generator next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Node Generator compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Node Generator this skilldafthunk-com/dafthunk134—~2.5kAutomated safety check: PassMIT
N8n ArchitectEtienneLescot/n8n-as-code1.6k—~7.3kAutomated safety check: WarnMIT
Datadog Appdatadog-labs/agent-skills177—~744Automated safety check: PassMIT
Skyvern Browser AutomationSkyvern-AI/skyvern23k—~1.9kAutomated safety check: PassAGPL-3.0
Instagram Skillsupreme-gg-gg/instagram-cli2.2k—~1.4kAutomated safety check: PassMIT
n8n Code Node JavaScriptczlonkowski/n8n-skills6.4k—~4.9kAutomated safety check: PassMIT

Similar skills

  • N8n Architect

    EtienneLescot/n8n-as-code

    A skill your agent uses when the user explicitly wants to create, edit, validate, sync, or troubleshoot n8n workflows, asks about n8n nodes or automation, or wants to use n8n-as-code in the current…

    1.6k GitHub stars~7.3k tokensUpdated today
    Productivity & AutomationAuto-check: warnings
  • Datadog App

    datadog-labs/agent-skills

    Guides developers building Datadog Apps with TypeScript, React, the @datadog/apps scaffolder, and @datadog/vite-plugin.

    177 GitHub stars~744 tokensUpdated 5 days ago
    DevOps & CloudAuto-check passed
  • Skyvern Browser Automation

    Skyvern-AI/skyvern

    Automates websites with Skyvern's AI browser agent to fill forms, extract data, download files, log in and run multi-step workflows through SDKs, REST, MCP or a CLI.

    23k GitHub stars~1.9k tokensUpdated today
    Productivity & AutomationAuto-check passed
  • Instagram Skill

    supreme-gg-gg/instagram-cli

    How to use Instagram CLI to interact with Instagram from the command line on behalf of a user.

    2.2k GitHub stars~1.4k tokensUpdated 1 mo ago
    Productivity & AutomationAuto-check passed
  • n8n Code Node JavaScript

    czlonkowski/n8n-skills

    Guides writing JavaScript in n8n Code nodes: picking an execution mode, reading input data, returning items, using built-in helpers and avoiding common errors.

    6.4k GitHub stars~4.9k tokensUpdated 21 days ago
    Productivity & AutomationAuto-check passed
  • Native Python in n8n Code Nodes

    czlonkowski/n8n-skills

    Explains how to write native Python in n8n Code nodes, including the two input variables, blocked imports and fixes for common errors.

    6.4k GitHub stars~2.8k tokensUpdated 21 days ago
    Productivity & AutomationAuto-check passed

More from dafthunk-com/dafthunk

  • Integration Generator

    dafthunk-com/dafthunk

    Generate new OAuth integration providers for Dafthunk with backend providers, type definitions, frontend configurations, and integration nodes

    134 GitHub stars~2.3k tokensUpdated 1 mo ago
    Auto-check passed
  • Template Generator

    dafthunk-com/dafthunk

    Generate workflow templates with coherent node graphs and integration tests

    134 GitHub stars~1.4k tokensUpdated 1 mo ago
    Auto-check passed

Questions about Node Generator

What does Node Generator do?

Generate new workflow nodes with implementation, tests, and registry registration. Node Generator is an agent skill from dafthunk-com/dafthunk.

When should I use Node Generator?

Node Generator fits situations like: tasks that involve Workflow automation.

How do I install Node Generator in Claude Code?

Run `npx skills add dafthunk-com/dafthunk --skill node-generator -a claude-code`. Or copy the skill folder (.claude/skills/node-generator in dafthunk-com/dafthunk) into .claude/skills/node-generator in your project. Claude Code loads it when a task matches its description.

How do I install Node Generator in Codex?

Run `npx skills add dafthunk-com/dafthunk --skill node-generator -a codex`. Or copy the skill folder (.claude/skills/node-generator in dafthunk-com/dafthunk) into .agents/skills/node-generator in your project. Codex loads it when a task matches its description.

Can I use Node Generator in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add dafthunk-com/dafthunk --skill node-generator -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/node-generator, .gemini/skills/node-generator, .github/skills/node-generator and .opencode/skills/node-generator in your project.

What does Node Generator need to run?

Going by SKILL.md and its folder, Node Generator needs the command-line tools its instructions call (pnpm).

Does Node Generator access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Node Generator safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Node Generator use?

Node Generator is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Node Generator use?

About 2.5k tokens (SKILL.md is roughly 9.9k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Node Generator?

Skills that share tags, products or a category with Node Generator: N8n Architect (EtienneLescot/n8n-as-code, 1.6k stars), Datadog App (datadog-labs/agent-skills, 177 stars), Skyvern Browser Automation (Skyvern-AI/skyvern, 23k stars) and Instagram Skill (supreme-gg-gg/instagram-cli, 2.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Node Generator?

dafthunk-com (a GitHub organization) maintains it in dafthunk-com/dafthunk, which has 134 GitHub stars. The repository holds 3 skills in this directory. The repository was last updated on September 4, 2026.

Source: dafthunk-com/dafthunk on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.