# FableCut — browser video editor, drivable by Claude Code A production-style non-linear video editor (Premiere-style) that runs in the browser. An AI agent edits videos by **editing `project.json`** (or calling the REST API / MCP tools) — the open browser UI live-reloads within ~150 ms via SSE. No build step, no npm dependencies. **This file is the master manual.** Any model pointed at this document (or at the `fablecut_docs` MCP tool, which returns it) has everything needed to fully drive the editor. ## MCP connection (preferred — works from any session, any directory) Register the MCP server (`mcp-server.js`) once at user scope as `fablecut`: `claude mcp add -s user fablecut -- node "/fablecut/mcp-server.js"`. Every Claude Code session then has these tools: - `fablecut_status` — auto-starts the editor server, returns URL + project summary. Call first. - `fablecut_docs` — returns this document (`section: "…"` returns only matching `## ` sections). - `fablecut_get_project` / `fablecut_set_project` — read / replace the timeline JSON. `fablecut_get_project {compact:true}` returns a one-line-per-clip summary instead. - `fablecut_patch_project` — apply targeted ops (add/update/remove clip/media, set project fields) without round-tripping the document. **Prefer this for edits.** - `fablecut_import_media` — copy a local file (or download an `https://` URL) into `./media/` and register it. The stored `src` is always `/media/…`. - `fablecut_analyze_reference` — turn a reference video into an edit blueprint (shots, beats, BPM, energy, drop) + extract its music. See "Remake a reference video". - `fablecut_encode_profiles` — list export presets from `encoding-profiles.json` (each is a raw ffmpeg args list). Set `project.encodeProfile` via patch to pin a project default. ### Token-efficient editing (important for agents) Editing via full get→modify→set costs thousands of tokens per change. Cheaper: 1. **Plan** from `fablecut_get_project {compact:true}` (≈10× smaller than the JSON) and `fablecut_status` — fetch the full JSON only to inspect exact keyframes. 2. **Edit** with `fablecut_patch_project` ops — send only what changes, e.g. `{ops:[{op:"updateClip", id:"c_v2", set:{props:{filterPreset:"noir"}}}]}`. It re-reads the latest document internally, so it is merge-safe by design (no CONFLICT dance) and never destroys concurrent UI tweaks. 3. **Docs**: request `fablecut_docs {section:"schema"}` (or "Recipes", "Remake", …) instead of the whole manual; skip it entirely if the schema is already in context. 4. **Media questions** (duration, fps, size): read them from the registered media entries — don't shell out to ffprobe; the browser probes and writes them back. 5. Batch related changes into ONE patch call (ops apply in order, one revision bump). **`fablecut_set_project` is conflict-checked.** The MCP server remembers the `revision` from the most recent `fablecut_get_project` call. If `project.json` has been written by anyone else since that read (e.g. the user dragged a clip in the UI), `fablecut_set_project` refuses with a "CONFLICT — not saved" error instead of overwriting. Protocol: 1. `fablecut_get_project` → read the document and note its `revision`. 2. Apply your edits in memory, bump `revision`. 3. `fablecut_set_project` → if it succeeds you're done. 4. **On conflict**: call `fablecut_get_project` again to get the latest document, re-apply your intended changes on top of it, bump `revision`, and call `fablecut_set_project` again. Pass `force: true` to `fablecut_set_project` only when the user explicitly asks to overwrite conflicting changes. `fablecut_import_media` only appends a new media entry and always merges safely — no conflict check needed. For Claude Desktop, add to its MCP config: `{"mcpServers":{"fablecut":{"command":"node","args":["/fablecut/mcp-server.js"]}}}` Direct file editing of `project.json` (below) works too and is equivalent. Installing as a Claude Code plugin (`/plugin marketplace add ronak-create/FableCut`, then `/plugin install fablecut@fablecut`) does the registration for you. ### Where the files are `project.json`, `media/`, `exports/`, `analysis/` and `library/` normally sit in the repo next to `server.js`. Set **`FABLECUT_DATA_DIR`** to move all five somewhere else; the code and the static app files stay in the install directory either way. The plugin sets this so a plugin update can replace the install directory without touching anyone's timeline or footage. **Don't assume `project.json` is beside `mcp-server.js`** — call `fablecut_status`, which reports the real paths. Tests (and nothing else) may set **`FABLECUT_NO_FS_WATCH=1`** to skip `fs.watch`. On Windows, libuv can abort the process when a file is created under a temp data dir. Production leaves watching on so the UI live-reloads. ## Run ``` node server.js # → http://localhost:7777 ``` Files: `index.html` + `style.css` + `app.js` (editor UI), `server.js` (API + hosting), `project.json` (the timeline — THE file to edit), `media/` (project footage), `library/` (default asset library, see below). ## How Claude Code edits a video 1. Ensure the server is running (background: `node server.js`, or `fablecut_status`). 2. Put source files in `./media/` (copy them in, import via the UI / **+ URL**, or `fablecut_import_media` with a local path or HTTPS URL). 3. Read `project.json`, modify `media` / `clips`, **increment `revision`**, write it back. 4. The browser UI (if open) reloads instantly. The user previews/exports from the UI. Rules: - **Prefer `fablecut_set_project`** over direct file writes — it detects conflicts automatically (see the MCP section above). If you do write `project.json` directly, read it **immediately** before writing (never write from a stale read: if the user tweaked something in the UI between your read and write, that write destroys their changes). The UI detects external changes by revision comparison, so a write that does not bump `revision` is invisible to it. - Make each edit a single atomic write (read → modify → write once), and bump `revision` (integer). Partial multi-step edits can be picked up half-finished. - New media entries may omit `duration` — the browser probes it and writes it back. If you need the duration yourself, re-read `project.json` after a second or two, or probe with ffprobe. - Don't edit `project.json` while the UI may be mid-drag — the UI defers external reloads during gestures, then picks up the next change. ## The asset library (`./library/`) — default media Reusable assets, visible in the editor's left-panel tabs and never copied: | Folder | Editor tab | Purpose | | ----------- | ------------ | ------- | | `library/sfx/` | **Sound FX** | whooshes, impacts, risers, UI clicks | | `library/elements/` | **Elements** | overlay art: alpha PNGs, light leaks, textures, stickers | | `library/svg/` | **SVG** | animated vector graphics **you author** (convention below) | | `library/fonts/` | font editor | `.ttf/.otf/.woff/.woff2`, auto-registered, family name = file name; a variable font draws every `weight` | - List via `GET /api/library?dir=sfx|elements|svg|fonts` (recursive; subfolders OK). - To use one in the timeline, add a media entry whose `src` is its library path, e.g. `{ "id":"m_x", "name":"whoosh.mp3", "kind":"audio", "src":"/library/sfx/whoosh.mp3" }` — then reference it from clips like any other media. - Dropping files into these folders live-refreshes the open UI. ## Authoring animated SVGs (the `svg` clip kind) You can create your own vector animations/overlays: write an `.svg` file into `library/svg/` (or `media/`), register it as media with `"kind": "svg"`, and place it on a video track. The compositor renders it frame-accurately, driven by the clip's local time (preview and export). **Conventions (required for time-driven animation):** 1. Root `` must carry `width` and `height` attributes (or a `viewBox`). 2. Animate with **CSS `@keyframes` inside a `