CtrlK
BlogDocsLog inGet started
Tessl Logo

gamussa/reels-producer-skill

help quicky produce instagram reels and youtube shorts

94

2.12x
Quality

96%

Does it follow best practices?

Impact

87%

2.12x

Average score across 3 eval scenarios

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

check_deps.pyskills/reel-builder/scripts/

#!/usr/bin/env python3
"""Preflight: verify every tool this pipeline needs, before touching media.

Run this FIRST, every session. A missing dependency found at step 6 has already
cost a normalize pass; found here it costs nothing.

Exits non-zero when a REQUIRED tool is missing, so it can gate a workflow.
Optional tools are reported with the exact capability that is lost — never
silently substituted, because a fallback the user did not hear about is
indistinguishable from a bug.

Usage:
  check_deps.py                       # music-video requirements (default)
  check_deps.py --mode talking-head   # VO-spine requirements
  check_deps.py --json                # machine-readable, for gating
"""
import argparse, json, os, shutil, subprocess, sys
from pathlib import Path

VENV_PY = Path(".venv/bin/python3")


def which_version(exe, args, first_line=True):
    path = shutil.which(exe)
    if not path:
        return None, None
    try:
        r = subprocess.run([path] + args, capture_output=True, text=True, timeout=20)
        out = (r.stdout or r.stderr).strip()
        return path, (out.splitlines()[0][:60] if first_line and out else "")
    except (subprocess.SubprocessError, OSError):
        return path, ""


def python_module(mod):
    """Check a module inside the project venv, falling back to this python.

    Reports which interpreter satisfied it — a module present only in system
    python is NOT usable by the pipeline, which invokes .venv/bin/python3.
    """
    interp = str(VENV_PY) if VENV_PY.exists() else sys.executable
    r = subprocess.run(
        [interp, "-c", f"import {mod}; print(getattr({mod}, '__version__', ''))"],
        capture_output=True, text=True)
    if r.returncode != 0:
        return None, interp
    return (r.stdout.strip() or "present"), interp


def check(mode):
    rows = []

    def add(name, required, found, detail, lost):
        rows.append({"name": name, "required": required, "present": bool(found),
                     "detail": detail or "", "without_it": lost})

    p, v = which_version("ffmpeg", ["-version"])
    add("ffmpeg", True, p, v, "nothing runs — every stage shells out to it")
    p, v = which_version("ffprobe", ["-version"])
    add("ffprobe", True, p, v, "no probing, no duration validation")

    venv_ok = VENV_PY.exists()
    add(".venv", mode == "music-video", venv_ok,
        str(VENV_PY) if venv_ok else "",
        "macOS refuses system pip (PEP 668) — python deps cannot be installed")

    for mod, needed_for in (("librosa", "music-video"), ("soundfile", "music-video")):
        ver, interp = python_module(mod)
        add(mod, mode == needed_for, ver,
            f"{ver} via {interp}" if ver else "",
            "no beat detection — cuts cannot be synced to the track")

    # Optional: each names precisely the capability lost, so the agent can
    # tell the user what degrades instead of quietly choosing a lesser path.
    p, v = which_version("auto-editor", ["--version"])
    add("auto-editor", False, p, v,
        "tighten_vo.py uses its ffmpeg silencedetect path (measured equivalent)")
    p, v = which_version("deepFilter", ["--help"])
    add("deepFilter", False, p, "",
        "clean_audio.py uses ffmpeg afftdn — audibly weaker on noisy rooms")
    p, v = which_version("node", ["--version"])
    add("node", False, p, v,
        "make_cards.py cannot run — build cards as stills and use Ken Burns")
    ver, interp = python_module("moviepy")
    add("moviepy", False, ver, ver or "",
        "render_moviepy.py unavailable — no engine A/B (ffmpeg still renders)")
    p, v = which_version("yt-dlp", ["--version"])
    add("yt-dlp", False, p, v, "fetch_music.py cannot pull audio from a link")
    ver, interp = python_module("mlx_whisper")
    add("mlx-whisper", False, ver, ver or "",
        "no local transcription — captions must come from elsewhere, and "
        "talking-head reels are watched muted" if mode == "talking-head"
        else "no local transcription — captions must come from elsewhere")
    ver, interp = python_module("Vision")
    add("apple vision", False, ver, "" if not ver else "pyobjc",
        "no subject detection — build_cut_plan --score-in-points and "
        "detect_subjects.py are unavailable, in-points stay positional")
    p, v = which_version("magick", ["-version"])
    add("imagemagick", False, p, v,
        "gen_captions.py cannot render styled cards — only plain "
        "render_reel.py --srt captions")
    key = next((k for k in ("GEMINI_API_KEY", "GOOGLE_AI_API_KEY",
                            "GOOGLE_API_KEY") if os.environ.get(k)), None)
    add("gemini api key", False, key, key or "",
        "no hook-copy drafts or stylized thumbnails")
    return rows


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--mode", choices=("music-video", "talking-head"),
                    default="music-video")
    ap.add_argument("--json", action="store_true")
    args = ap.parse_args()

    rows = check(args.mode)
    missing_required = [r for r in rows if r["required"] and not r["present"]]
    missing_optional = [r for r in rows if not r["required"] and not r["present"]]

    if args.json:
        print(json.dumps({"mode": args.mode, "ok": not missing_required,
                          "missing_required": [r["name"] for r in missing_required],
                          "missing_optional": [r["name"] for r in missing_optional],
                          "checks": rows}, indent=2))
    else:
        print(f"Dependency check — {args.mode}\n")
        for r in rows:
            mark = "OK  " if r["present"] else ("MISS" if r["required"] else "--  ")
            tag = "required" if r["required"] else "optional"
            print(f"  {mark} {r['name']:<16} {tag:<9} {r['detail']}")
        if missing_optional:
            print("\nUnavailable, and what each costs:")
            for r in missing_optional:
                print(f"  - {r['name']}: {r['without_it']}")
        if missing_required:
            print("\nMISSING REQUIRED — do not start the pipeline:")
            for r in missing_required:
                print(f"  - {r['name']}: {r['without_it']}")
            print("\n  ffmpeg/ffprobe:  brew install ffmpeg")
            print("  .venv + deps:    python3 -m venv .venv && "
                  ".venv/bin/pip install librosa soundfile")

    if missing_required:
        sys.exit(1)


if __name__ == "__main__":
    main()

.mcp.json

tile.json