FilmCraft is a clean-room, Premiere-class non-linear editor written in pure Rust. This distribution wraps its headless engine so an agent on Linux, Windows, or macOS can cut, caption, transition and export video — one JSON file in, an mp4 out.
One command puts all three headless engines and the fcedit
toolbox on PATH. Install all by default, or select a subset with FC_APPS.
curl -fsSL https://raw.githubusercontent.com/jokedud/filmcraft-agent/main/agent/install.sh | sh
powershell -ExecutionPolicy Bypass -c "irm https://raw.githubusercontent.com/jokedud/filmcraft-agent/main/agent/install.ps1 | iex"
docker build -f agent/Dockerfile -t fcedit .
docker run -p 8321:8321 -e FCEDIT_TOKEN=replace-me -v $PWD/media:/media fcedit
# POST recipes to http://localhost:8321/recipe
git clone https://github.com/jokedud/filmcraft-agent && cd filmcraft-agent
cargo build --release -p filmcraft-cli
# binary at target/release/filmcraft-cli; agent tool is agent/fcedit.py
Declarative recipe → exported video. Named clips, auto-append timeline, transitions, effects, captions, keyframes.
A persistent editing session over HTTP — POST /recipe, /run, /batch, /frame, GET /inspect.
filmcraft-cli, effectcraft-cli and soundcraft-cli each expose their full tools over stdio.
Choose an engine in the recipe's app field, or let a pipeline
create a title, mix audio, and assemble the final video. Recipes also accept raw
commands and direct MCP tool calls.
Video edits, transitions, captions, effects, audio placement and export. Use fcedit.
Compositions, text and shape layers, transforms, keyframes, expressions, masks, effects and rendered previews. Use ecedit.
Audio sessions, tracks, inserts, automation, markers and measured mix bounces. Use scedit.
{"app":"effect","comp":{"name":"Title","width":1920,"height":1080,"fps":30,"duration":5},
"layers":[{"type":"text","name":"title","text":"A NEW STORY","size":120,
"transform":{"position":[960,540],"opacity":100},
"keys":{"transform/opacity":[{"time":0,"value":0},{"time":1,"value":100}]},
"effects":[{"effect":"Glow","values":{}}]}],
"preview":[{"time":1,"path":"/ABSOLUTE/PATH/title.png"}],
"render":{"path":"/ABSOLUTE/PATH/title.mp4","format":"h264"}}
{"app":"sound","session":{"name":"Mix","sample_rate":48000},
"tracks":[{"name":"music","src":"/ABSOLUTE/PATH/music.wav","gain":-6,"pan":0,
"inserts":[{"plugin":"eq_7band","params":{}}]}],
"bounce":{"path":"/ABSOLUTE/PATH/mix.wav","format":"wav","bit_depth":"24","start":0,"end":10}}
{"app":"pipeline","steps":[
{"id":"title","app":"effect","comp":{"name":"Title","width":1920,"height":1080,"fps":30,"duration":5},
"layers":[{"type":"text","text":"TITLE","size":100}],
"render":{"path":"/ABSOLUTE/PATH/title.mp4","format":"h264"}},
{"id":"mix","app":"sound","session":{"name":"Mix","sample_rate":48000},
"tracks":[{"name":"music","src":"/ABSOLUTE/PATH/music.wav"}],
"bounce":{"path":"/ABSOLUTE/PATH/mix.wav","format":"wav","start":0,"end":5}},
{"id":"final","app":"film","media":{"title":"{{title}}","music":"{{mix}}","clip":"/ABSOLUTE/PATH/clip.mp4"},
"timeline":[{"op":"place","src":"title","at":0,"dur":5},
{"op":"place","src":"clip","at":0,"dur":5},{"op":"audio","src":"music","at":0,"dur":5}],
"export":{"path":"/ABSOLUTE/PATH/final.mp4","format":"h264"}}]}
One JSON document describes media, timeline, styling and export. fcedit build
compiles it to engine commands against a live session — clip ids resolve by name.
{
"sequence": {"width": 1920, "height": 1080, "fps": 30},
"media": {"a": "/abs/intro.mp4", "song": "/abs/music.mp3"},
"captions": {"format": "Subtitle", "style": {"size": 42, "anchor": "bottom"}},
"timeline": [
{"op": "place", "src": "a", "in": 2, "out": 8, "name": "intro"},
{"op": "place", "src": "a", "in": 12, "dur": 4}, // auto-appends
{"op": "audio", "src": "song", "at": 0, "track": "A2", "dur": 10},
{"op": "transition", "clip": "intro", "effect": "cross_dissolve", "frames": 24, "edge": "out"},
{"op": "text", "content": "MADE BY AN AGENT", "at": 0.5, "dur": 4, "position": [960, 540], "size": 120},
{"op": "effect", "clip": "#1", "effect": "gaussian_blur", "params": {"blurriness": 30}},
{"op": "speed", "clip": "intro", "speed": 200},
{"op": "gain", "clip": "intro.audio", "db": -6},
{"op": "caption", "text": "hello world", "at": 1, "dur": 2},
{"op": "marker", "at": 4, "name": "beat drop"}
],
"export": {"path": "out.mp4", "format": "h264", "crf": 20, "burnCaptions": true}
}
fcedit build recipe.json --verbose
# → out.mp4 (h264 1080p, captions burned in)
| op | what it does | engine command |
|---|---|---|
place | put media on the timeline (in/out/dur, at or auto-append, track, audioTrack, name) | timeline.place |
audio | audio-only placement (music beds, VO) | timeline.place |
image | still image with a duration | timeline.place |
matte / bars | generated color matte / bars & tone | file.newColorMatte … |
text | title graphic (position, size, font, props) | graphics.newText / graphics.set |
shape | rectangle / ellipse / polygon overlay | graphics.newRectangle … |
transition | dissolves, wipes, dips; "clip": "name.audio" for audio transitions | sequence.applyVideo/AudioTransition |
effect | apply an effect + params (blur, Lumetri-style color…) | effects.apply / effects.setParam |
param | set any effect/motion param, with keyframes | effects.setParam |
speed | speed/duration, reverse, optical flow | clip.speedDuration |
gain | clip audio gain (set/adjust/normalize) | clip.audioGain |
trim / move / razor | frame-accurate edits | timeline.trim / move / razor |
caption / importCaptions | subtitle cues or an .srt/.vtt file; burn in or sidecar on export | captions.add / captions.import |
marker | named timeline marker | markers.add |
preview | PNG of the program frame at a time — agent "eyes" | file.exportFrame |
transcribe | speech → text (when a speech model is present) | sequence.transcribe |
raw | escape hatch: any of the 700+ engine commands | any |
{"mcpServers": {"filmcraft":
{"command": "filmcraft-cli", "args": ["mcp"]}}}
Tools: command_run, command_batch, command_list, doc_inspect, media_import, render_frame, plus UI-driving tools in bridge mode.
fcedit serve --apps film,effect,sound --port 8321
# non-loopback listeners require a token
fcedit serve --host 0.0.0.0 --token "$FCEDIT_TOKEN"
curl -X POST localhost:8321/recipe \
-H 'Content-Type: application/json' -d @recipe.json
Also POST /run, /batch, /frame (PNG), GET /inspect, /commands. Or ship the whole thing as the Docker image.
Drop agent/SKILL.md into your agent's skills — it documents install, the recipe schema, clip refs, and the command cookbook.
filmcraft-cli exec timeline.place '{"item":8,"track":"V1","time":0}'
filmcraft-cli run script.jsonl --save-as edit.fcproj
filmcraft-cli export out.mp4 --project edit.fcproj --format h264
Every command is a shell call; projects persist as .fcproj files.
--bridge) and click, drag, and screenshot it.