The dev symlink this described is gone. All four dreevelle packages are now ComfyUI-Manager installs under their lowercase registry ids, and the working tree is not wired into ComfyUI at all, so the old wording would send someone editing files that are not live. Replaced with the accurate description, matching what ComfyUI-ResolutionTools already documented, including the reason not to leave a dev symlink in place: Manager's remove/replace path rmtree's the *resolved* path, so it deletes a symlink's target contents rather than unlinking. That destroyed a working tree on 2026-08-09. Keeping the warning in every package means it is present wherever the symlink recipe is. Docs only; CLAUDE.md is excluded from the published archive by .comfyignore, so nothing here reaches the registry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015p88qHFcfungDkCbG1n7z2
7.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What this is
A single-file ComfyUI custom node package. All code lives in __init__.py; there
is no build step, test suite, or linter config. The whole extension is one node,
SaveImageSequenceFast, which saves an image batch as a PNG sequence with the
frames encoded in parallel.
The node is registered through ComfyUI's V3 schema API, not the legacy
NODE_CLASS_MAPPINGS dict: comfy_entrypoint() returns a ComfyExtension whose
get_node_list() yields IO.ComfyNode subclasses. Inputs are declared in
define_schema() and arrive as named kwargs to execute(); hidden inputs
(prompt, extra_pnginfo) are read off cls.hidden, not from the signature.
Local environment
- ComfyUI checkout:
/home/dreevelle/comfy/ComfyUI - Python venv (has
av,torch,numpy):/home/dreevelle/comfy-env/bin/python - The package is published, and
ComfyUI/custom_nodes/comfyui-fast-image-sequence(lowercase, the registry id) is a ComfyUI-Manager install that tracks registry releases. This working tree is not wired into ComfyUI.
To test a change before releasing it, symlink this tree in under its CamelCase name so it doesn't collide with the Manager install:
ln -sfn /home/dreevelle/Projects/ComfyUI-FastImageSequence \
/home/dreevelle/comfy/ComfyUI/custom_nodes/ComfyUI-FastImageSequence
Remove that symlink before doing anything in ComfyUI-Manager for this
package. Manager's remove/replace path does shutil.rmtree on the resolved
path, so it deletes the symlink target's contents instead of unlinking — on
2026-08-09 that wiped a sibling package's working tree, .git included, and it
had to be restored by re-cloning. Commit and push before any Manager operation,
and never leave a symlinked dev copy in place while installing or updating the
published version.
Run ComfyUI:
/home/dreevelle/comfy-env/bin/python /home/dreevelle/comfy/ComfyUI/main.py
Verifying changes
__init__.py imports folder_paths, comfy.cli_args, and comfy_api.latest at
module scope, so it cannot be imported standalone — ComfyUI must be on
sys.path. To exercise the pure-encoding helpers (_encode_png,
inject_png_metadata) without launching the server:
cd /home/dreevelle/comfy/ComfyUI && PYTHONPATH=/home/dreevelle/comfy/ComfyUI \
/home/dreevelle/comfy-env/bin/python -c "
import importlib.util, torch
spec = importlib.util.spec_from_file_location('fis', '/home/dreevelle/Projects/ComfyUI-FastImageSequence/__init__.py')
m = importlib.util.module_from_spec(spec); spec.loader.exec_module(m)
print(len(m._encode_png(torch.rand(8, 8, 3), '16-bit')))
"
The project's core invariant is byte-identical output to the built-in
Save Image (Advanced) node. Any change to the encode path should be checked by
encoding the same tensor through both _encode_png here and _encode_image in
comfy_extras/nodes_images.py and comparing the bytes.
Architecture
Three layers in __init__.py, in order:
_encode_png— tensor → PNG bytes, entirely in memory. Quantizes to uint8/uint16 via_FORMAT_SPECS, then encodes through a rawav.CodecContextrather than a container: PyAV'simage2muxer needs a real file path and won't write toBytesIO, and for a single-frame PNG the codec output is the file. Note the little-endian frame format / big-endian stream format pairing for 16-bit — thereformat()call handles the swap.inject_png_metadata— splicestEXtchunks in after IHDR by parsing the IHDR length from bytes 8:12. Operates on finished PNG bytes, so it's independent of the encoder.SaveImageSequenceFast.execute— filename/counter allocation viafolder_paths.get_save_image_path, then aThreadPoolExecutoroverencode_and_write. Parallelism works because PyAV releases the GIL during zlib compression.pool.mapis used specifically because it preserves input order, keeping the returned UI results frame-ordered.
Because the node allocates one counter for the whole batch and derives each
filename as counter + batch_number, a single invocation owns a contiguous
numbering range. Splitting one sequence across two save nodes collides.
Relationship to ComfyUI core
Layers 1 and 2 are deliberately copied from
comfy_extras/nodes_images.py rather than imported, so a core update can't
silently break this node. That means upstream changes don't propagate: when
touching the encode path, diff against that file.
The copy is also a subset. Upstream keys _FORMAT_SPECS by
(file_format, bit_depth, num_channels) and supports EXR, colorspace
conversion, and 1-channel grayscale (including 2D HxW tensors); here the key is
(bit_depth, has_alpha) and only 3- and 4-channel PNG exist. A grayscale input
raises KeyError — if that needs supporting, port the num_channels-keyed spec
table and the ndim == 2 unsqueeze from upstream.
Conventions
- Version is duplicated in
__version__(__init__.py) and[project].version(pyproject.toml) — bump both. dependenciesstays empty:av,numpy, andtorchship with ComfyUI.- Respect
args.disable_metadata(ComfyUI's global CLI flag) alongside the node's ownmetadataoption in any metadata-related change.
Releasing
Published on the Comfy Registry as comfyui-fast-image-sequence under publisher
dreevelle, which is what makes it installable from ComfyUI-Manager.
Changing [project].version in pyproject.toml on main publishes a
release. .github/workflows/publish_action.yml watches that file's path and
runs Comfy-Org/publish-node-action, authenticating with the
REGISTRY_ACCESS_TOKEN repo secret. Don't touch the version field unless a
release is intended; republishing an existing version fails with
400 The node version already exists.
.comfyignore keeps dev-only files (CLAUDE.md, .github/) out of the
published archive. comfy node validate runs the registry's checks locally and
comfy node pack produces the exact archive that would be uploaded — inspect it
before releasing if the file list may have changed.
assets/icon.png is the registry icon, pointed at by [tool.comfy] Icon as a
raw GitHub URL on main. The registry stores the URL, not the bytes, and the
Manager hotlinks it, so replacing this file on main restyles every card with
no republish. Only changing the URL needs a version bump, because icon is
node-level metadata written as a side effect of publishing a version.
The background is transparent on purpose. The frontend's PackBanner draws
the icon twice into a 7:3 box — once as a backdrop, bg-cover plus blur(10px)
at opacity-30, and once object-contain on top. An opaque icon therefore shows
as a hard rectangle: bg-cover crops to the centre and zooms ~2.3x, so it samples
mostly glow and lifts the surround to ~47, while the contained copy still shows
the icon's own near-black corners at ~17. Deriving alpha from luminance removes
the edge completely, and it is the right derivation because the artwork is
additive glow on near-black. The cost, accepted deliberately, is that the pale
mark washes out on ComfyUI's light theme.
Regenerate from the brand original, capped at the registry's 400x400 square maximum. Downscale in linear light; the naive sRGB-space resize dulls the ring stroke and blurs the bar separations inside the D:
magick ~/Projects/alora/dev/assets/brand/dreevelle_pfp.png \
-colorspace RGB -filter Lanczos -resize 400x400 -colorspace sRGB \
\( +clone -grayscale Rec601Luma -level 7.45%,100% \) \
-alpha off -compose CopyOpacity -composite \
-background none -alpha Background -strip assets/icon.png
oxipng -o max --strip safe assets/icon.png
Do not set [tool.comfy] Banner. PackBanner resolves banner_url || icon, so
a banner replaces the mark rather than sitting behind it.
Check what's live:
curl -s https://api.comfy.org/nodes/comfyui-fast-image-sequence/versions