Skip to content

Repository files navigation

comfyui-andypack — Animation Coordinator

A ComfyUI custom-node pack for building large, direction-aware character animation sets from a single manifest. It is a dependency-aware FFLF (first-frame / last-frame) resolver: it drives character → pose → animation selection from one animations.json, gates what you can generate on what is already rendered, and feeds your sampler the right positive/negative prompts and start/end anchor images.

It does not sample or generate. You build the FLUX (pose/frame edits) and WAN (animation) graph; this pack resolves prompts, reference images, dependency gating, and completion metadata, and writes the results back to disk so the next node in the chain unlocks.


Mental model

Everything is one dependency graph of two rendered node kinds, rooted at the base pose:

graph TD
  ref["character reference<br/>(node input, persisted as _reference.png)"] --> base
  manikin["manikin[dir]<br/>(bundled, per direction)"] --> base["base pose<br/>(root, per direction)"]
  base --> stance["fighting_stance pose"]
  stance --> idle["fighting_stance_idle<br/>(WAN loop)"]
  base -. start_from .-> entry["fighting_stance_entry"]
  idle -. end_at .-> entry
  idle -. start_from + end_at .-> punch["punch / kick / …"]
Loading
  • Base pose — the tree root. Each of its 8 directions is a FLUX.2 multi- reference edit of the character reference image (a Character Creator input, persisted as _reference.png so it can be reloaded) paired with the bundled manikin for that direction (which supplies the camera angle / body orientation). Renders to _base/{dir}.png + a sidecar. A character's prompt layer lives in character.json (no provenance — base sidecars root staleness).
  • Pose — a per-direction still produced by a FLUX.2 edit of a source image (another pose). A pose with no from is a root pose (base). Renders to _{pose}/{dir}.png + a sidecar.
  • Animation — a Wan 2.2 i2v clip. Renders to {anim}/{dir}/frame_*.png + meta.json.

FFLF cross-wiring

Every animation needs a start image (the I2V initial latent): its explicit start_from, otherwise the manifest's defaults.start_from. end_at is optional — when present, the clip is FFLF. The cross-wiring is:

  • start_from consumes the dependency's last frame.
  • end_at consumes the dependency's first frame.
  • A single-image dep (a pose) resolves the same image for either slot.

Looping is a consequence of FFLF, not a flag. There is no loop field. A clip loops when its start and end anchors resolve to the same image (e.g. start_from and end_at both pointing at one pose) — it begins and ends on the same frame. The Animation Writer detects that and drops the duplicated final frame so the clip plays seamlessly on repeat.

Cascading prompts

Each render's final positive and negative are merged from layers, general → specific:

globals[kind] → entity

The character prompt layer (character.json) and the per-direction layer are not cascade layers — they surface only via the opt-in template variables, resolved by field context (positive vs negative):

Variable Expands to
{character_prompt} the character's positive_prompt / negative_prompt
{view_phrase} the manifest-level view_phrases[direction] camera phrase (positive only)
{direction_prompt} the entity's own per-direction positive_prompt / negative_prompt
{direction_name} the bare direction name (e.g. EAST)

{view_phrase} is the key to the 8-direction workflow: per-direction camera language lives once in the manifest's view_phrases map, so every pose and animation can opt into all 8 directions with empty per-direction layers and still get correct, affirmative view language — including the turnaround failure-mode mitigations ("only one eye visible" on profiles, "no face visible" on back views). The manifest stays character-agnostic: identity arrives only via {character_prompt}.

Positives are joined as prose; negatives are merged as a deduped, comma-separated term list. The merged prompt is hashed into the sidecar/meta.json as prompt_hash.

FLUX.2 Klein has no negative-prompt path, so the seed manifest's poses carry no negative layer — pose failure modes are mitigated affirmatively in the positive (via view_phrases). The negative pipeline is for the Wan animation path (effective at CFG > 1). See docs/prompting-guide.md.

Staleness

Staleness is transitive on the prompt hash. A complete node is stale if its own merged-prompt hash drifted from what was rendered, or any ancestor is stale. Editing the character prompt layer or any cascade layer ripples downstream. A stale node stays selectable — it just shows amber so you know to re-render.


Installation

Clone (or symlink) this repo into your ComfyUI custom_nodes/ directory:

cd ComfyUI/custom_nodes
git clone https://github.com/andyhite/comfyui-andypack.git

Restart ComfyUI. The nodes appear under the andypack category, and the web extensions (web/anim_coord.js — dynamic combos; web/anim_coord_panel.js — the sidebar manager) load automatically.

Runtime deps are the ones ComfyUI already provides (torch, numpy, Pillow, aiohttp). No extra install step is required for normal use.

Where files live

What Location
Manifests ComfyUI/user/default/andypack/animations/*.json
Character output ComfyUI/output/characters/<character>/

A character is any directory under the characters root containing a character.json, a _reference.png, a pose dir, or an animation dir. The Character Creator always persists the reference art, so a character can be reloaded (via the Character Loader) and its base pose re-generated later.

output/characters/cortex/
  character.json                    character prompt layer { positive_prompt?, negative_prompt? } (no provenance)
  _reference.png                    persisted reference art (read back by the Character Loader)
  _base/EAST.png   _base/EAST.json  base pose frame + sidecar (the tree root)
  fighting_stance_idle/EAST/
    frame_00000.png … frame_000NN.png
    meta.json                       written LAST (atomic) = completion sentinel

There is no .complete file. The sidecar / meta.json is written last via temp-file + atomic rename; its presence is the completion signal. A directory with no parseable meta/sidecar reads as incomplete.


Nodes

All nodes live under the andypack category, grouped into Manifest / Character / Reference / Pose / Animation / Sprite / Export / Diagnostics / Loop. Custom passthrough types: ANIM_MANIFEST (the loaded, validated manifest), ANIM_POSE (a pose job bundle from a selector to its writer) and ANIM_ANIMATION (an animation job bundle). The *Unpack nodes fan a bundle out into individual typed outputs.

andypack/Manifest

Node Role
Manifest Loader Load + validate animations.json (ref typing, cycle detection, 4n+1 length + view_phrases lint). Cached by file mtime.

andypack/Character

Character is pure CRUD: the Creator only ever writes, the Loader only ever reads. Base-pose emission (pairing the reference image with the bundled manikin for a multi-reference FLUX.2 edit) lives on the Pose Selector now — see andypack/Pose below.

Node Role
Character Creator Write-only: persist a character's character.json prompt layer and its reference art (_reference.png). Passes through (CHARACTER, REFERENCE) so a create graph can chain straight into the Pose Selector — no manifest/direction inputs, no pose output.
Character Loader Read-only merged reader: emit (REFERENCE, POSITIVE, NEGATIVE) from a character's persisted identity. A missing reference is not an error — it returns the empty-image sentinel, so this also works in the bootstrap stage (prompts authored, reference art not yet generated).

andypack/Reference

The manikin/pose-reference authoring nodes — build a named pose-reference turnaround (pose_references/<name>/) independent of any character or manifest.

Node Role
Manikin Loader Load a bundled per-direction manikin as an IMAGE (+ its direction name) — the pose/camera source for authoring custom pose references (drive an OpenPose/ControlNet-capable graph per direction).
Reference Bundle Assemble an ANIM_POSE from loose images (source + pose_reference + positive) for the manikin-turnaround authoring flow — feeds Pose Conditioning without a character/manifest.
Reference Selector Emit the next un-authored direction of a named turnaround (pose_references/<name>/) with the front posed-manikin as source + the bundled manikin as guide. Drive it in a Sweep Loop to author all 8 directions.
Reference Writer Save an IMAGE as pose_references/<name>/<DIRECTION>.png and return REMAINING (the sweep-loop continue signal).

andypack/Pose

Node Role
Pose Selector mode=sweep emits the next actionable (ready/stale) pose in dependency order — drive it inside a Sweep Loop to batch-generate every pose in one Queue press; raises when none remain. include_base=on also emits root (base) poses paired with their manikin — this is now where the base pose is emitted from, so one graph (→ Pose Conditioning) drives the whole turnaround, base + derived. mode=target force-regenerates one named pose@direction as a spot-fix, ignoring the completeness gate. Takes an optional flow (SWEEP_FLOW) input to sit inside a Sweep Loop, and an optional reference (IMAGE) that overrides the persisted reference art for a root pose — wire the Character Creator's REFERENCE output straight in for a one-press create graph.
Pose Unpack Fan an ANIM_POSE out into SOURCE_IMAGE, POSE_REFERENCE, POSITIVE_PROMPT, NEGATIVE_PROMPT, OUTPUT_DIR, HAS_POSE_REFERENCE (and forward the bundle).
Pose Conditioning One-node FLUX.2 pose-edit conditioning: text-encode + source (and pose-reference-when-present) reference latents + zeroed negative + empty latent → (positive, negative, latent).
Pose Writer Write {dir}.png then the {dir}.json sidecar last (atomic). Returns (OUTPUT_DIR, REMAINING)REMAINING is the live post-write actionable count (sweep mode) or 0 (target mode), feeding a Sweep Loop's continue/stop signal. Optional write_mirrored also writes a horizontally-flipped copy into every mirror_map direction derived from the one just written, each with its own sidecar and a mirrored_from provenance key (sound only for bilaterally symmetric designs — see docs/prompting-guide.md §4).

andypack/Animation

Node Role
Animation Selector mode=sweep emits the next actionable animation in dependency order — drive it inside a Sweep Loop to batch-generate every clip in one Queue press; raises when none remain. category scopes the sweep to one manifest category. mode=target force-regenerates one named animation@direction as a spot-fix. Emits an ANIM_ANIMATION bundle: START_IMAGE, END_IMAGE, IS_FFLF, merged prompts, plus LENGTH/FPS/WIDTH/HEIGHT/SHIFT that wire straight into WanFirstLastFrameToVideo + ModelSamplingSD3. Takes an optional flow (SWEEP_FLOW) input to sit inside a Sweep Loop.
Animation Unpack Fan an ANIM_ANIMATION out into its typed outputs (start/end image, prompts, is_fflf, length, fps, width, height, shift, output_dir).
Animation Conditioning One-node Wan 2.2 conditioning from an ANIM_ANIMATION bundle: text-encode + core FFLF encode, omitting end_image entirely for non-FFLF clips (the sentinel never reaches the sampler). Wire SHIFT into ModelSamplingSD3 as before.
Animation Writer Write frame_{:05d}.png, trim the duplicate closing frame of a seamless loop, then write meta.json last (atomic). Records the sampler seed. Returns (OUTPUT_DIR, REMAINING) — same sweep-loop continue/stop signal as Pose Writer. Optional write_mirrored (same semantics as Pose Writer's) and loop_color_match (ramps a per-channel color match toward the first frame across a derived loop clip, mitigating the low-noise expert's color/contrast drift on the start_image == end_image seam).
Animation Loader Load a rendered clip back as an IMAGE batch (+ fps) to reprocess (re-matte, re-pack, re-export) without re-sampling.

andypack/Sprite / andypack/Export

Node Category Role
Sprite Trim Sprite Trims transparent padding from each frame and records the pivot offset so all frames in a sheet stay consistently anchored.
Palette Lock Sprite Force frames onto one shared limited palette (optionally locked to a palette_image) for pixel-art consistency across directions/animations.
Frame Retime Sprite Resample a clip to a target fps (resample / trim / pad-hold) before packing/export.
Sprite Packer Sprite Packs a batch of frames into a single spritesheet image.
Sheet Builder Sprite Full-clip Stage-3 packer: rows = directions × cols = frames + a per-direction tagged atlas. Optional trim union-trims transparent padding across all directions before packing. entity_type: still packs a pose entity's per-state stills as single-frame tagged rows (state-sheets), threading any nine_slice into the atlas.
Atlas Writer Export Serializes frame coordinates, pivots, and per-frame duration to the engine format of your choice (incl. godot_stylebox for a 9-slice StyleBoxTexture).
Sprite Export Export GIF/APNG/WebP preview/export of a completed clip; preserves alpha (a 4-channel input, or a connected MASK, produces a transparent export instead of flattening to RGB).
Sheet Export Export Stage-3 batch export: one sheet + atlas per animation with ≥1 rendered direction, in one queue press. Skipped animations are listed in the report.
Layer Export Export Export a parallax background as an engine-ready layered scene: each rendered layer still is copied and a Godot ParallaxBackground .tscn wires them with per-layer motion_scale from the manifest scroll. Unrendered layers are reported.

andypack/Diagnostics

Node Role
Coverage Report A status table over every (entity, direction) for a character: generated / ready / stale / blocked, plus a JSON blob.
Turnaround Sheet Composites all rendered directions for a pose side-by-side as a contact sheet — catch drift between directions before packing.

andypack/Loop

Node Role
Sweep Loop Open Marks the start of a one-press sweep loop; emits a SWEEP_FLOW token wired into the sweep body's selector (flow) and into Sweep Loop Close (flow).
Sweep Loop Close Closes the loop: while the writer's REMAINING (wired to remaining) is > 0, clones and re-expands the Open→Close body so the engine runs another iteration; terminates cleanly at remaining <= 0.

Most of this is also driveable from the Andypack sidebar panel (see below).

Typical graph

  1. Manifest LoaderMANIFEST.
  2. Character Creator (persist character.json + reference art) — wire its REFERENCE output straight into the Pose Selector's optional reference input to skip the disk round-trip in a one-press create graph.
  3. Pose Selector (include_base=on, mode=target on base) → Pose Unpack → FLUX.2 multi-reference edit (SOURCE_IMAGE first, POSE_REFERENCE/manikin second) → Pose Conditioning → sampler → Pose Writer. This is also how every other pose is generated — the base directions are no longer a special Character-Creator-driven step.
  4. Pose Selector (mode=target, one pose/direction) → Pose Unpack → FLUX.2 edit → Pose Conditioning → sampler → Pose Writer. Walk poses in dependency order (poses that build on base).
  5. Animation Selector (mode=target, one animation/direction) → Animation UnpackWanFirstLastFrameToVideo (START_IMAGEstart_image, END_IMAGEend_image) → KSampler → VAE Decode → Animation Writer.

See Graph wiring for the exact FLUX.2 and Wan node connections.

Batch generation (one-press sweep loop). To work through a whole character without hand-picking every (entity, direction) or re-queueing per cell, set the Pose Selector / Animation Selector's mode to sweep and wrap the body (selector → … → writer) in Sweep Loop Open / Sweep Loop Close: wire Sweep Loop Open.flow into the selector's optional flow input AND into Sweep Loop Close.flow, and the writer's REMAINING output into Sweep Loop Close.remaining. Each pick still picks the next actionable job in dependency order, but now a single Queue press re-runs the body until nothing remains, instead of requiring a manual re-queue per cell (see examples/workflows/1b_turnaround_batch.json / 2_animate_fflf.json). Persist the character's reference art with the Character Creator first, then sweep the 8 base directions via the Pose Selector (include_base=on) same as every other pose.

The web extension repopulates the combos with live status glyphs after each writer run, so newly-unlocked nodes appear without a manual refresh:

✅ generated · 🔵 ready · 🟠 stale · 🔴 blocked


Manifest

The manifest is character-agnostic and identity-free — per-character prompt text lives only in each character's character.json. See examples/animations.json for a full, working manifest (generated by scripts/build_seed_manifest.py, which is the easiest way to author a large set), and the resolver code (andypack/manifest.py, andypack/resolve.py) for the authoritative schema.

Top-level keys: version, directions (canonical 8-way ordering), mirror_map, view_phrases (per-direction camera language, injected via {view_phrase}), defaults (fps / length / width / height / shift / start_from), globals (animation / pose cascade layers), poses, and animations. The base pose has no from (it is the tree root) and lists all 8 directions. Every anchor pose and animation lists all 8 directions too (usually with empty per-direction layers, leaning on view_phrases + the entity prompt).

A character can extend the manifest with its own poses / animations by adding them to its character.json; the merged manifest is re-validated, so a bad ref or a cycle is rejected rather than resolved silently.

Entity kinds (VFX / pickups / non-character lanes)

By default every entity is a character: identity comes from a character.json + _reference.png, stored under output/characters/<char>/, across the manifest's 8 directions. A manifest can declare other kinds for non-character assets — VFX, pickups, auras — via a top-level kinds map:

"kinds": {
  "flipbook": { "identity": "none", "root": "flipbooks", "directions": ["flat"] }
}

A kind declares its identity (character = reads character.json + reference art; style = reads a style.json prompt layer + optional _reference.png, injected via {style_layer}; none = no identity layer, no reference), a storage root (a subdir under the ComfyUI output dir), and its canonical direction-set (a 1-entry list like ["flat"] is directionless). An entity opts in with a "kind" field; entities with no kind stay characters, so existing manifests are unchanged.

The bundled examples/animations.json ships a worked flipbook example: a seed root still (the i2v first frame) and a pulse clip whose start_from and end_at both anchor to seed, so the resolver derives a seamless loop (there is no manifest loop flag). Select it with the Animation Selector's kind input set to flipbook; pack it with Sheet Export's kind set the same.

Environments & UI export (style layers, parallax, 9-slice)

Three generic capabilities cover zone/environment and UI-export lanes. All are authored as data — no game term touches the pack. See the seed's backdrop, parallax_bg, and meter_bar poses.

  • Style layer (identity: "style"). A kind whose instance is a place (a zone / level, e.g. environments/office/) carrying a style.json prompt layer + optional _reference.png. Every entity of that kind injects the instance's prompt via the {style_layer} template var (a value-level synonym of {character_prompt}, resolved positive/negative by field), so one edit to the zone style re-stales every backdrop in it. This is "palette per zone" made enforceable.
  • Parallax backgrounds. A pose whose direction-set is the layer-set (back / mid / front), each layer carrying numeric scroll (and optional scale) metadata. Render the layers like any pose, then run Layer Export (format: godot_parallax) to emit a Godot 4 ParallaxBackground .tscn wiring each layer's motion_scale from scroll. Layers with nothing rendered are reported, never silently dropped.
  • UI state-sheets & 9-slice. A ui-kind pose whose direction-set is the element's states (off / partial / full), optionally with a nine_slice object (left/right/top/bottom margins). Pack it with Sheet Builder in entity_type: still mode — one single-frame tagged row per state — then Atlas Writer / Sheet Export with format: godot_stylebox emits a StyleBoxTexture .tres carrying the margins (the nineSlice block is also added to the JSON atlas formats when present).

Manikins

The 8 bundled pose references in andypack/assets/manikins/<DIR>.png (one per canonical direction) supply the camera angle / body orientation for the base pose. The Character Creator pairs the character reference image with the matching manikin as a second FLUX.2 reference, so all 8 base directions are generated directly — base does not rely on mirror_map.

A pose can carry a pose-level reference_pose: the name of a turnaround folder under user/default/andypack/pose_references/<name>/ holding one <DIRECTION>.png per direction. Each direction's PNG replaces the bundled standing manikin as the FLUX reference (root pose) or adds a second reference (derived pose). A pose is blocked at any direction whose turnaround PNG is not yet authored. Build a turnaround with the Reference Selector + Reference Writer inside a Sweep Loop.


HTTP routes

Registered on PromptServer.instance.routes when running inside ComfyUI:

All routes return JSON only and take no client filesystem path — a manifest is addressed by a validated bare basename resolved under the pack's manifests dir, a character by a name snake-cased to one segment under the characters dir. There is nothing to traverse out of.

Route Purpose
GET /anim_coord/ping Liveness check the frontend uses before enabling inputs.
GET /anim_coord/characters List character directories (server-resolved).
GET /anim_coord/manifests List available manifest filenames.
GET /anim_coord/manifest_options?manifest=… Pose/animation → directions map (no rendered tree needed).
GET /anim_coord/options?manifest=…&character=… Every (pose|animation, direction) with its status + blocked_by.
GET /anim_coord/manifest?name=… Raw manifest JSON text (for the editor).
POST /anim_coord/manifest/save {name, content} Validate + atomically save a manifest (rejected, not saved, if invalid).
GET /anim_coord/character?character=… A character's character.json prompt layer.
POST /anim_coord/character/create {character} Create a character dir + empty character.json.
POST /anim_coord/character/save {character, positive_prompt, negative_prompt} Write a character's prompt layer (preserving any overlay).

Sidebar panel

web/anim_coord_panel.js registers an Andypack sidebar tab (app.extensionManager.registerSidebarTab) with three sections backed by the routes above:

  • Manifest — load / edit / validate-and-save a manifest JSON.
  • Characters — create a character and edit its character.json prompt layer.
  • Coverage — a live status grid over every (entity, direction) for a character, auto-refreshing after each graph run.

Graph wiring

The pack resolves and writes back; you build the FLUX.2 and Wan sampler graphs. The confirmed node wiring (late 2025 / 2026):

Poses — FLUX.2 Klein multi-reference edit. Per reference image (SOURCE_IMAGE first, POSE_REFERENCE/manikin second), normalize with ImageScaleToTotalPixels (~1 MP) then chain a ReferenceLatent per reference onto the conditioning. Distilled Klein: 4 steps, guidance ~1.0, Euler + Simple (never Euler Ancestral). FLUX.2 has no negative prompt — leave NEGATIVE_PROMPT unwired. Pipe the edit → Pose Writer.

Animations — Wan 2.2 14B i2v first-last-frame. The recommended path is Animation Selector → Animation Conditioning → samplers → Animation Writer: the conditioning node text-encodes the bundle's merged prompts and delegates to the core WanFirstLastFrameToVideo, omitting end_image entirely for a non-FFLF clip so one graph serves both FFLF and plain-i2v animations without a switch. Manual WanFirstLastFrameToVideo wiring remains valid too — driven by the standard Wan 2.2 i2v models (wan2.2_i2v_high_noise_14B + wan2.2_i2v_low_noise_14B, umt5_xxl text encoder, wan_2.1_vae). Wire START_IMAGEstart_image, END_IMAGEend_image (leave end_image unconnected when IS_FFLF is false), WIDTH/HEIGHT/LENGTH into the node, SHIFT into ModelSamplingSD3. The generated clip's final frame equals end_image, which is exactly what the FFLF cross-wiring assumes. Decode → Animation Writer (wire the sampler seed in for provenance). See docs/prompting-guide.md for prompt structure, the standard Wan negative block, and sampler settings.


Game-asset / sprite export

Once you have rendered poses and animation clips, comfyui-andypack provides a second layer of nodes for turning those renders into engine-ready game assets.

Alpha → trim → pack → atlas pipeline

Bring your own background-removal step (any BG remover node in your graph). Feed its output into the writers' optional MASK input (or supply a 4-channel RGBA image directly). The writers record has_alpha in the sidecar/meta and preserve the transparency throughout the pack chain. ComfyUI IMAGE tensors stay 3-channel inside the graph; RGBA materializes only at the disk boundary.

From there, the one-node path for a finished clip:

  • Sheet Builder — the Stage-3 packer. Loads every frame of every rendered direction of an animation and packs a game sheet (rows = directions, cols = frames) with a per-direction tagged atlas (fps included). Optional trim union-trims transparent padding across ALL directions' frames before packing (RGBA renders only), shrinking cells while keeping every direction registered. Feed its ATLAS + SHEET straight into Atlas Writer (aseprite / godot_spriteframes get one animation per direction).
  • Sheet Export — the batch form of the above: builds and writes a sheet + atlas for every animation in the character's manifest that has at least one rendered direction, in one queue press, instead of one Sheet Builder run per animation. Animations with nothing rendered are listed in the report, never silently dropped.

Or the manual chain when you want per-frame control:

  1. Animation Loader — load a rendered clip back as an IMAGE batch (+ fps) to re-process without re-sampling.
  2. Frame Retime — resample a clip to a target fps (resample / trim / pad_hold) before packing/export — Wan renders natively at 16 fps, game sprites often want less.
  3. Palette Lock — force every direction/animation onto one shared limited palette (optionally locked to a palette_image) for pixel-art consistency; run after background removal, before packing.
  4. Sprite Trim — trims transparent padding from each frame and records the pivot offset so all frames in a sheet stay consistently anchored.
  5. Sprite Packer — packs a batch of frames into a single spritesheet image.
  6. Atlas Writer — serializes frame coordinates, pivots, and per-frame duration to the engine format of your choice.
  7. Sprite Export — GIF/APNG/WebP preview of a completed clip; preserves alpha (a 4-channel input, or a connected MASK, produces a transparent export instead of flattening to RGB).

Diagnostics & conditioning helpers

  • Turnaround Sheet — composites all rendered directions for a pose side-by-side as a contact sheet. Use it to catch drift between directions before packing.
  • Coverage Report — a status table over every (entity, direction): generated / ready / stale / blocked. The at-a-glance "what's left to render."
  • Pose Conditioning — collapses the whole FLUX.2 pose-edit conditioning into one node: text-encodes the pose prompt, attaches the source image as a reference latent (and the pose reference too when the pose carries one — a base pose), and outputs (positive, negative, latent). With the Pose Selector's include_base, a single turnaround graph handles base (2-ref) + derived (1-ref) poses — no separate workflows.
  • Pose Selector / Animation Selector — mode + categorymode=sweep emits the next actionable pose/animation in dependency order (drive it inside a Sweep Loop for one-press batch fill); mode=target force- regenerates one named pose@direction or animation@direction as a spot-fix. category scopes the sweep to one manifest category (e.g. locomotion, combat); leave empty for all.

The node set was culled (2026-06-30) to the pipeline-essential node set for clarity; niche/unused nodes (variant/color batchers, boomerang, tween, extra reports, the one-frame Character Atlas Builder) were removed in favor of the core create → turnaround → animate → sheet path. Manikin control, mirroring, and palette lock were later reinstated as focused nodes (Manikin Loader / Reference Writer, the writers' write_mirrored flag, Palette Lock) once real authoring workflows needed them. The whole node surface was later renamed to a consistent <Domain> <Role> scheme, with pose-reference authoring split into its own andypack/Reference category and Character reduced to CRUD.


Development

pytest -q                 # tests
ruff check .              # lint
mypy andypack            # types

The resolver core (andypack/resolve.py, andypack/manifest.py, andypack/io.py, andypack/api.py) is pure stdlib — no ComfyUI or torch imports — so it is fully unit-testable from the fixtures in tests/. The torch/PIL bridge is isolated in andypack/images.py, and the ComfyUI node classes in andypack/nodes.py are thin wrappers over the pure core.

Layout

Module Responsibility
andypack/manifest.py Load, validate, ref-classify, cycle-detect.
andypack/resolve.py Merge prompts, hash, completeness, anchors, transitive staleness.
andypack/io.py Atomic writes, meta/sidecar builders, path safety.
andypack/api.py JSON payload builders + manifest/character CRUD for the routes.
andypack/server.py aiohttp route registration (read + write).
andypack/nodes.py ComfyUI node classes.
andypack/images.py torch/PIL ↔ ComfyUI IMAGE tensors.
andypack/manikins.py Bundled per-direction manikin pose references.
web/anim_coord.js Frontend: dynamic character-scoped combos + status glyphs.
web/anim_coord_panel.js Frontend: the Andypack sidebar manager panel.
scripts/build_seed_manifest.py Generator for the bundled seed manifest.

Claude Code plugin

This repo ships a Claude Code plugin, andypack-workflows, that teaches Claude to author ComfyUI workflows with these nodes — a router skill plus one per pipeline stage (create, turnaround, animate, export). Install it by adding this repository as a plugin marketplace in Claude Code; the plugin lives under plugin/ and versions in lockstep with the node pack. Each stage skill is anchored to a tested workflow in examples/workflows/, and tests/test_skill_node_refs.py fails CI if a skill's node references drift from the actual node surface.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages