CONTRIBUTING.mdContributing to showtime
Thanks for helping. showtime is a Claude Code skill that makes videos entirely on the user's machine, on macOS (Apple Silicon and Intel), Windows 10/11 and Linux. Read CONTEXT.md for the words we use and .out-of-scope/ for what we have decided not to do.
Set up a development checkout#
git clone https://github.com/FavioVazquez/showtime
cd showtime
skills/showtime/bin/showtime setup # Windows: skills\showtime\bin\showtime.cmd setup
skills/showtime/bin/showtime doctor
To try your working copy inside Claude Code without installing the plugin, link it as a personal skill (development only; the published install is the plugin):
skills/showtime/bin/showtime setup --link # ~/.claude/skills/showtime -> this checkout
Ground rules#
- Everything runs locally. No cloud AI services, API keys, accounts, uploads or telemetry. The network is for downloads (pinned by URL, size and SHA-256 wherever the source allows) and for public web sources the user asks for (archive media search, site capture), which fetch and never send the user's files.
- Cross-platform is not optional. Python (stdlib-only for the launcher and installer) and Node only;
no shell-only logic. Use
pathlib, pass subprocess arguments as lists (never a shell string), handle.exe, and put file paths into ffmpeg filter arguments withst.ff.filter_path(). Never call a bareffmpeg: go throughst.ff(orscripts/lib/ff.mjsin Node). Fonts are files fetched by setup, never system font names. - Original work, clean licenses. Write your own code and docs. Libraries are installed by setup and pinned; assets and models must allow commercial use (CC0, public domain, OFL, MIT, Apache; CC-BY only with automatic credits).
- Friendly by default. Every command has
--helpwith examples and sensible defaults. Errors say what went wrong, why, and the exact fix (ShowtimeError(message, why=..., hint=...)); tracebacks appear only with--debug. Anything slower than 30 s announces an estimate and shows progress (st.common.estimate()andst.common.Progress). Optional extras are never installed silently: callst.common.require_extra(name, feature). - Never overwrite earlier work. Outputs go to a fresh
showtime-out/<slug>-<timestamp>/folder unless the user names a file.
Adding a command#
- Python: add
skills/showtime/lib/st/cli_<module>.pywith a literalCOMMANDS = {"name": "one line"}andregister(subparsers); the CLI discovers it. Node: addskills/showtime/scripts/<name>.mjs; the launcher routesshowtime <name>to it. - Put the command in a group in
skills/showtime/lib/st/launcher.py(GROUPS) soshowtime --helpshows it with an example. - Mention it in SKILL.md or a reference only as it really exists:
scripts/check_release.pyfails when the docs name a command or sub-command the CLI does not have.
Tests#
python skills/showtime/tests/run_all.py --fast # what CI runs on every pull request (a few minutes)
python skills/showtime/tests/run_all.py # everything (longer; renders, voice, ASR)
python skills/showtime/tests/run_all.py -k audio # one module
python skills/showtime/tests/run_all.py --fast --changed --explain # only what your changes can affect
python scripts/check_release.py --check # versions, SKILL.md, links, commands, paths, names
Each module has tests/test_<module>.py: stdlib + subprocess, real runs, short and small renders. Every
validator needs a known-bad sample that must fail next to a known-good one that passes, so a green run
proves the check still bites.
run_all.py runs several test files at once: -j N files in parallel, each in its own process with its
own working folder and TMPDIR (default auto = min(files, cores / 2, 8), and up to cores / 2 on machines
with more than 16 cores, fewer when memory is short); -j 1 runs them one after another with live output.
From 9 processes at once up, long files run in parts (tests/_split.py says which files and which tests
must share a process; tests/_part.py runs a part, and any test the plan does not know runs in the first
part). A test that leaves something for a later test (a result on the class, a file in the class's
folder) is kept with it when the scan can see it; otherwise add the pair to KEEP_TOGETHER there.
--changed [REF] runs only the test files that the changes since REF (default: the merge-base with the
integration branch, plus uncommitted and untracked files) can affect: what each test used in earlier runs
(recorded while tests run, in ~/.showtime/cache/test-impact.json), a static scan of imports, paths and
commands, and OVERRIDES in tests/_impact.py; a file it cannot place runs everything and says which.
Longest files start first (timings of the previous run, kept in ~/.showtime/cache/test-times.json); a
failing file's output is printed in full at the end. A new test
must use temp folders, free ports (port 0) and atomic writes into ~/.showtime/cache
(common.part_path + os.replace, common.cache_lock); a file that truly cannot share the machine goes
in SERIAL in run_all.py, with the reason. --shard I/N runs one of N weight-balanced parts of the
files (CI splits the fast suite into three jobs per OS this way); the split uses only the file names and
SHARD_WEIGHTS in run_all.py, so give a new heavy test file a weight there.
Example media#
The examples live in their own repository,
showtime-examples: the 22 example folders, the
launch film, examples/MEDIA.json and scripts/publish_media.py. Example renders stay small in git
there: posters, share.txt, project sources and videos up to 10 MB. Any single file over 10 MB and every
.mov (ProRes masters) is published as a release asset of that repository instead; MEDIA.json lists
them (path, bytes, sha256, asset name) and a managed block at the end of its .gitignore keeps them out
of commits. This repository keeps only the README art (assets/readme/). Run these in a clone of
showtime-examples:
python scripts/publish_media.py # verify (check_release runs this too)
python scripts/publish_media.py --refresh # after adding or re-rendering example media
python scripts/publish_media.py --links --example 20 # markdown links for that example's README
python scripts/publish_media.py --upload --dry-run # the gh release upload command
python scripts/publish_media.py --upload # upload (needs gh and the release tag)
A README links an asset as https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>, where
<asset> is the path under examples/ with -- for / (20-curtain-call-pack--pack--lower-thirds--lt-bar.mov);
--links prints them from the manifest.
Releases#
- Update CHANGELOG.md (what changed and why).
python scripts/check_release.py --set-version X.Y.Z(updatesst.__version__, both plugin manifests,setup/package.json,server.jsonandpackages/npm/package.json).python scripts/check_release.py --checkand the full test run must be clean; work through the "Before publishing" list in CHANGELOG.md.- When examples changed: in showtime-examples,
python scripts/publish_media.py --refresh, then--uploadto the release tag named inexamples/MEDIA.json(see "Example media"). - MCP packages (optional):
python scripts/build_packages.py all --out <folder outside the repo> --packbuilds the npm package (packages/npm/, named inserver.jsonfor the MCP Registry) and the.mcpbbundle (local stdio server for desktop clients and Smithery). It publishes nothing.