Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# supercut director — LLM provider. Copy to .env and fill in.
# Only `supercut generate` needs this; `record` and `render` work without a key.
# generate sends crawled DOM text, optional screenshots, and optional repo notes
# to the configured provider. Use trusted apps/providers only.

# ── Provider selection ──────────────────────────────────────────────────
# Optional when exactly one provider key is set. Required to disambiguate if
# multiple provider keys exist.
# SUPERCUT_PROVIDER=deepseek # deepseek | openrouter | custom

# ── DeepSeek (text-only; DOM-only analysis, no screenshot QC) ───────────
# Get a key: https://platform.deepseek.com/api_keys
DEEPSEEK_API_KEY=
# SUPERCUT_MODEL=deepseek-v4-pro # or deepseek-v4-flash
# SUPERCUT_VISION=false # DeepSeek text-only; true is rejected

# ── OpenRouter (can use vision-capable models) ──────────────────────────
# OPENROUTER_API_KEY=
# SUPERCUT_MODEL=anthropic/claude-sonnet-4.6
# SUPERCUT_VISION=true

# ── Custom OpenAI-compatible endpoint ───────────────────────────────────
# SUPERCUT_API_KEY=
# SUPERCUT_LLM_BASE_URL=https://your-compatible-endpoint.example/api/v1
# SUPERCUT_MODEL=your-model-name # required for custom endpoints
# SUPERCUT_VISION=false
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

jobs:
typecheck-and-unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run typecheck
# whole unit suite (no explicit file list, so new test files aren't
# silently skipped); the e2e files run in the browser-e2e job below,
# which has chromium + ffmpeg installed.
- run: npx vitest run --exclude '**/*.e2e.test.ts'
- run: npm audit --audit-level=moderate

browser-e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: sudo apt-get update && sudo apt-get install -y ffmpeg
- run: npm test -- --run test/record.e2e.test.ts test/generate.e2e.test.ts
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ out/
*.mp4
*.raw
test/.tmp/

# secrets — never commit
.env
.env.local

.worktrees/
.next/
examples/demo-app/node_modules/
examples/demo-app/.next/
.roast/
185 changes: 164 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,174 @@
# supercut
<p align="center">
<img src="assets/supercut-wordmark-final.png" alt="supercut — real app footage → cinematic launch video" width="620" />
</p>

> Point it at your app. Get the supercut.
<p align="center">
<strong>Point an AI director at your live app. Get a cinematic 60-second launch video.</strong>
<br />
<em>Real product footage — performed, shot, and edited automatically. No mockups, no timeline, no manual cuts.</em>
</p>

Institutional-grade, max-60-second launch videos generated from your **real**
product — not an HTML mockup. A scripted browser performs your app on camera;
a cinematic renderer adds the Screen Studio look (spring zoom-to-cursor,
motion blur, padded background, music on the beat grid); an AI director writes
the script and quality-checks the footage.
<p align="center">
<a href="#-quick-start"><img src="https://img.shields.io/badge/Quick_start-1a1a1a" alt="Quick start" /></a>
<a href="#license"><img src="https://img.shields.io/badge/License-MIT-yellow" alt="License: MIT" /></a>
<img src="https://img.shields.io/badge/node-%E2%89%A520-339933?logo=node.js&logoColor=white" alt="Node >= 20" />
<img src="https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white" alt="TypeScript" />
<img src="https://img.shields.io/badge/status-pre--release-orange" alt="Status: pre-release" />
</p>

**Status: pre-release, under active construction.** The design doc and build
plan are complete; stages are landing in order. Nothing to install yet.
<p align="center">
<img src="assets/demo-meridian.gif" alt="supercut filming a live app: it opens the console, fills in a record, and frames the resulting audit" width="760" />
<br />
<sub><em>Generated by supercut from a live web app — zero manual editing. The cursor, the camera, the cuts: all automatic.</em></sub>
</p>

---

**You built something great. Now you need a launch video — and all you've got is a screen recording, iMovie, and a deadline.**

`supercut` points an AI director at your *running* app. It reads your source, crawls the live UI, decides the 2–4 moments that actually sell the product, drives a real browser to perform them on camera, then renders the whole thing with the Screen-Studio look — spring zoom-to-cursor, motion blur, a padded background, and a clean 1080p60 export.

> Not a screen recording. Not a fake UI mockup. **Your real product**, shot like a launch film — automatically.

## ✨ What makes it different

- **Real footage only.** It drives your actual app in a real browser. Nothing is faked or re-created.
- **It understands the product.** It reads your routes/source *and* crawls the DOM, so it films the money moments — type a query → frame the result — instead of parking on the landing page.
- **It frames the payoff.** The camera holds on the *result* an action produces (the graph, the dashboard, the detail panel), not the button you clicked.
- **Works on any app.** Same pipeline for a light editorial dashboard or a dark single-page tool — copy and colors adapt per app.
- **No API key required to run it.** `record` + `render` work fully offline; only the AI director (`generate`) calls an LLM.
- **An open contract.** The recorder writes a documented event log; any recorder can feed the renderer.

## 🎬 How it works

```text
your app URL ──▶ ① analyze read the source + crawl the app → pick the money moments (LLM)
② script write the filming recipe (LLM, schema-validated, no hallucinated selectors)
③ record a deterministic browser performs it, captured frame-by-frame
④ qc deterministic + optional vision checks, bounded re-takes
⑤ render cinematic compositing ──▶ final.mp4 (≤60s, 1080p60)
```

Each stage hands off a plain-JSON artifact, so you can stop at any point, hand-edit, and resume.

## 🚀 Quick start

```bash
git clone https://github.com/Co-Messi/supercut
cd supercut
npm install
npm run build

# point it at your running app — that's it
node dist/cli/index.js generate --url http://127.0.0.1:3000 --yes
```

`generate` needs an LLM key (see [provider setup](#-llm-provider-setup)). No key? The non-AI path works standalone:

```bash
node dist/cli/index.js record --recipe examples/demo.recipe.json --out out/take
node dist/cli/index.js render --take out/take --out out/final.mp4
node dist/cli/index.js doctor # check Chromium + ffmpeg are installed
```

> Browser + video need Chromium and ffmpeg: `npx playwright install chromium` and an `ffmpeg` on your PATH.

Help the director understand a deeper, multi-page app by pointing it at the source:

```bash
node dist/cli/index.js generate --url http://127.0.0.1:3000 --repo ./ --yes
```

### Private/local apps & untrusted targets

Filming your own local dev app is the primary use case, so `generate` **allows**
localhost / RFC1918 / link-local by default — no flag needed. If you point it at an
**untrusted or public** URL, add `--block-private-network` to engage the SSRF guard
(rejects localhost, RFC1918, link-local, and cloud-metadata addresses, and validates
each redirect hop):

```bash
node dist/cli/index.js generate --url https://untrusted.example --block-private-network --yes
```

(`--allow-private-network` is a deprecated no-op kept for back-compat. Known limit: the
guard does not defend against DNS-rebinding / resolve-time TOCTOU.)

> ⚠️ **supercut drives and may MUTATE the target app** — it performs real clicks and
> typing on whatever you point it at. Destructive controls (Delete, Pay, Checkout, …)
> are excluded from filming by default; pass `--allow-destructive` to include them.
> Never run it against production data or URLs/recipes you do not trust.

## 🔌 LLM provider setup

Copy `.env.example` to `.env` (or pass `--env-file <file>`):

```bash
cp .env.example .env
```

DeepSeek is text-only here, so supercut disables screenshots and vision QC for it by default:

```env
SUPERCUT_PROVIDER=deepseek
DEEPSEEK_API_KEY=...
SUPERCUT_MODEL=deepseek-v4-pro
```

OpenRouter / custom OpenAI-compatible providers can use vision-capable models:

```env
SUPERCUT_PROVIDER=openrouter
OPENROUTER_API_KEY=...
SUPERCUT_MODEL=anthropic/claude-sonnet-4.6
SUPERCUT_VISION=true
```

For `SUPERCUT_PROVIDER=custom`, set both `SUPERCUT_LLM_BASE_URL` and `SUPERCUT_MODEL`.
If multiple provider keys are present, set `SUPERCUT_PROVIDER` explicitly — ambiguous
config fails loudly rather than guessing.

## 🔒 Privacy

`generate` may send crawled DOM text, element labels/selectors, optional screenshots,
and optional repo notes (`--repo`) to your configured LLM provider. It also writes
frames, recipes, and director reports to `out/`. Review those before sharing — and use
`record` + `render` for a fully no-LLM workflow.

## 📜 Event-log contract

The public boundary is plain JSON, so any recorder can feed the renderer:

```text
recipe.json ──▶ record ──▶ take directory
├─ events.json (the event-log contract)
├─ frames-index.json
└─ frames/*.png

take directory ──▶ render ──▶ final.mp4
```
your app URL ──▶ ① analyze pick the 3-4 money moments (LLM)
② script write the filming recipe, beat-aligned (LLM, schema-validated)
③ record a deterministic browser executor performs it (pure code)
④ qc vision checks the footage, refilms what's bad (bounded loop)
⑤ render cinematic compositing + music ──▶ launch.mp4 (≤60s, 1080p60)

Schemas reject unsupported URL schemes, malformed events, non-monotonic timelines,
oversized logs, and impossible camera boxes.

## Project principles

- Real product footage beats mockups.
- The event log is a public contract.
- The non-AI `record` / `render` paths stay useful without an API key.
- Defaults fail loudly on unsafe or ambiguous config.

## Contributing

```bash
npm run typecheck
npm run test:fast
npm run test:e2e # needs Chromium + ffmpeg
npm audit --audit-level=moderate
```

- Real footage only — no fake UI renders, ever
- The event-log JSON between recorder and renderer is a public contract; any
recorder can feed it
- Stages ③ and ⑤ run standalone with zero API key (`supercut record`,
`supercut render`)
- MIT, CC0-only bundled music, macOS + Linux
Keep PRs focused and add tests for behavior changes.

## License

MIT
[MIT](LICENSE)
Binary file added assets/demo-meridian.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/supercut-wordmark-final.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions examples/pandora-demo.recipe.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"version": 0,
"app_url": "http://127.0.0.1:8455",
"music_track": "institutional-01",
"scenes": [
{
"name": "trace-one-company",
"priority": 1,
"entry": { "url": "http://127.0.0.1:8455/", "prelude": [] },
"depends_on": [],
"actions": [
{ "kind": "click", "selector": "#ticker", "duration_ms": 1000 },
{ "kind": "type", "selector": "#ticker", "text": "NVDA", "submit": true, "focus_selector": "#graph", "duration_ms": 1600 }
],
"hold_ms": 2600
},
{
"name": "expose-the-illusion",
"priority": 2,
"entry": { "url": "http://127.0.0.1:8455/", "prelude": [] },
"depends_on": [],
"actions": [
{ "kind": "click", "selector": "#ticker", "duration_ms": 1000 },
{ "kind": "type", "selector": "#ticker", "text": "SEMICONDUCTOR", "submit": true, "focus_selector": "#graph", "duration_ms": 2400 }
],
"hold_ms": 3000
}
]
}
Loading
Loading