Entry Points & Demo Data¶
Four executable scripts at the repo root are the everyday way in. Each checks
its own prerequisites, installs what is missing, and then launches — so none of
them assume a previous step was run. Shared shell helpers live in
scripts/_common.sh.
| Command | Gets you | Needs API keys? | |
|---|---|---|---|
| Look around | ./demo |
A populated dashboard, instantly and offline | No |
| Start for real | ./init |
A clean, empty install ready to scrape your own niche | Gemini key (promptable, skippable) |
| Read the docs | ./docsite |
This site, built and served with live reload | No |
| Check it works | ./health |
Every test suite, build and repo invariant, with a non-zero exit on failure | No |
| Stop it | ./stop |
Every process this checkout started, shut down | No |
| Start over | ./clean |
Data archived to a zip, then wiped back to a fresh clone | No |
./demo — a populated dashboard, offline¶
./demo # unpack + load the sample dataset, set up if needed, launch
./demo --keep # launch without overwriting data already on disk
./demo --port 9000 # prefer a specific port
The realistic dataset is not in this repo — it ships separately as
demodataset.zip. Put that zip in the repo root and ./demo unpacks it into
demo-data/data/, copies it over the working tree, and starts the hub, so the
Corpus grid, the Studio gate, the rendered reels, the evals and the activity
log all have content. No API keys, no scraping, no model calls — nothing
here reaches the network.
If no dataset and no demodataset.zip are present, ./demo stops and prints
both ways forward: drop in the zip, or run ./init and add your own handles.
It overwrites generated data
./demo replaces the corpus, studio, renders, evals and logs on disk. It
never touches source code or your .env files. Use --keep if you have
real work in the working tree, or ./init for a clean empty start.
./init — a clean, empty install¶
./init # check, install, configure, launch
./init --no-launch # set everything up but don't start the hub
./init --reset # clear stored API keys first (your data is kept — see ./clean)
./init --port 9000 # pin this checkout to a specific port
The first-run path from a clean clone. It verifies uv, Python, Node, npm and
curl (and warns if ffmpeg is absent, since SimilarContent stitches reels
with it), syncs every Python project, builds the Dashboard, then handles your
GEMINI_API_KEY. If a key is already present — in the environment or in a
previous run's AnalysisEngine/.env / SimilarContent/.env — it is reused
without re-prompting and re-verified against Google; otherwise it prompts,
verifies the key, and writes it to both .env files (telling you where, so you
can remove it by hand). Skipping the prompt is fine — it tells you exactly where
to add the key later.
You land on an empty dashboard. That is the point: no corpus, no proposals,
no renders, ready for your own handles in
ReelScraper/platforms/instagram/pages.txt.
It also pins the port this checkout owns — 8787 for the first clone on the
machine, 8788 for the next — into ReelScraper/.env as HUB_PORT, and writes
that address into every component's BACKEND_API. A hub already answering is
only reused if it belongs to this checkout and is running the code on disk; a
stale one is restarted and another checkout's is left alone. See
Niches → Running two niches at once.
--reset clears keys, not data
It removes the stored API keys (AnalysisEngine/.env, SimilarContent/.env,
AutoSearch/.env, ReelScraper/.env, platforms/x/session.txt) and leaves
every scraped reel where it is — re-running setup after rotating a key should
not cost you a scrape. To wipe the data too, use ./clean,
which archives it to a zip first. Keys are not recoverable either way.
./docsite — this site¶
./docsite # build, then serve with live reload and open a browser
./docsite --build # build only, into documentation/site
./docsite --port 9000 # prefer a specific port
mkdocs is not a standalone install here — it lives in ReelScraper's dev
dependency group, so everything runs through
uv run --project ReelScraper. There is nothing to install first.
Building also lights up the hub's own /documentation route: the hub mounts
documentation/site when that directory exists, but only checks at startup, so
restart the hub after a first build.
Ports¶
All three prefer a port (8787 for the hub, 8000 for the docs) and fall back
to a free one when it is busy, printing the port they actually got. Nothing
in the system hardcodes 8787 — the hub exports BACKEND_API so every agent it
spawns inherits the real address. See
CLI Reference → Ports.
The hub's port is additionally pinned per checkout in ReelScraper/.env
(HUB_PORT), so a second clone settles on 8788 and stays there instead of
taking a different random port on every restart. A pinned port that another
checkout has taken is re-claimed rather than handed over.
demo-data/ and demodataset.zip¶
This is the clean, shareable build: demo-data/data/ is empty. The ~47 MB
sample dataset ships separately as demodataset.zip, because it is real
scraped content and is handed out privately rather than committed.
demo-data/
README.md how to get and use demodataset.zip
data/ empty here; ./demo unpacks demodataset.zip into it
With demodataset.zip in the repo root, ./demo unpacks it and loads a scored
corpus, Gemini blueprints, clone recipes at the human gate, rendered reels that
play, evals and an activity log. Its data/ mirrors the repo layout exactly, so
loading it is a plain recursive copy into place. Nothing here is required to
run the project — ./init starts clean without any dataset.
What was stripped when the snapshot was captured: every signed Instagram CDN
URL (they carry auth parameters and expire within hours), every .env and
session.txt, the agent memory databases, the raw scrape dump, the xlsx/CSV
exports, and the bulk of media/ — only posters plus the clips that have
blueprints are kept.
demodataset.zip is real scraped data — keep it private
It carries real creator handles, real captions, real engagement metrics, and
AI-generated reels derived from specific third-party videos. That is why it
is never committed and is shared only with people the owner chooses.
Publishing it would republish other people's content and personal data
without consent. demo-data/README.md has the details.
See also¶
- Quickstart & Usage — the guided first run.
- CLI Reference — every flag on every command, root and per-agent.
- Architecture — how the hub, agents and Dashboard relate.
./health — one command before you commit¶
./health # test suites + typecheck + lint + builds + docs + invariants
./health --quick # test suites + typecheck + lint — fast, skips builds, docs and the live smoke
./health --live # also boots the hub and exercises the real HTTP surface
./health --fix # run the formatters that have a --write mode, then re-check
It exits non-zero when anything fails and prints the tail of each failing command's output, so it works unchanged as a pre-commit hook or a CI gate.
What it runs
| Group | Checks |
|---|---|
| Prerequisites | uv, python3, node ≥ 20, npm, curl, git; ffmpeg reported as optional |
| Python | pytest for ReelScraper, AnalysisEngine, AutoSearch, SimilarContent |
| Dashboard | tsc --noEmit, vitest run, eslint, and the production build |
| Documentation | mkdocs build |
| Invariants | see below |
Live (--live) |
boots the hub, checks the key endpoints, that a render streams with HTTP range support, and that CORS still rejects a foreign origin |
Why invariants, not just tests
Several things that have genuinely broken in this repo were invisible to unit tests, because they are properties of the repository rather than of any function:
- a generated reel written into
ReelScraper/media/, the scraped-corpus namespace — which once made the Corpus serve our own output under real creators' IDs; - an unanchored
.gitignorerule matching insidedemo-data/and silently dropping most of the sample dataset from a commit; - a
.envreappearing on disk or entering git history; - the hub growing a
0.0.0.0bind.
./health asserts each of those directly. If you add a similar structural guarantee,
add the assertion here too — that is what the section is for.
./stop — shut this checkout down¶
./stop # stop the hub and any stage jobs it started
./stop --list # show what would be stopped, stop nothing
Processes are matched by working directory, not by command line. The hub runs as
uvicorn api.app:app with cwd <repo>/ReelScraper and the repo path never appears in its
arguments — so a name match would either miss it or, worse, kill an identically-named
process belonging to a different clone. Several checkouts of this repo on one machine is
normal, and a hub from the wrong one answering on :8787 is a genuinely confusing way to
lose an afternoon, so ./stop also tells you when the port is held by a checkout that is
not this one, without touching it.
Nothing on disk changes.
./clean — back to a fresh clone¶
./clean # archive, confirm, then delete data AND keys
./clean --keep-keys # wipe data only
./clean --list # show what would go, do nothing
./clean --yes # skip the prompt (the archive is still written first)
The order matters, and it is the whole point of the script:
- Stop everything.
- Archive every generated path to
backups/cuttingroom-data-<timestamp>.zip. - Verify the archive — a corrupt or empty one aborts with nothing deleted.
- Print where it is, then ask.
- Delete.
Restore any time with unzip -o backups/<archive>.zip -d .
API keys are deleted and are NOT in the archive
Live credentials do not belong in a zip file somebody will forget about, so they are
removed without a copy. Re-enter them with ./init, or keep them with --keep-keys.
For the credentials alone — keys cleared, scraped data kept — use ./init --reset.
Two things it will never do: remove a file git tracks, and leave agent-written memory
behind. memory/*/patterns.md ships as curated scaffolding but accumulates observations
about real analyzed clips during a run, so it is restored to the shipped version rather
than deleted or kept.