Files
malkuthro-ComfyUI-Koolook/scripts/sync_to_dev_audio.py
T
malkuthroandClaude Opus 4.7 15d56882c5 feat(scripts): add dev-sync-audio — scoped sync for audio-lipsync iteration
`dev-sync-audio` is a scoped wrapper around `dev-sync` for the
[`docs/automations/LTX-2.3/audio-lipsync/`](docs/automations/LTX-2.3/audio-lipsync/)
iteration loop. It copies *only* the paths that module touches — the
`forks/whatdreamscost_koolook/` subtree and the root `__init__.py` —
instead of `rmtree`-ing the whole dest `forks/` and re-copying every
Koolook fork on every render. Same `KOLOOK_COMFYUI_DEV_PATH` target,
same ComfyUI-Manager restart, same two-line chat-report shape.

Changes
- `scripts/sync_to_dev_audio.py` (new). Thin wrapper that reuses
  `sync_to_dev`'s env loader, target validator, restart helper, and
  chat-report builder, supplying its own `AUDIO_PATHS` tuple. Defaults
  the `--scope` tag to `"audio-lipsync fork edit"` so the build
  identifier names this module even when invoked without an explicit
  scope.
- `scripts/sync_to_dev.py` — `sync()` gains an optional `paths` kwarg
  (defaults to the existing `RUNTIME_PATHS`). Backwards-compatible with
  every existing call site.
- `docs/automations/LTX-2.3/audio-lipsync/README.md` and
  `runs/LOOP.md` updated to point at `dev-sync-audio` instead of plain
  `dev-sync` for the iteration step.
- Project `CLAUDE.md` — new "Per-module scoped variants" subsection
  under the existing `dev-sync` section documents the
  `dev-sync-<module>` pattern, the trigger-name → script-name mapping,
  and reiterates that the user-initiated-only rule applies verbatim to
  every `dev-sync-*` variant.

Verification
- ruff check . → all passed
- python scripts/sync_to_dev_audio.py --dry-run -v →
    would copy: __init__.py
    would copy: forks/whatdreamscost_koolook
    e7f3ab4 - beautiful-mahavira-ae6da6
    would sync 2 entries -> <KOLOOK_COMFYUI_DEV_PATH>  (dev-sync-audio)
  (Confirms scope is correct and the existing dev-sync chat-report
  shape is preserved.)

User-initiated only — never automatic. Trigger phrase: `dev-sync-audio`
in chat (also: "copy audio fork", "sync audio fork"). Same rule as
`dev-sync` — see project CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 21:56:44 +02:00

163 lines
5.5 KiB
Python

#!/usr/bin/env python3
"""
Scoped variant of ``dev-sync`` for the LTX 2.3 audio-lipsync automation.
Copies only what that automation's iteration loop touches:
- forks/whatdreamscost_koolook/ the Koolook fork of LTXDirector +
its modified prompt_relay + the
verbatim-vendored patches.py
- __init__.py root loader (registers the fork's
node mappings; needs to be re-synced
when the fork's NODE_CLASS_MAPPINGS
set changes)
Everything else in the live install — ``forks/radiance_koolook/``, the
root ``k_*.py`` nodes, ``web/``, ``video_formats/`` — is left untouched.
USER-INITIATED ONLY. Same rule as plain ``dev-sync`` (see project
``CLAUDE.md`` → ``dev-sync`` section). Never run automatically:
* after a commit
* after a PR merge or ``/ship-pr``
* at session end / wrap-up
* on hook completion or "task complete" cleanup
* from any agent skill that doesn't explicitly require it
Run only on the explicit user trigger phrase ``dev-sync-audio`` (or
``copy audio fork``, ``sync audio``, etc.). The maintainer typically has
multiple parallel sessions across worktrees; an unsolicited sync from one
silently destroys what another is reviewing.
The target path comes from ``KOLOOK_COMFYUI_DEV_PATH`` in ``.env`` —
identical to plain ``dev-sync``. No new env var.
Usage:
python scripts/sync_to_dev_audio.py
python scripts/sync_to_dev_audio.py --dry-run
python scripts/sync_to_dev_audio.py --scope "vstr=10 trial"
python scripts/sync_to_dev_audio.py --no-restart
Exit codes mirror ``sync_to_dev.py`` exactly:
0 success
2 KOLOOK_COMFYUI_DEV_PATH unset or target missing without --init
3 --init refused
After copying, the script triggers a ComfyUI-Manager reboot so the new
Python files are actually loaded (custom-node modules load once at server
start; a file-only sync leaves ``.py`` changes invisible). Use
``--no-restart`` to opt out.
"""
from __future__ import annotations
import argparse
import os
import sys
from pathlib import Path
# Reuse the dev-sync infrastructure verbatim — same ``.env`` loader,
# same target validation, same restart, same chat-report shape — so the
# only thing that differs between full and scoped syncs is *which paths
# get copied*. Per-module wrappers stay small and the safety guarantees
# (target validation, parent-directory sanity check, restart fallbacks)
# only need to be audited in one place.
_SCRIPT_DIR = Path(__file__).resolve().parent
if str(_SCRIPT_DIR) not in sys.path:
sys.path.insert(0, str(_SCRIPT_DIR))
import sync_to_dev as _dev # noqa: E402
AUDIO_PATHS: tuple[str, ...] = (
"__init__.py",
"forks/whatdreamscost_koolook",
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print what would be copied without touching the target.",
)
parser.add_argument(
"--init",
action="store_true",
help=(
"Create the target directory if it doesn't exist. The parent "
"(typically a ComfyUI custom_nodes/ folder) must already be "
"in place. Use on first-run only."
),
)
parser.add_argument(
"--verbose",
"-v",
action="store_true",
help="Print each file/dir as it's copied.",
)
parser.add_argument(
"--scope",
type=str,
default="audio-lipsync fork edit",
help=(
"Short (≤10 word) description for the chat report's second line "
"and the in-browser footer in `web/_dev_build.json`. Defaults to "
"'audio-lipsync fork edit' so the build identifier always names "
"this module even when the maintainer doesn't pass a scope."
),
)
parser.add_argument(
"--no-restart",
action="store_true",
help=(
"Skip the auto-restart of the live ComfyUI server. Without a "
"restart, the new fork code stays loaded as the previous version "
"(custom-node .py files load once at server start)."
),
)
parser.add_argument(
"--restart-url",
type=str,
default=_dev.DEFAULT_RESTART_URL,
help=(
f"Override the restart endpoint. Default: {_dev.DEFAULT_RESTART_URL}. "
f"Ignored when --no-restart is set or --dry-run is used."
),
)
args = parser.parse_args()
_dev.load_dotenv(_dev.REPO_ROOT / ".env")
target_str = os.environ.get("KOLOOK_COMFYUI_DEV_PATH")
if not target_str:
print(
"KOLOOK_COMFYUI_DEV_PATH not set. Add it to .env (see .env.example).",
file=sys.stderr,
)
return 2
target = Path(target_str).expanduser()
err = _dev.ensure_target(target, init=args.init)
if err is not None:
return err
n = _dev.sync(
target,
dry_run=args.dry_run,
verbose=args.verbose,
paths=AUDIO_PATHS,
)
verb = "would sync" if args.dry_run else "synced"
print(_dev._build_line())
print(f"{verb} {n} entries -> {target} (dev-sync-audio)")
if not args.dry_run:
_dev.write_build_info(target, args.scope)
if not args.no_restart:
_ok, msg = _dev.trigger_restart(args.restart_url)
print(msg)
return 0
if __name__ == "__main__":
sys.exit(main())