| created | "2026-06-04T00:00:00.000Z" |
| modified | "2026-07-29T00:00:00.000Z" |
| reviewed | "2026-07-29T00:00:00.000Z" |
| model | opus |
| name | comfy-node |
| description | Orchestrate a ComfyUI node pack from idea to registry: scaffold, create + seed the repo, open the gitops adoption PR. Use when releasing or spinning up a new comfyui node pack. |
| allowed-tools | Bash, Read, Write, Edit, Grep, Glob, TodoWrite, AskUserQuestion |
comfy-node
Take a ComfyUI custom-node idea and drive it through every step from empty
to publish-ready, collapsing the manual repo-creation + gitops wiring into one
orchestrated pass with a single human approval gate.
This is the orchestrator around the comfyui-node-scaffold skill (which only
generates the local repo). Use comfyui-node-scaffold alone if you just want
the files; use this when you want the GitHub repo created, pushed, and
adopted into gitops too.
Canonical owner of the gitops repo-adoption procedure. Phases 3–5 below
(seed main → open the gitops PR → human gate → remove the import block),
the branch-protection hook note, and the generic failure-mode rows are the
single source of truth for every laurigates repo class, not just ComfyUI
packs. foundryvtt-plugin:foundryvtt-module carries only its own deltas and
defers here. If you arrived from another skill, read
Adapting Phases 3–5 to another repo class
first — it names every value you substitute.
When to Use This Skill
| Use this skill when... | Use the alternative when... |
|---|
| The user gives an idea and wants the whole pipeline stood up — repo created, seeded, and gitops-adopted | You only want the local files → comfyui-node-scaffold |
Spinning up a project:comfyui-nodes taskwarrior backlog idea end to end | Adding a node to an existing pack → edit that repo directly |
Do not use it to add a node to an existing pack, or to publish a release
(release-please + publish.yml already automate that once the repo exists).
The shape it automates
flowchart LR
idea["idea"] --> sc["scaffold.py"] --> gh["gh repo create<br/>+ seed main"] --> gop["gitops PR<br/>(entry + import block)"]
gop --> gate["👤 merge gitops PR"] --> apply["tofu-apply on release:<br/>adopt + secrets + protection"] --> rm["remove import block"] --> impl["implement + release"]
classDef g fill:#1b4332,stroke:#2d6a4f,color:#fff
classDef m fill:#6a040f,stroke:#9d0208,color:#fff
class sc,gh,gop,apply,rm g
class gate,impl m
Everything left of the gate is one orchestrated pass. There is no scaffold
PR — the seed goes straight to main (see Phase 3 for why). The single gate
(merging the gitops PR) is intentionally human — it feeds the apply pipeline on
shared infra state (release-please cuts a gitops release, whose publication
triggers the tofu-apply.yml GitHub Actions workflow). Never merge it on the
user's behalf.
Preconditions
- Run from the workspace root
repos/laurigates/ (new repo lands as a sibling
of the reference packs; modal-variant primitive copy resolves there).
gh auth status is a personal account that can create repos. The gitops
GitHub App cannot create repos on user accounts — that is exactly why the
repo is created out-of-band here and then imported into Terraform state.
- The gitops repo is clean (no uncommitted
repositories.tf / main.tf
changes) so the orchestrator's gitops PR is isolated.
Phase 0 — Derive and confirm the spec
From the idea, derive and show the user before creating anything external:
| Field | How to derive | Example |
|---|
--name | comfyui-<kebab>; reuse the family prefix (touch-… for touch UX). | comfyui-touch-resize |
--display | Title-case. | Touch Resize |
--desc | One line, registry-facing. | Selection-gated pinch-to-resize for ComfyUI nodes and groups on touch devices. |
--variant | gesture for canvas interactions (resize/move/region); frontend for a per-widget modal; backend only if it reads disk / serves data. | gesture |
--widgets | CSV of target widget names (modal variants only; omit for gesture). | — |
| topics | ["comfyui","comfyui-nodes",…] + facet tags. | …,"mobile","touch","resize" |
Confirm the name and variant with the user — these are hard to change after the
repo exists. If the idea matches a project:comfyui-nodes task, mark it
in_progress:
task project:comfyui-nodes export | jq -r '.[] | select(.description | test("resize"; "i")) | .uuid'
Phase 1 — Preflight (fail fast if the name is taken)
test ! -e comfyui-touch-resize && echo "local: free" || echo "local: EXISTS"
grep -q '"comfyui-touch-resize"' gitops/repositories.tf && echo "gitops: EXISTS" || echo "gitops: free"
gh repo view laurigates/comfyui-touch-resize >/dev/null 2>&1 && echo "github: EXISTS" || echo "github: free"
All three must report free. Stop and surface any collision.
Phase 2 — Scaffold + local green check
python3 ${CLAUDE_SKILL_DIR}/../comfyui-node-scaffold/scaffold.py --name comfyui-touch-resize --display "Touch Resize" --desc "Selection-gated pinch-to-resize for ComfyUI nodes and groups on touch devices." --variant gesture
Then bring the pack to green locally (the scaffold prints these too):
cd comfyui-touch-resize
uv sync --group dev
npm install --no-audit --no-fund
just check
just check must pass before anything is pushed. If it fails, fix locally and
re-run — do not create the remote repo on a red pack.
The scaffold prints a finishing-pass audit (issue #1877): it emits the
registry icon/banner SVGs + Icon/Banner wiring and the renovate /
registry-health / clear-autorelease workflows, and grades the follow-ups it can't
do itself. Before the first release, run just assets (rasterizes icon.svg /
banner.svg → the PNGs the registry serves; needs rsvg-convert) and commit the
PNGs. The screenshot pipeline stays deferred to the comfyui-screenshot-pipeline
skill (just screenshots) — surface it as a Phase 6 follow-up.
Treat the audit's ERROR rows as blocking, not as notes: Icon/Banner already
point at PNG URLs, so a pack that skips just assets publishes a 404 icon. The
pack's own tests/test_publish_hygiene.py fails CI on exactly that, and Phase 6
re-checks it before hand-back.
Phase 3 — Create the GitHub repo and seed main
Seed main directly as the first commit — no scaffold branch, no PR. The
repo has no branch protection yet (gitops adds it on adoption in Phase 5), so
this is allowed, and it avoids the branch juggling you'd otherwise hit: if the
first push were a feature branch, main would be missing on origin, forcing a
later rename + default-branch change + base-branch fixups. Pushing main first
sidesteps all of it. Implementation work afterward goes through feature-branch
PRs as normal (protection is live by then).
git init -b main
git add -A
git commit -m "feat: scaffold comfyui-touch-resize (gesture pack)"
gh repo create laurigates/comfyui-touch-resize --public --source . --remote origin --push
Branch-protection hook note (expect this): in a Claude Code session the
branch-protection hook will block the agent from git add/commit on
main (confirmed on the first real run). For a brand-new, not-yet-protected
repo this is a false positive. Hand the whole seed to the user as one
paste-safe line to run with the ! prefix, e.g.:
cd <repo> && git add -A && git commit -m "feat: scaffold <name> (gesture pack)" && gh repo create laurigates/<name> --public --source . --remote origin --push
(git add -A and &&-chaining are fine in the user's shell — those hooks
are agent-side.) Do not work around it by seeding a feature branch — that
reintroduces the missing-main/rename juggling this phase exists to avoid.
Don't fight the hook with quoting tricks either.
The --push makes the seeded main the default branch.
Phase 4 — Open the gitops PR (entry + transient import block)
Two edits in the gitops/ repo, on a dedicated branch.
gitops/repositories.tf — add to the active repositories locals block,
next to the other comfyui-* entries (mirror comfyui-touch-connect):
"comfyui-touch-resize" = {
description = "Selection-gated pinch-to-resize for ComfyUI nodes and groups on touch devices"
visibility = "public"
release_please = true
comfy_registry = true
topics = ["comfyui", "comfyui-nodes", "mobile", "touch", "resize"]
}
gitops/main.tf — add a transient import block alongside the existing
ones at the top of the file:
import {
to = github_repository.this["comfyui-touch-resize"]
id = "comfyui-touch-resize"
}
Validate, branch, commit, push, open the PR (run inside gitops/):
just check
git -C gitops switch -c feat/adopt-comfyui-touch-resize
git -C gitops add repositories.tf main.tf
git -C gitops commit -m "feat: adopt comfyui-touch-resize (comfy_registry)"
git -C gitops push -u origin feat/adopt-comfyui-touch-resize
gh pr create -R laurigates/gitops -a laurigates -l chore -l opentofu --title "feat: adopt comfyui-touch-resize (comfy_registry)" --body-file /tmp/gitops-pr-body.md
Write a short body (to /tmp/gitops-pr-body.md) rather than --fill — it's an
infra PR that triggers an apply, so spell out what merge does: imports the repo,
pushes REGISTRY_ACCESS_TOKEN + release-please credentials, applies the
branch-protection ruleset, and that a follow-up PR removes the import block. Use
labels chore + opentofu (both exist in the gitops repo; check
gh label list -R laurigates/gitops if unsure).
Set metadata per github-metadata-hygiene (assignee laurigates; skip
self-reviewer — the author is the running user). The tofu-plan.yml workflow
posts the plan as a comment on the PR; the expected plan imports the repo
and creates the
REGISTRY_ACCESS_TOKEN secret + release-please var/secret + branch-protection
ruleset.
Phase 5 — Human gate, then finish
Hand the user the new repo URL and the gitops PR URL. The user merges the
gitops PR — that starts the apply chain on shared infra state (release-please
cuts a gitops release PR; merging that publishes a release, which triggers
tofu-apply.yml). Do not merge it for them.
After the user confirms the tofu apply landed, verify the wiring and remove the
now-dead import block:
gh secret list -R laurigates/comfyui-touch-resize
gh api repos/laurigates/comfyui-touch-resize/actions/variables/RELEASE_PLEASE_APP_ID --jq .name
REGISTRY_ACCESS_TOKEN should be listed; the variable lookup should return its
name. Then open the import-block-removal follow-up PR (it is a one-time
adoption artifact — leaving it is harmless but untidy):
git -C gitops switch -c chore/remove-comfyui-touch-resize-import
Remove the import { … "comfyui-touch-resize" … } block from main.tf, then:
git -C gitops commit -am "chore: remove one-time import block for comfyui-touch-resize"
git -C gitops push -u origin chore/remove-comfyui-touch-resize-import
gh pr create -R laurigates/gitops -a laurigates -l chore --fill --title "chore: remove comfyui-touch-resize import block"
Adapting Phases 3–5 to another repo class
Phases 3–5 are repo-class-agnostic: the seed-main-first rationale, the
branch-protection hook workaround, the import-block mechanics, the human gate,
and the import-block-removal follow-up are identical for a ComfyUI pack, a
FoundryVTT module, or anything else gitops adopts. Only these values change —
substitute them and the phases read verbatim:
| Substitute | ComfyUI pack (the examples above) | How to find yours |
|---|
| Repo name | comfyui-touch-resize | The caller skill's Phase 0 spec |
| Workspace → gitops path | gitops/ (run from repos/laurigates/) | gitops/ if the clone is a sibling; ../gitops/ from a nested workspace |
repositories.tf adoption flags | release_please = true + comfy_registry = true | release_please = true is universal; extra flags are per-repo-class |
| Seed commit subject | feat: scaffold <name> (gesture pack) | feat: scaffold <name> (<variant> <noun>) |
| Phase 5 verification | gh secret list (REGISTRY_ACCESS_TOKEN) and the RELEASE_PLEASE_APP_ID variable lookup | Check one probe per flag you set; release_please always means the RELEASE_PLEASE_APP_ID variable |
Everything else — the commands, the PR-body guidance, the labels, the metadata
hygiene, the failure-mode rows below — applies unchanged.
Phase 6 — Verify the finishing pass, then hand back
Run the gate before declaring done — do not close on a self-authored summary:
python3 ${CLAUDE_SKILL_DIR}/../comfyui-node-scaffold/scaffold.py --verify comfyui-touch-resize
Read STATUS=; the exit code is 1 on ERROR.
STATUS= | Action |
|---|
ERROR | Not done. Finish it now — the ERROR rows are publish-blocking (missing icon.png/banner.png, or surviving PLACEHOLDER-GLYPH). just assets, commit the PNGs, re-run. |
WARN | Deferrable. Log each WARN to project:comfyui-nodes in taskwarrior (the queue that outlives this session) and say so in the hand-back. |
OK | Hand back. |
An ERROR is never a follow-up item. It shipped that way three times —
comfyui-touch-manager (Icon = "", weeks), comfyui-output-swap (31 hours),
comfyui-touch-shim — each recorded in the closing report as "tracked, not
blocking" and each caught only when the user noticed.
The pipeline is now live: conventional-commit feature PRs → merge → release-please
PR → merge → tag → publish.yml publishes to registry.comfy.org. Tell the user
what's left:
- Implement the pack logic (for
gesture: tune web/js/<short>.js — groups
support, affordance hint, the anisotropic-scale TODO).
- First merged
feat:/fix: commits drive the first release-please PR.
Log durable follow-ups (groups support, browser smoke matrix, the jsdom modal
DOM test gap) to project:comfyui-nodes per taskwarrior-cross-session.
Failure modes & guards
| Symptom | Cause | Fix |
|---|
publish.yml fails Option '--token' requires an argument | comfy_registry flag not yet applied | Confirm the tofu apply landed; gh secret list shows REGISTRY_ACCESS_TOKEN; re-run gh workflow run publish.yml -R laurigates/<name> |
release-please job fails on empty app-id | release_please credentials not applied, or repo on the legacy PAT workflow | The scaffold ships the App-token release-please.yml; confirm the apply landed (gh api repos/laurigates/<name>/actions/variables/RELEASE_PLEASE_APP_ID --jq .name returns a name), re-run via workflow_dispatch |
403 Resource not accessible by integration on repo create | Tried to create via the gitops App, not a personal token | Create with personal gh auth; the App only adopts via import |
| The tofu plan shows a create (not import) for the repo | Import block missing or id wrong | The id is the bare repo name, not owner/name; add/fix the import block |
just check red in gitops | tofu fmt/validate failure | just format then re-check before pushing |
Notes
- The orchestrator never runs
tofu apply — all applies go through the gitops
repo's tofu-apply.yml GitHub Actions workflow, triggered by publishing the
release-please release (see gitops/CLAUDE.md). Local gitops work is
plan/validate only.
- The scaffold now emits the registry finishing-pass pieces (icon/banner SVGs +
wiring, renovate + registry-health + clear-autorelease workflows) and audits
for the rest;
just assets (rsvg-convert) produces the served PNGs. See the
finishing-pass note in Phase 2 (issue #1877).
- Screenshots pipeline +
docs/blueprint/ PRD/ADR set are not scaffolded; add
them later from a reference pack (the comfyui-screenshot-pipeline skill) if
the pack warrants them.