comfyui-node-scaffold

v2026.09.24

Scaffold a new ComfyUI custom-node repo (TypeScript + bun build, CI, release-please, vitest+pytest) consuming @laurigates/comfy-modal-kit. Use when bootstrapping or init-ing a comfyui node pack.

GitHub
Install command
npx skhub add laurigates/comfyui-node-scaffold
Markdown
SKILL.md

comfyui-node-scaffold

Bootstrap a new ComfyUI custom-node pack that matches the established mobile-first TypeScript + bun build architecture of comfyui-gallery-loader, comfyui-sampler-info, and comfyui-touch-numeric, leaving only the actual node logic to implement.

Supporting material is split across references/ by the path that needs it; see REFERENCE.md for the index.

When to Use This Skill

Use this skill when...Use the alternative when...
Starting a new ComfyUI usability pack repo — CI-green TS toolchain, Comfy Registry publish, and the widget-intercept/gesture skeleton before writing pack logicYou want the full pipeline (repo created + seeded + gitops-adopted) → comfy-node
Spinning up a project:comfyui-nodes backlog idea (touch-numeric, prompt-editor, model-gallery…)Adding a node to an existing pack — this creates a new repo

The architecture it scaffolds

TypeScript source in src/ (entry src/index.ts), built to web/dist/ via bun build — typed authoring against @comfyorg/comfyui-frontend-types, browser-ESM output, the /scripts/app.js runtime import left unbundled. This supersedes the old vanilla-JS (web/js/*.js + copied modal primitives) template. The generated pack starts with an ADR (docs/blueprint/adrs/0001-…) recording the decision, mirroring sampler-info's ADR-0010.

  • Type gate: bun run typecheck → tsc --noEmit (never emits).
  • Emit: bun build ./src/index.ts --target browser --format esm --outdir web/dist --external '/scripts/*'. If the pack ships a static data corpus, append && cp -R web/data web/dist/data.
  • Serve: __init__.py sets WEB_DIRECTORY = "./web/dist"; ComfyUI serves that tree at /extensions/<name>/.
  • Distribute: web/dist/ is committed (tracked) so git clone/update carries the served bundle; [tool.comfy] includes = ["web/dist"] also force-ships it to the registry, and publish.yml runs bun run build first.
  • The /scripts/app.js type shim: a paths mapping in tsconfig.json points the rooted import at src/comfyui-shims.d.ts (TypeScript will not match an ambient declare module against a /… specifier). The emitted import string stays /scripts/app.js and --external '/scripts/*' keeps it unbundled.

The vein

A frontend extension that intercepts widget.onPointerDown (modern Vue frontend, comfyui-frontend-package >= 1.40) and opens a touch-friendly HTML modal in place of a clunky native LiteGraph control. Widgets are matched by name (generic across node packs); the enhancement is additive (graceful fallback, never breaks serialized workflows); the modal is touch-first (16px inputs, big tap targets, momentum scroll). The modal primitives come from @laurigates/comfy-modal-kit (openModalShell / fuzzyRank / highlightMatches) — imported, not copied — and bun build inlines them.

Name-matching needs a gate when the modal's content is external. The emitted TARGET_WIDGETS set matches on name alone, which is correct for a modal that renders the widget's own options.values. If your pack instead fills the modal from a folder_paths listing, an endpoint, or a corpus, a node that hardcodes a combo under the same name (RIFE VFI's ckpt_name) has its only valid choices replaced with values it rejects — see comfyui-node-authoring § "A widget name is not proof of its option source" for the overlap gate.

Four variants

VariantUse whenShapeModal kit
frontend (default)No Python needed — pure widget UX (seed/numeric keypad, prompt editor, tooltips, enum recipes).Empty NODE_CLASS_MAPPINGS; widget-intercept modal in src/index.ts. Like sampler-info / touch-numeric.imports the kit
backendNeeds to read disk / serve thumbnails / add a node (model thumbnails, file listings).Adds <module>.py (node + aiohttp endpoints, ComfyUI-bundled libs only) + a tests/conftest.py that stubs aiohttp/server so pytest is green. Like gallery-loader.imports the kit
gestureThe UX is a canvas interaction, not a widget — drag/long-press on nodes or groups (resize, move, region-box). Must be recognizable on the first pointerdown — see the contract in variants.md.Empty NODE_CLASS_MAPPINGS; a canvas pointer layer in src/index.ts with exported pure geometry helpers. Like touch-resize's corner grab-handles.no kit
shimThe pack's whole job is injecting scoped CSS / registering commands to paper over upstream frontend bugs — no modal, no widget hook.Empty NODE_CLASS_MAPPINGS; a SHIMS registry in src/index.ts with applyCssShim/removeCssShim, one managed <style> per shim driven by a boolean setting, + a jsdom lifecycle smoke test. Like comfyui-touch-shim.no kit

Decision rule: frontend/backend with --widgets for a per-widget modal; frontend/backend without --widgets for a standalone modal opened from the shared Touch Tools hub (the family's one action-bar button — a scaffolded pack contributes a chooser row, never a button of its own); gesture when the interaction is on the canvas/node frame itself (no widget to hook); shim when the pack only injects scoped CSS / registers commands to paper over an upstream frontend bug (no modal, no widget). Add backend only when the feature genuinely needs the server to read files or serve data. A non-bundled Python dependency is never allowed — if you reach for one, it belongs in a separate companion pack.

For what --widgets changes about the emitted modal, and what the gesture and shim skeletons contain, see references/variants.md.

How to run

scaffold.py is stdlib-only. Run from the workspace root (repos/laurigates/) so the new repo lands as a sibling of the reference packs.

Frontend-only pack:

python3 ${CLAUDE_SKILL_DIR}/scaffold.py --name comfyui-touch-numeric --display "Touch Numeric" --desc "Touch-friendly keypad + slider modal for seed and INT/FLOAT widgets." --variant frontend --widgets seed,noise_seed,cfg,steps,denoise

The backend, standalone-modal, gesture, and shim recipes, the full flag list, and the banner-tagline rule live in references/invocation.md.

After scaffolding

The generator prints the exact next steps. In order:

cd comfyui-<name>
git init -b main
uv sync --group dev
bun install
pre-commit install
just check

Seed main directly (the repo is unprotected until gitops adopts it) — pushing a feature branch first would leave main missing on origin and force a rename

  • default-branch fixup later.

Then implement, and wire up infra:

  1. Implement the modal in src/index.ts — tune TARGET_WIDGETS and replace the openPicker stub with the real modal body (import { fuzzyRank } from "@laurigates/comfy-modal-kit" for search, openModalShell for the dialog). For the standalone-modal shape, keep the Touch Tools hub wiring intact and swap the placeholder pi pi-th-large icon for a fitting PrimeIcon — see references/variants.md § "The Touch Tools hub contract". For the backend variant, fill in <module>.py's node + endpoints; widen ALLOWED_EXTENSIONS explicitly for any new file type read off disk. For the gesture variant, tune the pointer layer (selectedNodes/nodeScreenRect/hitHandle/resizedGeometry) and re-measure CONFIG.handleHitRadius against this pack's handle placement, keeping the gesture recognizable on the first pointerdown per the contract in references/variants.md. For the shim variant, replace the placeholder SHIMS entry — link the upstream issue, point the selector at a stable data-testid, keep each shim fail-soft.
  2. Add the repo to gitops/repositories.tf with comfy_registry = true (and release_please = true). On apply, gitops pushes both the release-please App credentials and the REGISTRY_ACCESS_TOKEN secret. No per-repo secret creation is needed.

Or skip steps 1–2 entirely: run the /comfy-node orchestrator, which chains scaffold → gh repo create → seed main → the gitops PR.

Before opening the registry PR, grade the pack's finishing pass — the artwork gate, the --verify audit, and the registry-publishing facts are in references/registry-readiness.md. What the generator emits, the invariants the emitted code assumes, and what it deliberately does not generate are in references/implementing-the-pack.md.

Agentic Optimizations

ContextCommand
Scaffold a frontend packpython3 ${CLAUDE_SKILL_DIR}/scaffold.py --name comfyui-X --display "X" --desc "…" --variant frontend --widgets a,b
Scaffold a gesture packpython3 ${CLAUDE_SKILL_DIR}/scaffold.py --name comfyui-X --display "X" --desc "…" --variant gesture
Scaffold a CSS/shim packpython3 ${CLAUDE_SKILL_DIR}/scaffold.py --name comfyui-X --display "X" --desc "…" --variant shim
Verify a generated packcd comfyui-X && bun install && just check
Grade one pack's finishing passpython3 ${CLAUDE_SKILL_DIR}/scaffold.py --verify path/to/comfyui-X
Sweep the whole fleet for driftpython3 ${CLAUDE_SKILL_DIR}/scripts/check-fleet-drift.py
Sweep one pack onlypython3 ${CLAUDE_SKILL_DIR}/scripts/check-fleet-drift.py --pack comfyui-X
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

comfyui-plugin/skills/comfyui-node-scaffold

Default branch

main

Latest commit

1668324

Tree SHA

b2d4cc3