# re:Creator Agent-driven video production toolkit. Takes raw footage and a brief, produces an **editable CapCut project** — not a flat render. The user opens the result in CapCut and keeps editing. This file is written for an AI agent. If you are an agent reading it, you are the component that supplies judgment. Read the load-bearing rule before anything else. Home: https://recreator.ilvs.space Installer: https://recreator.ilvs.space/install.sh Remote MCP: https://mcp.recreator.ilvs.space Status: pre-release. Python packages are published on PyPI (see Install). --- ## The load-bearing rule **Skills orchestrate. Python is deterministic. The agent owns ALL judgment.** | You (the agent) do | Python does | | --- | --- | | Transcribe, diarize, detect scenes | Turn explicit instructions into a valid timeline | | Decide what counts as silence, and where to cut | Apply the exact ranges it is given | | Choose caption wording, styling, line breaks | Lay out text clips with correct offsets | | Research, select, and fetch b-roll | Insert media clips at the given positions | | Judge where a zoom helps | Emit the supplied keyframe curve | Python in this project contains **no ML, no LLM calls, no transcription, no heuristics, and no threshold guessing**. Every operation is a pure, testable `Timeline -> Timeline` transformation. Measurement is allowed — `ffprobe` for duration and dimensions, windowed loudness as a raw number series. **Interpretation is not.** There is no `detect_silence`, no `guess_threshold`, no `auto_caption`. Those names are banned by policy. **What this means for you concretely:** the tools will not decide for you. If you call `recreator.probe.loudness` you get back dB numbers, not a list of silences. Deciding which of those windows is "silence worth cutting" is your job, using the brief, the footage, and the user's taste — none of which the library can see. Then you hand the exact ranges to `recreator.timeline.remove_ranges`. Do not look for a tool that does the thinking. It does not exist, on purpose. --- ## State model `.recreator/project.json` in the current working directory is the **source of truth** — a neutral timeline IR (media sources, tracks, clips, ranges marked for removal, text clips, keyframes, transforms). Small, diffable JSON, safe to hand-edit or commit. The CapCut draft is a **build artifact**, produced only by an explicit export: ``` agent decides -> .recreator/project.json -> export -> CapCut draft folder ``` Rules that follow from this, which you must respect: - Subskills read and update `project.json`. They **never mutate a CapCut draft in place**. Only the export step touches CapCut's files, and only on request. - `project.json` must exist before any `autocut` step. If missing, run `recreator.project.init` first, seeding it by probing the input media. - Layering is safe: silence, then captions, then zoom, all onto the same timeline. Re-export is safe to repeat. - Regeneration is one-directional. The draft is rebuilt from `project.json`, never the reverse. Companion state files, all owned by the `project` namespace alone: - `.recreator/setup.json` — intent and readiness. Written/read only by `/recreator project`. `autocut` and `export` never read it. - `.recreator/toolchain.json` — this project's toolchain preset and cost tier. - `~/.recreator/toolchain.json` — machine-level install state. Stores API key variable *names* and whether they were set, never values. - `.recreator/hyperframes/run.json` — the `hyperframes` namespace's own run state. That namespace renders an MP4 and never touches `project.json`. - `.recreator/jobs/` — one JSON file per async job. --- ## HARD WARNING: `~/Movies/CapCut/` is read-only Never write into the user's CapCut library. Treat it as read-only. The single exception is the explicit, **opt-in** install path: passing `install=true` to `recreator.export.capcut` copies a built draft into the real CapCut drafts folder. That path **refuses to overwrite an existing draft of the same name without `overwrite=true`**. Leave `install` off unless the user asked for it in this conversation. Export defaults to an `out_dir` you specify and never defaults anywhere inside CapCut's own library. A tool call that could damage a user's real project is a bug, not a convenience. --- ## Skill tree Invoked as `/recreator `. Routing is two-hop: the root `SKILL.md` parses only the first token; each namespace's own `SKILL.md` parses the second. Read only the one matching file at each hop — do not preload the tree. ``` /recreator project init [] /recreator project status /recreator autocut silence /recreator autocut caption /recreator autocut profanity /recreator autocut zoom /recreator autocut reframe /recreator autocut broll /recreator hyperframes plain language — routes and builds /recreator hyperframes autonomous [] /recreator hyperframes map|script|design|scenes|build|render /recreator export capcut /recreator idea PLANNED — stub only, not implemented /recreator script PLANNED — stub only, not implemented ``` `idea` and `script` are design stubs (`version: 0.0.0-stub`). If a user invokes one, say it is not implemented before doing anything else. Do not improvise a substitute procedure under the subskill's name. `autocut reframe` has no leaf file of its own — the router sends it to `autocut/zoom.md`, which covers reframing via `recreator.timeline.set_transform`. It is a real action; do not go looking for a `reframe.md`. The seven `hyperframes` keywords (`autonomous`, `map`, `script`, `design`, `scenes`, `build`, `render`) are reserved words, not a namespace. Anything else after `hyperframes` is a free-text prompt — pass the whole remaining string through. `/recreator hyperframes a 45s launch video for acme.dev` is a prompt. ### Cross-cutting rules for every autocut leaf 1. **Always preview before committing.** Show the user a concrete, readable preview of the decision and get explicit go-ahead before the MCP write. Never chain transcription straight into a committed cut. 2. **Re-derive, do not re-append.** Re-running a leaf recomputes from the current media and current `project.json`. Silence removal shifts timestamps captions were built against — call that out and re-derive downstream artifacts. 3. **Non-ASCII text is not optional.** CJK and Vietnamese must round-trip with no mojibake. See the UTF-16 note below; naive byte counting is wrong. 4. **Validate before you finish.** Call `recreator.validate` after any batch of timeline writes, before telling the user the step is done. --- ## Install ``` curl -fsSL https://recreator.ilvs.space/install.sh | bash ``` Installs, with no sudo and idempotently: - `recreator-mcp` via `uv tool install` (a real binary on PATH). `uv` is the package manager — not pip, not poetry. The installer installs `uv` via its official installer if absent. - The `/recreator` skill tree once into `~/.agents/skills/recreator`, symlinked into every agent runtime found on the machine — `~/.claude/skills/recreator`, `~/.codex/skills/recreator`, `~/.config/opencode/skills/recreator`, `~/.cursor/skills/recreator`, `~/.gemini/skills/recreator` — whichever of those runtime directories exist. Set `RECREATOR_SKILLS_DIR` to install to one exact directory instead and skip multi-runtime linking. - The MCP server registered automatically into every detected runtime's config (see "Register the MCP server" below). **Published:** `recreator-mcp` and `recreator-core` are live on PyPI (v0.1.0). The installer checks PyPI first and exits with an actionable message rather than a resolver traceback if it is ever unreachable. To install only the skill tree and skip the CLI/MCP server on purpose (agent-only machine, or you already run `recreator-mcp` from elsewhere): ``` RECREATOR_SKIP_CLI=1 curl -fsSL https://recreator.ilvs.space/install.sh | bash ``` From a repo checkout, install from source instead: ``` uv tool install --from ./packages/mcp recreator-mcp ``` ### Dependency: ffprobe `ffprobe` is a **real runtime dependency**, not optional. Core measures media with it (duration, resolution, stream presence) and locates it on PATH, then at `/opt/homebrew/bin/ffprobe` and `/usr/local/bin/ffprobe`. Without it, probing and media-clip insertion fail. `brew install ffmpeg` on macOS. ### Register the MCP server The installer registers `recreator-mcp` automatically into every agent runtime's config it finds on the machine — Claude Code, Codex, OpenCode, Cursor, Gemini — merging into the existing file rather than overwriting it, backing it up first, and leaving alone any `recreator` entry the user already customized. Set `RECREATOR_SKIP_MCP=1` to skip the whole registration pass (the skill tree and CLI still install; register by hand later). Registration is also skipped automatically if the CLI itself was not installed, or if `python3` is unavailable to perform the merge — in both cases the installer prints these same manual snippets in its summary. Manual registration, per runtime, if you skipped it or a runtime was flagged: Claude Code — `~/.claude.json`: ```json { "mcpServers": { "recreator": { "command": "recreator-mcp" } } } ``` Codex — `~/.codex/config.toml`: ```toml [mcp_servers.recreator] command = "recreator-mcp" ``` OpenCode — `~/.config/opencode/opencode.json`. Note the outlier shape: the key is `mcp`, not `mcpServers`, and `command` is an array: ```json { "mcp": { "recreator": { "type": "local", "command": ["recreator-mcp"], "enabled": true } } } ``` Cursor — `~/.cursor/mcp.json`: ```json { "mcpServers": { "recreator": { "command": "recreator-mcp" } } } ``` Gemini — `~/.gemini/settings.json`: ```json { "mcpServers": { "recreator": { "command": "recreator-mcp" } } } ``` ### Remote MCP endpoint ``` https://mcp.recreator.ilvs.space ``` Bearer token required on every request: `Authorization: Bearer `. The user obtains the token; it is not self-service and there is no provisioning, rotation, or per-client identity — it is a static pre-shared token, not OAuth. The remote endpoint exposes **only cloud-safe tools** — registry and catalog lookups that are pure metadata over HTTPS. Tools that need a binary, the user's disk, or their CapCut library are local-only via stdio and are deliberately absent from the remote surface. Do not expect `recreator.probe.*`, `recreator.export.capcut`, or a local-destination `templates.pull` to be available remotely. For those, run `recreator-mcp` locally over stdio. Local HTTP transport is also available and **refuses to start without a token**: ``` recreator-mcp # stdio (default) recreator-mcp --transport http # 127.0.0.1:8000/mcp, token required ``` | Flag | Env var | Default | | --- | --- | --- | | `--transport {stdio,http}` | `RECREATOR_MCP_TRANSPORT` | `stdio` | | `--host` | `RECREATOR_MCP_HOST` | `127.0.0.1` | | `--port` | `RECREATOR_MCP_PORT` | `8000` | | `--path` | `RECREATOR_MCP_PATH` | `/mcp` | | `--token-file` | `RECREATOR_MCP_TOKEN` | none | --- ## MCP tools All times at the tool boundary are **seconds** (float). Ranges are `[[start, end], ...]`. Keyframe offsets are measured from the start of the clip, not the timeline. Every tool defaults its `project` argument to `.recreator/project.json` and saves atomically. ``` recreator.project.init create a project seeded from probed media recreator.project.load read timeline state; source of clip ids recreator.project.save update project name and metadata recreator.probe.media ffprobe duration, resolution, streams recreator.probe.loudness windowed RMS/peak dB series — raw numbers only recreator.timeline.remove_ranges delete ranges, rippling by default recreator.timeline.keep_ranges keep only the given ranges recreator.timeline.mute_ranges silence ranges without changing timing recreator.timeline.bleep_ranges censor: mute, bleep, or cut recreator.timeline.add_text_clips caption/text clips with character-offset styles recreator.timeline.add_media_clips b-roll, overlays, music recreator.timeline.add_keyframes animate one property of one clip recreator.timeline.set_transform static scale/position/rotation/opacity/crop recreator.export.capcut build a draft into a caller-specified directory recreator.validate report structural issues before exporting recreator.catalog.list every tool this server exposes, by namespace recreator.catalog.get full parameter schema for one tool id recreator.templates.list HyperFrames route templates recreator.templates.get full contract for one route recreator.templates.recommend rank routes by counted lexical evidence recreator.hyperframes.templates.list mirrored HyperFrames template catalog recreator.hyperframes.templates.get one template's metadata recreator.hyperframes.templates.pull fetch a template to disk recreator.jobs.submit submit an async job kind; optionally block recreator.jobs.get one job's record, non-blocking recreator.jobs.wait rejoin an existing job, block until terminal recreator.jobs.list recent jobs, newest first recreator.jobs.cancel cooperative cancellation ``` **Discover at runtime rather than trusting this list.** `recreator.catalog.list` is generated by introspecting the live registered tools, so it cannot drift. `recreator.catalog.get(id=...)` returns the full JSON input schema. `templates.list` filters on **hard facts only** (`aspect`, `type`, `tags`, `min_duration`, `max_duration`, `origin`, `limit`) and returns candidates in the registry's own order. It does no scoring and picks no winner — the response carries a `ranking` note saying so. Which templates are worth showing, and in what order, is your judgment. Same boundary as everything else. `hyperframes.templates.pull` is the one tool here that writes to disk, so it refuses a non-empty destination without `overwrite=true`, and rejects an archive outright if any member would escape the destination (`../`, absolute path, or symlink) before extracting a byte. ### CLI fallback — what actually exists today Core is a plain library and CLI, usable with no MCP installed. That is what makes the Codex / non-MCP path real. **But be accurate about the current state:** - **Implemented today:** `recreator doctor` (with `--preset`, `--json`, `--os`, `--no-versions`) and `recreator --version`. `doctor` measures this machine against the toolchain presets and is report-only — it prints install commands, never runs them. - **PLANNED, not implemented:** a subcommand per MCP tool (e.g. `recreator timeline remove-ranges --ranges '[[12.4,15.9]]'`, `recreator export capcut`). The skill tree documents this as a contract surface; the parser currently registers only `doctor`. Do not tell a user to run a CLI subcommand other than `doctor`. Use MCP for timeline and export work today. --- ## Platform reality — do not overstate this - **macOS writes `draft_info.json`. Windows writes `draft_content.json`.** Most public tooling assumes the Windows name. The writer branches on OS. - **Only macOS is actually exercised.** There is no CI. **Linux and Windows are UNVERIFIED** — treat both as unverified until something runs them. CapCut desktop does not exist on Linux at all; the timeline IR, CLI, and MCP server work there, but the export path has never been run. - The installer supports macOS and Linux, and refuses cleanly elsewhere. On Windows, use WSL2. ## CapCut format facts Measured from 15 real projects, CapCut 6.3.0 → 9.3.0. These correct errors that are widespread in public tooling — trust this over blog posts and other repos. - **Text `styles[].range` is UTF-16 code-unit indexed.** Not bytes (0/1121 matched), not characters. Decisive case: `"11:50 PM\nGoodnight!! 😴"` is 22 characters, 23 UTF-16 units, 25 bytes — and `range=[0,23]`. Measured 1250/1250 UTF-16, 1249/1250 codepoints, 0/1250 bytes. CapCut is Electron; these are JS string indices. Vietnamese and CJK are BMP and cannot expose the difference — only emoji can. - **All times in the draft are microseconds.** A 583.7s project has `duration: 583733333`. The MCP boundary is seconds; conversion happens at the edge. - **`version: 360000` held constant** across app 6.3.0 → 9.3.0 (`new_version` 136 → 183). All drafts plaintext, `app_source: "cc"`. - **Companion materials are mandatory and track-type-specific.** Video segments need `speed`, `placeholder_info`, `sound_channel_mapping`, `vocal_separation`, `canvas` (plus `material_color` on 9.x). Audio segments need `beats` instead of canvas/material_color. Omitting them is the likeliest cause of CapCut rejecting or corrupting a draft. - **Keyframes are unverified.** All 1817 segments across 15 projects had `common_keyframes: []`. The curve wire format is reconstructed, not observed — prefer baking curves into linear keyframes. - Images live in `materials.videos`, not the empty `materials.images`. - `draft_meta_info.json` is what makes a project appear in CapCut's home grid. --- ## Repository layout ``` packages/core/ timeline IR, edit ops, CapCut writer, validator (recreator-core) packages/mcp/ MCP server — thin protocol wrapper, zero logic (recreator-mcp) packages/auth/ credentials (planned) apps/landing/ this site skills/recreator/ the SKILL.md tree agents consume ``` Distribution names (`recreator-core`, `recreator-mcp`) and the import name (`recreator`) are stable. Skills, tests, and the MCP server depend on them.