reel-studio 0.2.0 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/make-video/SKILL.md +2 -1
- package/.claude/skills/review-desk/SKILL.md +52 -0
- package/.claude/skills/scene-3d/SKILL.md +5 -0
- package/README.md +38 -4
- package/bin/reel-studio.mjs +16 -2
- package/core/lib/runtime.js +11 -1
- package/core/package-lock.json +9 -9
- package/core/package.json +2 -2
- package/core/render.mjs +4 -2
- package/desk/README.md +47 -0
- package/desk/app.js +332 -0
- package/desk/desk.css +120 -0
- package/desk/index.html +102 -0
- package/desk/lib.mjs +190 -0
- package/desk/mcp.mjs +99 -0
- package/desk/server.mjs +222 -0
- package/package.json +9 -3
- package/scripts/init-project.sh +2 -1
- package/templates/project/CLAUDE.md +2 -0
- package/templates/project/gitignore +3 -0
|
@@ -18,7 +18,8 @@ Fill sections 1–4 of the production README: the problem, the benefit, 3 hook v
|
|
|
18
18
|
visual, **engine**, on-screen text, sound), caption. Pick engines with the `pick-engine` skill and say why.
|
|
19
19
|
State size, length, placement and sound (VO / music / silent).
|
|
20
20
|
|
|
21
|
-
**Stop and ask for approval
|
|
21
|
+
**Stop and ask for approval** in chat. No plates, audio or composition before the box is ticked. (If the person uses
|
|
22
|
+
the optional desk, they may approve there instead: `review-desk` skill. Never tick the box on your own.)
|
|
22
23
|
|
|
23
24
|
## 2. Produce
|
|
24
25
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-desk
|
|
3
|
+
description: Optional. Open the Reel Studio desk (a local review app) and work from its review notes - notes pinned to frames, renders, approvals. Use only when the person asks for the desk or already uses it - "open the desk", "apply my desk notes", "fix what I marked", "what's on this frame" - or a production has a review.json. Never required: reviewing and approving in chat works the same.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Review desk (optional)
|
|
7
|
+
|
|
8
|
+
The kit works the same without it: storyboards are approved in chat, and QA is the `make-video` steps. Don't start
|
|
9
|
+
the desk or suggest it unless the person asks, or they already use it (a `review.json` in the production).
|
|
10
|
+
|
|
11
|
+
The desk (`desk/`) is a local page for the person: it plays a production's composition frame-exact through the
|
|
12
|
+
renderer's own clock (`__seek`), and lets them pin notes to frames, render stills, preview videos and QA, read the
|
|
13
|
+
storyboard, approve, and send notes to Claude. It only reads and writes files in the project, so everything it does
|
|
14
|
+
reaches you as files (and, if it is registered, through the `reel-desk` MCP server).
|
|
15
|
+
|
|
16
|
+
## Open it
|
|
17
|
+
|
|
18
|
+
`node desk/server.mjs` (run it in the background), then the person opens <http://127.0.0.1:4100>. In the Claude
|
|
19
|
+
desktop app, open that URL in the browser pane. `npx reel-studio desk` does the same. Port: `--port <n>`.
|
|
20
|
+
|
|
21
|
+
The `reel-desk` MCP tools below are not registered by default. If they aren't available and the person wants them:
|
|
22
|
+
`claude mcp add reel-desk --scope project -- node desk/mcp.mjs` (then restart the session). Without them, read and
|
|
23
|
+
write `review.json` directly.
|
|
24
|
+
|
|
25
|
+
## Where things are
|
|
26
|
+
|
|
27
|
+
| | |
|
|
28
|
+
|---|---|
|
|
29
|
+
| Notes and approvals | `productions/<brand>/<ID>/review.json` (tracked in git: it is the review record) |
|
|
30
|
+
| What the person has open | `desk/.state.json`, or the `desk_state` tool |
|
|
31
|
+
| Desk renders | `productions/<brand>/<ID>/qa/desk/` (stills, `preview.mp4`, `qa/`); never the delivered export |
|
|
32
|
+
|
|
33
|
+
## Working from notes
|
|
34
|
+
|
|
35
|
+
1. `desk_state` tells you which production and time "this frame" means. `list_notes` gives the open notes
|
|
36
|
+
(`id`, `t` in seconds, `text`). Without the MCP server, read `review.json`.
|
|
37
|
+
2. For each note: change the composition, check it with `render_frame` at the note's `t` (or
|
|
38
|
+
`node core/render.mjs still … --t <t>`) and **look at the image**.
|
|
39
|
+
3. `resolve_note` with a one-line reply saying what changed. If a note needs a fact, an asset or a decision you
|
|
40
|
+
don't have, keep it `open` and put the question in the reply. Only the person closes notes (after checking).
|
|
41
|
+
4. Your own QA findings can go on the timeline too: `add_note` (they show as by `claude`).
|
|
42
|
+
|
|
43
|
+
The desk's **Send to Claude** button runs exactly this loop headlessly (`claude -p` with the person's own Claude Code
|
|
44
|
+
login, `ANTHROPIC_API_KEY` removed, tools limited to read/edit/render/QA and `reel-desk`), one session per production
|
|
45
|
+
(`review.json` → `claude.session`).
|
|
46
|
+
|
|
47
|
+
## Approvals are the person's
|
|
48
|
+
|
|
49
|
+
The desk's **Approve storyboard** ticks `- [x] Storyboard approved` in the production README and fills
|
|
50
|
+
*Approved by / date*; **Approve final cut** adds a final-cut line. Both are logged in `review.json` → `approvals`.
|
|
51
|
+
**Never write an approval yourself**, in the README or in `review.json`, and never treat a fixed note as approval.
|
|
52
|
+
When a storyboard is ready, say "it's in the desk for approval" and stop (golden rule 2).
|
|
@@ -15,6 +15,11 @@ description: Build a 3D shot for a video - live in the composition with Three.js
|
|
|
15
15
|
to the import map). Wrap the load in `Studio.wait(...)`. Put model files in the brand's `assets/models/` or the production's `composition/`.
|
|
16
16
|
- Cartoon look: `toon(color, { steps })` + `outline(mesh, { thickness })`. Hard light: one `DirectionalLight` + low `AmbientLight`.
|
|
17
17
|
- Animated GLB clips: create an `AnimationMixer`, then in `update` call `mixer.setTime(t - start)`. Never `mixer.update(delta)`.
|
|
18
|
+
- HTML tags that follow 3D points: in `update(t)`, call `camera.updateMatrixWorld()` and the parent's
|
|
19
|
+
`updateMatrixWorld()` before `vector.applyMatrix4(obj.matrixWorld).project(camera)`. Three.js refreshes these only
|
|
20
|
+
inside `render()`, so without it a tag follows the previous frame. Example: `productions/bike-logger/REEL-002-engines`.
|
|
21
|
+
- CAD meshes as JSON (vertices + indices): `toNonIndexed()` + `computeVertexNormals()` gives crisp flat facets.
|
|
22
|
+
Render with `threeLayer(..., { pixelRatio: 2 })` for supersampled edges.
|
|
18
23
|
- Physics: bake it (simulate at a fixed 1/120 s step into an array before the first frame) and look up by `t`.
|
|
19
24
|
|
|
20
25
|
## Blender (plate)
|
package/README.md
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
<h1 align="center">🎬 Reel Studio</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
+
<a href="https://github.com/murathany7/reel-studio/actions/workflows/ci.yml"><img src="https://github.com/murathany7/reel-studio/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
4
5
|
<a href="https://www.npmjs.com/package/reel-studio"><img src="https://img.shields.io/npm/v/reel-studio?color=e8a33d" alt="npm"></a>
|
|
6
|
+
<a href="https://pypi.org/project/reel-studio/"><img src="https://img.shields.io/pypi/v/reel-studio?color=e8a33d" alt="PyPI"></a>
|
|
7
|
+
<a href="https://github.com/murathany7/homebrew-reel-studio"><img src="https://img.shields.io/badge/homebrew-tap-e8a33d" alt="Homebrew"></a>
|
|
5
8
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-3ecf7a" alt="MIT"></a>
|
|
6
9
|
<img src="https://img.shields.io/badge/Claude%20Code-plugin-8b93a1" alt="Claude Code plugin">
|
|
7
10
|
</p>
|
|
8
11
|
|
|
9
|
-
<p align="center"><b>
|
|
10
|
-
|
|
12
|
+
<p align="center"><b>Agency-quality reels, ads and posts, made by Claude Code.</b><br>
|
|
13
|
+
Open source and free. Your Claude subscription does the rest.<br>
|
|
14
|
+
No editor, no timeline, no per-video fees. Every frame is code you own.</p>
|
|
15
|
+
|
|
16
|
+
<p align="center"><sub>Plain HTML pages on one clock → headless Chrome → MP4, for any brand. Every frame is reproducible.<br>
|
|
17
|
+
AI voiceover (ElevenLabs) is optional and billed by its provider.</sub></p>
|
|
11
18
|
|
|
12
19
|
<p align="center">
|
|
13
20
|
<img src="docs/media/bike-logger-reel.gif" width="240" alt="Bike Logger reel">
|
|
@@ -81,6 +88,20 @@ It copies the renderer into the folder, installs it and checks Chrome and ffmpeg
|
|
|
81
88
|
npx reel-studio init
|
|
82
89
|
```
|
|
83
90
|
|
|
91
|
+
**Or with Python or Homebrew**, the same kit and the same `init` (the renderer still runs on Node):
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pipx install reel-studio && reel-studio init # or: uvx reel-studio init
|
|
95
|
+
brew install murathany7/reel-studio/reel-studio && reel-studio init # brings Node and ffmpeg
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Or with Docker**, with Node, Chromium, ffmpeg and fonts inside, nothing to install (Linux renders use software WebGL):
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
docker run --rm -it -v "$PWD":/work ghcr.io/murathany7/reel-studio reel-studio init
|
|
102
|
+
docker run --rm -v "$PWD":/work ghcr.io/murathany7/reel-studio node core/render.mjs video <page>.html out.mp4
|
|
103
|
+
```
|
|
104
|
+
|
|
84
105
|
**Or clone it** and work inside the repo:
|
|
85
106
|
|
|
86
107
|
```bash
|
|
@@ -93,6 +114,14 @@ cd core && npm install && cd ..
|
|
|
93
114
|
> *Add my product as a new brand: https://my-product.com* → facts, colours, fonts, voice, rules
|
|
94
115
|
> *Make a 15 s reel for <brand> about <feature>* → brief and storyboard, then the video once you approve
|
|
95
116
|
|
|
117
|
+
That's the whole product: you talk to Claude Code, it writes the storyboard, waits for your OK in chat, then makes,
|
|
118
|
+
renders and checks the video.
|
|
119
|
+
|
|
120
|
+
**Optional: the desk.** If you'd rather review on a page than in chat, run `npx reel-studio desk` (or
|
|
121
|
+
`node desk/server.mjs`) and open <http://127.0.0.1:4100>. You can scrub frame by frame, pin notes to frames, send them
|
|
122
|
+
to Claude Code (your own login, no API key), render stills and previews, and approve the storyboard and the final cut.
|
|
123
|
+
Nothing else needs it, and nothing changes if you never open it. See [`desk/README.md`](desk/README.md).
|
|
124
|
+
|
|
96
125
|
## What's inside
|
|
97
126
|
|
|
98
127
|
```
|
|
@@ -101,12 +130,17 @@ engines/ one adapter per engine
|
|
|
101
130
|
brands/ one folder per brand (_template to start)
|
|
102
131
|
productions/ one folder per piece (brief, composition, audio, QA, export)
|
|
103
132
|
scripts/ audio mix, transcription, QA, frame tools
|
|
104
|
-
|
|
133
|
+
desk/ optional local review app (and its optional reel-desk MCP server)
|
|
134
|
+
.claude/skills setup · make-video · new-brand · pick-engine · scene-3d · character-2d · capture-site · review-desk (optional)
|
|
105
135
|
```
|
|
106
136
|
|
|
107
137
|
## Contributing
|
|
108
138
|
|
|
109
|
-
Open source and early. New engine adapters,
|
|
139
|
+
Open source and early. New engine adapters, skill fixes and QA checks are all welcome: see
|
|
140
|
+
[CONTRIBUTING.md](CONTRIBUTING.md). `npm test` re-renders the demo twice and checks every frame matches.
|
|
141
|
+
|
|
142
|
+
**Made something?** [Show your reel](https://github.com/murathany7/reel-studio/issues/new?template=show-your-reel.yml):
|
|
143
|
+
good examples get featured here. Questions go to [Discussions](https://github.com/murathany7/reel-studio/discussions).
|
|
110
144
|
|
|
111
145
|
## License
|
|
112
146
|
|
package/bin/reel-studio.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// npx reel-studio init [dir] [--update]: copy the Reel Studio kit and its Claude Code skills into a project.
|
|
3
|
+
// npx reel-studio desk [--port 4100]: open the review desk of the project in this folder.
|
|
3
4
|
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
4
6
|
import path from "node:path";
|
|
5
7
|
import { fileURLToPath } from "node:url";
|
|
6
8
|
|
|
@@ -17,15 +19,27 @@ if (cmd === "init") {
|
|
|
17
19
|
console.log(`
|
|
18
20
|
Done. Open this folder in Claude Code and ask:
|
|
19
21
|
"Add my product as a new brand: https://…"
|
|
20
|
-
"Make a 15 s reel for <brand> about <feature>"
|
|
22
|
+
"Make a 15 s reel for <brand> about <feature>"
|
|
23
|
+
Optional, to review on a page instead of in chat: npx reel-studio desk`);
|
|
21
24
|
}
|
|
22
25
|
process.exit(r.status ?? 1);
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
if (cmd === "desk") {
|
|
29
|
+
// The project's own copy, so the desk serves the project (its brands, productions and kit version)
|
|
30
|
+
const server = path.resolve("desk/server.mjs");
|
|
31
|
+
if (!existsSync(server)) {
|
|
32
|
+
console.error("No desk/ here. Run `npx reel-studio init` in this folder first (or `--update` an older project).");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
process.exit(spawnSync(process.execPath, [server, ...rest], { stdio: "inherit" }).status ?? 1);
|
|
36
|
+
}
|
|
37
|
+
|
|
25
38
|
console.log(`Reel Studio: marketing videos made with code, run by Claude Code.
|
|
26
39
|
|
|
27
40
|
npx reel-studio init [dir] copy the kit and skills into dir (default: here) and install it
|
|
28
|
-
npx reel-studio init [dir] --update refresh core/, engines/, scripts/ and skills from this version
|
|
41
|
+
npx reel-studio init [dir] --update refresh core/, engines/, scripts/, desk/ and skills from this version
|
|
42
|
+
npx reel-studio desk [--port 4100] optional review page: play, pin notes, send them to Claude, approve
|
|
29
43
|
|
|
30
44
|
Claude Code plugin instead: /plugin marketplace add murathany7/reel-studio
|
|
31
45
|
More: https://github.com/murathany7/reel-studio`);
|
package/core/lib/runtime.js
CHANGED
|
@@ -86,7 +86,17 @@
|
|
|
86
86
|
this.onSeek((t) => new Promise((res) => {
|
|
87
87
|
const target = Math.max(0, Math.min(t - start, (el.duration || 0) - 0.001));
|
|
88
88
|
if (Math.abs(el.currentTime - target) < 0.0005) return res();
|
|
89
|
-
|
|
89
|
+
// "seeked" can fire before the new frame is painted, and a screenshot then catches the old one (seen on a
|
|
90
|
+
// cold CI runner): also wait for the frame to be presented. A seek within the same plate frame presents
|
|
91
|
+
// nothing new, so that wait gives up after a moment; the old frame is the right one then.
|
|
92
|
+
let seeked = false, shown = !el.requestVideoFrameCallback, timer;
|
|
93
|
+
const done = () => seeked && shown && (clearTimeout(timer), res());
|
|
94
|
+
if (!shown) el.requestVideoFrameCallback(() => { shown = true; done(); });
|
|
95
|
+
el.addEventListener("seeked", () => {
|
|
96
|
+
seeked = true;
|
|
97
|
+
timer = setTimeout(() => { shown = true; done(); }, 250);
|
|
98
|
+
done();
|
|
99
|
+
}, { once: true });
|
|
90
100
|
el.currentTime = target;
|
|
91
101
|
}));
|
|
92
102
|
return el;
|
package/core/package-lock.json
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"gsap": "^3.12.5",
|
|
10
10
|
"lottie-web": "^5.12.2",
|
|
11
11
|
"pixi.js": "^8.6.0",
|
|
12
|
-
"playwright-core": "1.
|
|
13
|
-
"three": "^0.
|
|
12
|
+
"playwright-core": "1.64.1",
|
|
13
|
+
"three": "^0.187.0"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"node_modules/@pixi/colord": {
|
|
@@ -118,21 +118,21 @@
|
|
|
118
118
|
}
|
|
119
119
|
},
|
|
120
120
|
"node_modules/playwright-core": {
|
|
121
|
-
"version": "1.
|
|
122
|
-
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.
|
|
123
|
-
"integrity": "sha512-
|
|
121
|
+
"version": "1.64.1",
|
|
122
|
+
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.1.tgz",
|
|
123
|
+
"integrity": "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==",
|
|
124
124
|
"license": "Apache-2.0",
|
|
125
125
|
"bin": {
|
|
126
126
|
"playwright-core": "cli.js"
|
|
127
127
|
},
|
|
128
128
|
"engines": {
|
|
129
|
-
"node": ">=
|
|
129
|
+
"node": ">=20"
|
|
130
130
|
}
|
|
131
131
|
},
|
|
132
132
|
"node_modules/three": {
|
|
133
|
-
"version": "0.
|
|
134
|
-
"resolved": "https://registry.npmjs.org/three/-/three-0.
|
|
135
|
-
"integrity": "sha512-
|
|
133
|
+
"version": "0.187.0",
|
|
134
|
+
"resolved": "https://registry.npmjs.org/three/-/three-0.187.0.tgz",
|
|
135
|
+
"integrity": "sha512-Qiu7R+Cmh8CstZsY1uJOSWnVlfo7ZtNLV7mF8Jltk3LB3AtDu9cRZlcnRKkAk8Y4z6KKw35z7Y7Urd7jWY8etA==",
|
|
136
136
|
"license": "MIT"
|
|
137
137
|
},
|
|
138
138
|
"node_modules/tiny-lru": {
|
package/core/package.json
CHANGED
package/core/render.mjs
CHANGED
|
@@ -78,8 +78,10 @@ function serve() {
|
|
|
78
78
|
// WebGL (Three.js, Pixi) on the GPU, but compositing in software: with GPU compositing Chrome
|
|
79
79
|
// draws <video> plates ~10 levels brighter than ffmpeg decodes them (measured 2026-11-02).
|
|
80
80
|
// playwright-core is pinned (1.49.1) because 1.64 shifted plate colours the same way.
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
// WebGL: Metal on macOS; elsewhere (Linux, CI without a GPU) Chrome's software renderer, SwiftShader
|
|
82
|
+
const CHROME_ARGS = ["--force-color-profile=srgb", "--hide-scrollbars", "--enable-gpu", "--ignore-gpu-blocklist",
|
|
83
|
+
"--disable-gpu-compositing",
|
|
84
|
+
...(process.platform === "darwin" ? ["--use-angle=metal"] : ["--use-angle=swiftshader", "--enable-unsafe-swiftshader"])];
|
|
83
85
|
|
|
84
86
|
async function openPage(input, safe) {
|
|
85
87
|
const server = await serve();
|
package/desk/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Desk (optional)
|
|
2
|
+
|
|
3
|
+
A local review app for productions, **for people who'd rather review on a page than in chat**. Nothing in the kit
|
|
4
|
+
needs it: the skills are the product, and Claude Code writes, renders and checks the pieces either way. If you never
|
|
5
|
+
open the desk, nothing changes. It adds no dependencies and doesn't register itself anywhere.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
node desk/server.mjs # or: npx reel-studio desk → http://127.0.0.1:4100
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| Panel | What it does |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Player | Loads `composition/index.html?render=1` and drives it with `__ready`, `__meta`, `__seek`, the same calls as `core/render.mjs`. Space play/pause, ←/→ one frame (⇧ one second), S safe zones, N new note |
|
|
14
|
+
| Notes | Pin a note to the current frame. Dots on the scrubber. Claude marks notes *fixed* with a reply; you close them after checking |
|
|
15
|
+
| Claude | Sends the open notes and/or a request to a headless Claude Code run and streams what it does. The composition reloads when it's done |
|
|
16
|
+
| Render | Still at this frame, preview video, QA (`scripts/qa-video.sh`). Output goes to `qa/desk/`, never `export/` |
|
|
17
|
+
| Approve | Storyboard (ticks the README box) and final cut. People only: Claude and the MCP server can't approve |
|
|
18
|
+
| Storyboard | The production README |
|
|
19
|
+
|
|
20
|
+
## How Claude is involved, and what it costs
|
|
21
|
+
|
|
22
|
+
There are three ways, and none needs an API key.
|
|
23
|
+
|
|
24
|
+
1. **Files.** Notes and approvals live in `productions/<brand>/<ID>/review.json`. In any Claude Code session say
|
|
25
|
+
"apply my desk notes" (the `review-desk` skill).
|
|
26
|
+
2. **Send to Claude** runs `claude -p` (the Claude Code CLI on this machine, signed in as you) from the project root.
|
|
27
|
+
`ANTHROPIC_API_KEY` is removed from its environment, so it always uses your Claude Code login. Its tools are limited
|
|
28
|
+
to Read/Edit/Write/Glob/Grep, `node core/render.mjs`, `scripts/qa-video.sh`, `scripts/extract-frames.sh` and the
|
|
29
|
+
`reel-desk` tools. It runs one session per production (`--resume`). Set `CLAUDE_BIN` to use another `claude`.
|
|
30
|
+
3. **`reel-desk` MCP server** (`desk/mcp.mjs`), optional on top of the optional desk, and not registered by default.
|
|
31
|
+
Add it with `claude mcp add reel-desk --scope project -- node desk/mcp.mjs`. It lets a Claude Code session see the desk:
|
|
32
|
+
`desk_state`, `list_productions`, `list_notes`, `add_note`, `resolve_note`, `render_frame` (returns the image).
|
|
33
|
+
It has no approval tool.
|
|
34
|
+
|
|
35
|
+
## Safety
|
|
36
|
+
|
|
37
|
+
The server listens on 127.0.0.1 only, serves files from the project root only (not `.git` or `.env*`), and refuses
|
|
38
|
+
API calls from other origins, so another site open in your browser can't drive it. It has no dependencies.
|
|
39
|
+
|
|
40
|
+
## Files
|
|
41
|
+
|
|
42
|
+
| | |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `server.mjs` | HTTP server: static files with range requests (for plates) + the JSON API + job streams (SSE) |
|
|
45
|
+
| `lib.mjs` | Productions, notes, approvals, render commands, the Claude prompt. Shared by the server and the MCP server |
|
|
46
|
+
| `mcp.mjs` | The `reel-desk` MCP server (stdio JSON-RPC) |
|
|
47
|
+
| `index.html`, `app.js`, `desk.css` | The page. No build step |
|