effectcraft · diff
git:20261026.76415fe to git:20261029.b8b54c3
112 added, 113 removed. Audit A to A.
---
name: effectcraft
description: >-
EffectCraft is an open-source, pure-Rust motion-graphics, compositing and VFX app for macOS,
Windows, Linux and the web — a clean-room, After Effects-shaped studio with compositions,
layers, keyframes, 307 effects, expressions, Classic/Advanced 3D, tracking, roto, a render queue
and Lottie, driven by a CLI, an MCP server, a localhost JSON control channel and JavaScript
scripting. Trigger this skill whenever the user mentions EffectCraft, `.ecproj`/`.ecprojx`/
`.ectemplate`/`.ecpreset`, `effectcraft-cli`, its MCP tools or control channel; asks what it can
or cannot do, which formats/shortcuts it has, or how to script, batch, render, verify, repair or
troubleshoot it; or describes the job without naming the app ("make a title card", "animate this
logo", "key this green screen", "render a ProRes", "export Lottie", "open this .aep"). Also use
it when choosing between EffectCraft and FilmCraft, or preparing/verifying assets for it. Do not
assume After Effects project files open: they do not.
---
# EffectCraft
- Audit basis: this checkout is tag `v0.17.0`, commit `362474a843ddb67555d4c2d19ad6a590388aad0e`. The installed `/Applications/EffectCraft.app` reports version `0.17.0` (`CFBundleShortVersionString`, and `EffectCraftBuildCommit` = the same commit) and its binary prints `effectcraft 0.17.0`, so the bundle matches this source exactly. **Verify against your installed version** if the ArtCraft Launcher has auto-updated since. No `references/` directory ships with this skill: every claim is cited inline to the repository.
+ Audit basis: this checkout is tag `v0.18.0`, commit `c3600f9b59b109da97871a43a6898683a0163cb1`. The installed `/Applications/EffectCraft.app` reports version `0.18.0` and its binary prints `effectcraft 0.18.0`, so the bundle matches this source exactly. **Verify against your installed version** if the ArtCraft Launcher has auto-updated since. No `references/` directory ships with this skill: every claim is cited inline to the repository.
## Overview
- EffectCraft is a free, open-source, clean-room reimplementation of Adobe After Effects 2026 for motion graphics and visual effects: compositions, layers, keyframes, 307 effects, layer styles, expressions, Classic and Advanced 3D, tracking, roto/paint, a render queue and Lottie/timeline interchange. It is pure Rust with no FFmpeg; media codecs come from the sibling FilmCraft project, pinned by revision (`Cargo.toml`). It is **alpha**: the repo self-rates ≈91% feature breadth but ≈45% "ready for real work" (`docs/gaps.md:15-28`, `docs/target-app-parity.md`). FilmCraft is the editing/NLE sibling; EffectCraft is the compositor. Source: GitHub `storytold/effectcraft`, MIT OR Apache-2.0.
+ EffectCraft is a free, open-source, clean-room reimplementation of Adobe After Effects 2026 for motion graphics and visual effects: compositions, layers, keyframes, 307 effects, layer styles, expressions, Classic and Advanced 3D, tracking, roto/paint, a render queue and Lottie/timeline interchange. It is pure Rust with no FFmpeg; media codecs come from the sibling FilmCraft project, pinned by revision (`Cargo.toml`). It is **alpha**: the repo self-rates ≈91% feature breadth but ≈45% "ready for real work" (`docs/gaps.md`, `docs/target-app-parity.md`). FilmCraft is the editing/NLE sibling; EffectCraft is the compositor. Source: GitHub `storytold/effectcraft`, MIT OR Apache-2.0.
- `v0.17.0` is a broad maintenance release with two new content features, not a parity jump. A tree comparison against `v0.15.0` (the previous skill's pin) shows ~354 changed/added/removed files and ~12,200 changed lines, but **no change to the effect registry**: 307 effects / 3032 parameters / 280 GPU / 21 categories stand. What is new for agents: **`layer.replaceSource`** (Layer ▸ Replace Source, Alt-drag and Cmd+Opt+/) and the **Cryptomatte picker** (`effect.pickCryptomatte`, `effect.cryptomatteAt`, #473), taking the engine registry from 669 to **672** ids; a `run --args-file` CLI flag; Georgian (`ka`) localization (11 menu catalogs, 8 panel catalogs); a real PR CI gate (`cargo xtask ci`, `.github/workflows/ci.yml`); Reveal Layer Source in Project; timeline row controls that act on the whole layer selection (#1849); and a Composition Settings Resolution popup (#2167).
+ `v0.18.0` is a maintenance release: the **effect registry is unchanged** (307 effects / 3032 parameters / 280 GPU / 21 categories stand) and the engine command registry gains exactly one id, **`text.deleteSurrounding`**, over v0.17.0's 672. What is new for agents: **each open composition is a tab in the timeline group's tab row, with an overflow menu** (PR #2458); **export pipelines now encode while the next batch renders and skip transparent pixels when compositing** (PR #2518); **Edit-menu keys edit a focused text field**, docked and in panel windows (PR #2459); a workarea-selector hover cursor fix; software-GPU median timeouts fixed; panels moved onto craft-ui's shared docking (PR #2432); the UI migrated to egui 0.37; a startup update prompt with direct self-updating (PR #2420); and Windows installs per-user by default (PR #2493). The shared-module refactor consolidated the `cmd!`/`query!` call sites (697 → 678 occurrences) without changing the registry.
## Capabilities (verified)
### Project and document model
- - **Native `.ecproj`** is versioned, pretty-printed JSON headed by `savedBy` and `schema` (currently 2), with `activeComp`/`openComps`/`compTimes`, `settings`, `items`, `nextId`, `renderQueue`, render templates and preferences (`crates/project/src/lib.rs:35,54-80,1601-1640`). Saving re-reads the text before writing and writes atomically, so a save cannot produce a file the app cannot open; a project whose `savedBy` is a newer version still opens, with a warning that settings this version does not know are lost if you save (`crates/engine/src/commands/file.rs:86-120`).
- - **XML project copy `.ecprojx`** is the same data as `EffectCraftProject` XML (lossless, element-per-value, nesting capped at 128); Save a Copy As XML writes it, `file.open` reads it back, and a project opened from XML saves as XML again (`crates/engine/src/xml_project.rs`, `crates/engine/src/commands/file.rs:43-96`).
- - **Autosave, crash recovery and MCP checkpoints**; `mcp --autosave` writes a checkpoint after each mutating call (`crates/engine/src/autosave.rs`, `docs/agents.md` §Keeping headless work). Out-of-range values (size > 30,000 px, frame rate outside 0.001–1000, pixel aspect outside 0.1–10, motion-blur samples 2–64) are repaired on open and reported in the result's `repaired` list, not rejected (`crates/project/src/limits.rs:17-29`, `crates/engine/src/commands/file.rs:49-70`).
- - **One project per app process**, with compositions open as tabs restored from `openComps`; **36 panel kinds** (34 plain + `ScriptPanel(u32)` and `Viewer(u32)`) and **15 named workspaces**, and any panel can move to its own native window (`crates/ui-egui/src/dock.rs:14-58,281-297`, `docs/architecture.md` §3).
+ - **Native `.ecproj`** is versioned, pretty-printed JSON headed by `savedBy` and `schema` (currently 2), with `activeComp`/`openComps`/`compTimes`, `settings`, `items`, `nextId`, `renderQueue`, render templates and preferences (`crates/project/src/lib.rs`). Saving re-reads the text before writing and writes atomically; a project whose `savedBy` is a newer version still opens, with a warning.
+ - **XML project copy `.ecprojx`** is the same data as `EffectCraftProject` XML (lossless); Save a Copy As XML writes it, `file.open` reads it back.
+ - **Autosave, crash recovery and MCP checkpoints**; `mcp --autosave` writes a checkpoint after each mutating call (`crates/engine/src/autosave.rs`, `docs/agents.md`). Out-of-range values are repaired on open and reported in the result's `repaired` list, not rejected (`crates/project/src/limits.rs`).
+ - **One project per app process**, with compositions open as tabs restored from `openComps` — **now one tab per open comp in the timeline group's tab row, with an overflow menu**; **36 panel kinds** and **15 named workspaces**, and any panel can move to its own native window (`crates/ui-egui/src/dock.rs`, `docs/architecture.md`).
### Composition, animation, expressions
- - **Compositions** use exact integer `Tick` time at 254,016,000,000 ticks/s; `comp.new` defaults to 1920×1080, 29.97 fps, 10 s, and accepts `renderer: classic3D|advanced3D` (`crates/time/src/lib.rs:1-22`, `crates/engine/src/commands/comp.rs:103-104`). Layer kinds cover footage, precomp, solid, text, shape, null, camera, light, 3D model and parametric primitive; adjustment layers are solids with the adjustment switch (`crates/project/src/lib.rs` layer model).
- - **Parenting, track mattes, all 38 blend modes and motion blur** are in the document model (`crates/color/src/blend.rs:52`).
- - **Keyframes**: linear/Bezier/hold, per-side speed and influence (Easy Ease), auto/continuous Bezier, roving keys, Keyframe Velocity/Interpolation, and a Graph Editor with value and speed graphs (`crates/engine/src/commands/prop.rs:1369-1389`, `docs/ui-parity.md` Timeline row). Time remap/stretch/reverse/freeze are `layer.*` commands (`crates/engine/src/commands/layer_time.rs:253`).
- - **Expressions** run on `boa` with the AE object model (`thisComp`, `thisLayer`, `thisProperty`, `valueAtTime`, `velocityAtTime`, `wiggle`, `temporalWiggle`, `smooth`, `loopIn/Out`, `sampleImage`, `sourceRectAtTime`, `nearestKey`, `propertyGroup`, text style API) and AE legacy array maths (`crates/expr/src/lib.rs:14-25`, `docs/scripting-parity.md`).
+ - **Compositions** use exact integer `Tick` time at 254,016,000,000 ticks/s; `comp.new` defaults to 1920×1080, 29.97 fps, 10 s, and accepts `renderer: classic3D|advanced3D`. Layer kinds cover footage, precomp, solid, text, shape, null, camera, light, 3D model and parametric primitive; adjustment layers are solids with the adjustment switch.
+ - **Parenting, track mattes, all 38 blend modes and motion blur** are in the document model (`crates/color/src/blend.rs`).
+ - **Keyframes**: linear/Bezier/hold, per-side speed and influence (Easy Ease), auto/continuous Bezier, roving keys, Keyframe Velocity/Interpolation, and a Graph Editor with value and speed graphs. Time remap/stretch/reverse/freeze are `layer.*` commands.
+ - **Expressions** run on `boa` with the AE object model (`thisComp`, `thisLayer`, `thisProperty`, `valueAtTime`, `velocityAtTime`, `wiggle`, `temporalWiggle`, `smooth`, `loopIn/Out`, `sampleImage`, `sourceRectAtTime`, `nearestKey`, `propertyGroup`, text style API) and AE legacy array maths (`crates/expr/src/lib.rs`).
### Effects, text, shapes, masks
- - **307 registered effects, 3032 parameters, 280 GPU-capable, all 32-bit float, 21 categories, 0 marked partial**, plus 9 Layer Styles with Global Light (`docs/effects.md:6-10`, generated from `crates/effects/src/catalog.rs` and checked by the `effects_doc_is_current` test at `crates/effects/src/catalog.rs:173-182`; `crates/project/src/styles.rs:28-40`). The README's "306" is one behind the registry (`README.md:23,129,231`). Effects are addressed by id (`ec.<category>.<name>`) or display name.
- - **54 built-in animation presets** (8 text animators, 23 backgrounds, 23 transitions) and `.ecpreset` save/apply; the preset file is JSON tagged `effectcraftAnimationPreset` (`crates/engine/presets/*.json`, `crates/engine/src/commands/animation.rs:16-17`).
- - **Text, shapes and masks**: point/paragraph text with style runs, variable fonts (named instances by PostScript name, per-axis `variations`), text animators; shape operators (trim, repeater, round corners, offset, zig zag, twist, wiggle, merge, gradient stroke); masks with **7 modes**, feather, expansion, vertex editing and Mask Interpolation (`crates/engine/src/commands/{text_edit,text_anim,mask,mask_interp,paths}.rs`, `crates/project/src/props.rs:217-243`, `crates/text/src/fonts.rs:57-63`).
+ - **307 registered effects, 3032 parameters, 280 GPU-capable, all 32-bit float, 21 categories, 0 marked partial**, plus 9 Layer Styles with Global Light (`docs/effects.md`, generated from `crates/effects/src/catalog.rs` and checked by the `effects_doc_is_current` test). The README's "306" is one behind the registry. Effects are addressed by id (`ec.<category>.<name>`) or display name.
+ - **54 built-in animation presets** (8 text animators, 23 backgrounds, 23 transitions) and `.ecpreset` save/apply.
+ - **Text, shapes and masks**: point/paragraph text with style runs, variable fonts, text animators; shape operators (trim, repeater, round corners, offset, zig zag, twist, wiggle, merge, gradient stroke); masks with **7 modes**, feather, expansion, vertex editing and Mask Interpolation. **New:** `text.deleteSurrounding` deletes characters around the caret (the only registry addition this release).
### 3D, tracking, roto and paint
- - **Classic 3D and Advanced 3D**: 3D layers, one/two-node cameras with depth of field, Parallel/Spot/Point/Ambient/Environment lights, ray-cast shadows; Advanced 3D adds PBR materials, six primitives (cube, sphere, plane, torus, cone, cylinder), imported models and environment lighting (`crates/gpu/src/lib.rs:1-12`, `crates/engine/src/commands/model3d.rs`, `docs/agents.md` §Pitfalls — Environment light). "New 3D Primitive" with no parameters opens its dialog (`crates/ui-egui/src/menus.rs:405-407`).
- - **Tracking**: point/transform tracking, stabilize, affine/perspective corner-pin tracks, mask tracking including face outline/detailed features, Warp Stabilizer, and a 3D Camera Tracker that creates cameras, solids, nulls, text and shadow catchers (`crates/engine/src/commands/{track,warp_cmds,camera_cmds,mask_interp}.rs`).
- - **Roto Brush and Object Matte**: stroke/propagate/freeze with optional MobileSAM or classical fallback; `objectMatte.apply/propagate/freeze` for click/box segmentation with disk-cached mattes; face tracking with optional MediaPipe Face Landmarker. Paint strokes/clone source, Liquify and all five puppet pin kinds are commands too (`crates/engine/src/commands/{roto_cmds,paint,liquify,puppet}.rs`). **Content-Aware Fill and Scene Edit Detection** run as background jobs with `wait`; **Auto-trace** is synchronous (`crates/engine/src/commands/{content_fill,autotrace,scene_detect}.rs`).
- - **Cryptomatte picking (new in 0.17.0)**: `effect.pickCryptomatte {layer?, effect, x, y, mode: replace|add|remove, scale?}` replaces/edits the effect's Selection with the ID under a point in effect space, in one undo step; `effect.cryptomatteAt` only reports `{name, hash, layer, selected}` (a `query!`). Both refuse a Selection driven by an expression (`crates/engine/src/commands/effect.rs:250-290,731-746`, `crates/effects/src/crypto_select.rs`, `crates/engine/src/tests_crypto_pick.rs`).
- - **Replace Source (new in 0.17.0)**: `layer.replaceSource {layers?, item: id|name}` (defaults to the Project-panel selection) swaps a footage/precomp/solid layer's source while keeping name, timing, transforms, keyframes, expressions, effects, masks, switches, parenting and track matte; folders, data files and 3D models are refused, and a comp cannot contain itself (`crates/engine/src/commands/layer.rs:317-400`, `crates/engine/src/tests_replace_source.rs`). `layer.revealSource` (Layer ▸ Reveal ▸ Reveal Layer Source in Project) emits a `project.reveal` frontend event that the live UI handles by opening the Project panel on the item (`crates/engine/src/commands/layer_menu.rs:771-782,928`, `crates/ui-egui/src/menus.rs:952`).
+ - **Classic 3D and Advanced 3D**: 3D layers, one/two-node cameras with depth of field, Parallel/Spot/Point/Ambient/Environment lights, ray-cast shadows; Advanced 3D adds PBR materials, six primitives, imported models and environment lighting.
+ - **Tracking**: point/transform tracking, stabilize, affine/perspective corner-pin tracks, mask tracking including face outline/detailed features, Warp Stabilizer, and a 3D Camera Tracker that creates cameras, solids, nulls, text and shadow catchers.
+ - **Roto Brush and Object Matte**: stroke/propagate/freeze with optional MobileSAM or classical fallback; face tracking with optional MediaPipe Face Landmarker. Paint strokes/clone source, Liquify and all five puppet pin kinds are commands. **Content-Aware Fill and Scene Edit Detection** run as background jobs with `wait`; **Auto-trace** is synchronous.
+ - **Cryptomatte picking**: `effect.pickCryptomatte` replaces/edits the effect's Selection with the ID under a point, in one undo step; `effect.cryptomatteAt` only reports.
+ - **Replace Source**: `layer.replaceSource {layers?, item: id|name}` swaps a footage/precomp/solid layer's source while keeping everything; `layer.revealSource` opens the Project panel on the item.
### Import formats (only what the code registers)
| Kind | Extensions / route | Evidence |
|---|---|---|
- | Stills | `png jpg jpeg gif webp tif tiff bmp exr tga hdr psd psb svg pdf ai eps` (17; animated WebP as footage) | `crates/media/src/lib.rs:62-63` |
+ | Stills | `png jpg jpeg gif webp tif tiff bmp exr tga hdr psd psb svg pdf ai eps` (17; animated WebP as footage) | `crates/media/src/lib.rs` |
| Film scans | `dpx cin` (8/10/12/16-bit; log shown as stored) | `crates/media/src/dpx.rs` |
- | Video | `mp4 m4v mov mkv webm mxf`; H.264, HEVC, VP9, AV1, ProRes, DNxHD, MPEG-2, MJPEG; MXF OP1a/OP-Atom | `crates/media/src/lib.rs:67`, `docs/file-format-parity.md` |
- | Audio | `wav wave bwf aif aiff mp3 mp2 m4a aac flac ogg opus` (FilmCraft `AUDIO_EXTENSIONS`) | `crates/media/src/lib.rs:65` |
- | 3D models | `gltf glb obj stl fbx` (glTF: meshes/PBR/skins/animations; FBX 7.x: meshes/materials only) | `crates/media/src/lib.rs:70`, `crates/model/src/fbx.rs` |
- | Data | `json csv tsv` as data footage (plain `file.import`; Lottie needs `file.importLottie`) | `crates/engine/src/commands/file.rs` |
- | Lottie | `.json`, `.lottie` via `file.importLottie`; export `file.exportLottie` | `crates/engine/src/commands/lottie.rs:61-65` |
- | Timelines | `.xml` (FCP7), `.fcpxml`, `.otio`, `.edl`, `.aaf`, `.omf` | `crates/interchange/src/lib.rs:1-26` |
- | Color / fonts | LUTs `.cube .3dl .csp .spi1d/.spi3d`, CDL; bundled + installed fonts, `EFFECTCRAFT_FONT_DIRS` | `docs/file-format-parity.md:93`, `crates/text/src/font_dirs.rs` |
+ | Video | `mp4 m4v mov mkv webm mxf`; H.264, HEVC, VP9, AV1, ProRes, DNxHD, MPEG-2, MJPEG; MXF OP1a/OP-Atom | `crates/media/src/lib.rs`, `docs/file-format-parity.md` |
+ | Audio | `wav wave bwf aif aiff mp3 mp2 m4a aac flac ogg opus` | `crates/media/src/lib.rs` |
+ | 3D models | `gltf glb obj stl fbx` (glTF: meshes/PBR/skins/animations; FBX 7.x: meshes/materials only) | `crates/media/src/lib.rs`, `crates/model/src/fbx.rs` |
+ | Data | `json csv tsv` as data footage (plain `file.import`; Lottie needs `file.importLottie`) | `crates/engine/src/cmd/` |
+ | Lottie | `.json`, `.lottie` via `file.importLottie`; export `file.exportLottie` | `crates/lottie/` |
+ | Timelines | `.xml` (FCP7), `.fcpxml`, `.otio`, `.edl`, `.aaf`, `.omf` | `crates/interchange/src/lib.rs` |
+ | Color / fonts | LUTs `.cube .3dl .csp .spi1d/.spi3d`, CDL; bundled + installed fonts, `EFFECTCRAFT_FONT_DIRS` | `docs/file-format-parity.md`, `crates/text/src/font_dirs.rs` |
- `.json` through plain `file.import` is **data footage**, not Lottie — use `file.importLottie`. PSD/PDF/AI/EPS import as footage or as a composition (`importAs`); PSD groups become precomps, text becomes editable, effects map best-effort to Layer Styles (`crates/engine/src/psd_import.rs`). The macOS bundle declares a QOI document type that `STILL_EXTENSIONS` does not implement (`packaging/macos/Info.plist.in`, `crates/media/src/lib.rs:62-63`).
+ `.json` through plain `file.import` is **data footage**, not Lottie. PSD/PDF/AI/EPS import as footage or as a composition (`importAs`).
### Export formats
- The Render Queue writes **13 formats** (`crates/project/src/render_queue.rs:531-545`):
+ The Render Queue writes **13 formats** (`crates/project/src/render_queue.rs`):
| Family | Formats |
|---|---|
- | Video | H.264 MP4 (+AAC), HEVC MP4 (`hvc1`, Main/Main 10), AV1 MP4 (`av01`), ProRes MOV (Proxy→4444 XQ; 4444 with alpha), uncompressed 8-bit MOV, WebM (VP9 or AV1 + Opus) |
+ | Video | H.264 MP4 (+AAC), HEVC MP4 (`hvc1`), AV1 MP4 (`av01`), ProRes MOV (Proxy→4444 XQ; 4444 with alpha), uncompressed 8-bit MOV, WebM (VP9 or AV1 + Opus) |
| Stills | PNG, JPEG, TIFF, OpenEXR (32-bit float) sequences; animated GIF |
| Audio | WAV, AIFF (16-bit PCM) |
- Render Settings/Output Modules cover quality, resolution, time span (**Work Area by default**, `crates/project/src/render_queue.rs:368`), frame rate, proxy use, effects/solo/guide/depth overrides, colour depth, field rendering + 3:2 pulldown, motion blur, frame blending, skip existing frames, storage overflow, crop/ROI/resize, extra outputs, post-render actions and logs (`crates/project/src/render_queue.rs:324-382,1027-1065`).
+ Render Settings/Output Modules cover quality, resolution, time span (**Work Area by default**), frame rate, proxy use, overrides, colour depth, field rendering, motion blur, frame blending, skip existing frames, crop/ROI/resize, extra outputs, post-render actions and logs. **New:** export now **encodes while the next batch renders** and **skips transparent pixels when compositing** (faster exports, PR #2518).
- - **Single frames** also export as a layered PSD (Composition ▸ Save Frame As ▸ Photoshop Layers) or a multi-layer OpenEXR (ProEXR) (`crates/engine/src/commands/frame_export.rs:1-12,111`).
- - **Lottie** `.json`/`.lottie` with a `warnings` list for what Lottie cannot express (`crates/engine/src/commands/lottie.rs:9-30`, `crates/lottie/src/lib.rs`).
- - **Timeline interchange** `.xml` (FCP7 default), `.fcpxml`, `.otio`, `.edl`, `.aaf`, `.omf`; `prerender: none|unsupported|all` chooses what becomes ProRes 4444 media instead of clips (`crates/engine/src/commands/timeline.rs`, `crates/interchange/src/export.rs`).
- - **Essential Graphics template `.ectemplate`** is a ZIP (a manifest.json, the project JSON, a media folder and poster.png); **animation preset `.ecpreset`** is JSON tagged `effectcraftAnimationPreset` (`crates/engine/src/commands/essential.rs:11-12,860-882`, `crates/engine/src/commands/animation.rs:16-17`).
+ - **Single frames** also export as a layered PSD or a multi-layer OpenEXR.
+ - **Lottie** `.json`/`.lottie` with a `warnings` list.
+ - **Timeline interchange** `.xml` (FCP7 default), `.fcpxml`, `.otio`, `.edl`, `.aaf`, `.omf`.
+ - **Essential Graphics template `.ectemplate`** and **animation preset `.ecpreset`**.
### Automation and platform (counts re-measured at this tag)
- | Surface | Count at v0.17.0 | vs v0.15.0 | Evidence |
+ | Surface | Count at v0.18.0 | vs v0.17.0 | Evidence |
|---|---:|---|---|
- | Engine command ids | **672** | 669 → **+3**: `effect.pickCryptomatte`, `effect.cryptomatteAt`, `layer.replaceSource` | `crates/engine/src/commands/mod.rs:155-241` registers 80 modules; 672 unique `cmd!`/`query!` ids across `crates/` |
- | UI-only command ids | 111 | unchanged (no adds/removes) | `crates/ui-egui/src/menus.rs:29` (`uic!`) |
- | MCP tools | 36 (27 headless + 9 bridge-only) | unchanged, identical tool names | `crates/automation/src/tools.rs:542-979` |
- | Control-channel methods | 24 (21 dispatch arms, some with two names) | unchanged, identical set | `crates/ui-egui/src/control.rs:172-490` |
- | CLI subcommands | 12 | unchanged; new `run --args-file` flag | `apps/effectcraft-cli/src/main.rs:459-633` |
- | Render Queue formats | 13 | unchanged | `crates/project/src/render_queue.rs:531` |
- | Effects / params / GPU / categories / partial | 307 / 3032 / 280 / 21 / 0 | unchanged | `docs/effects.md:6-10` |
- | Blend modes | 38 | unchanged | `crates/color/src/blend.rs:52` |
- | Layer Styles | 9 | unchanged | `crates/project/src/styles.rs:28-40` |
- | Menu languages / panel catalogs | **11 / 8** | 10 → 11 menus, 7 → 8 panels: Georgian `ka` added | `crates/ui-egui/src/i18n.rs:18`, `crates/ui-egui/src/i18n/ui.rs:69-78` |
+ | Engine command ids | **672 +1** = registry unchanged except +`text.deleteSurrounding` | `cmd!`/`query!` call sites consolidated 697 → 678 by the shared-module refactor; id sets otherwise identical | `crates/engine/src/cmd.rs` `command_specs()` wires 83 `specs()` modules |
+ | UI-only command ids | 111 | unchanged (no adds/removes; table moved to `crates/ui-egui/src/commands.rs`) | `crates/ui-egui/src/menus.rs` `pub use commands::UI_COMMANDS` |
+ | MCP tools | 36 (27 headless + 9 bridge-only) | unchanged, identical tool names | `crates/automation/src/tools.rs` `static TOOLS` (36 `ToolDef` entries; 9 `bridge_only: true`) |
+ | Control-channel methods | 24 (21 dispatch arms) | unchanged, identical set | `crates/ui-egui/src/control.rs` |
+ | CLI subcommands | 12 | unchanged; `run --args-file` flag carried | `apps/effectcraft-cli/src/main.rs` |
+ | Render Queue formats | 13 | unchanged | `crates/project/src/render_queue.rs` |
+ | Effects / params / GPU / categories / partial | 307 / 3032 / 280 / 21 / 0 | unchanged | `docs/effects.md` |
+ | Blend modes | 38 | unchanged | `crates/color/src/blend.rs` |
+ | Layer Styles | 9 | unchanged | `crates/project/src/styles.rs` |
+ | Menu languages / panel catalogs | 11 / 8 | unchanged | `crates/ui-egui/src/i18n.rs` |
- - **CLI** `effectcraft-cli`: the 12 subcommands are `info commands exec run props get set render-frame render bench script mcp`, with `--json`, `--project`, `--demo`, `--empty`, `--save`, `--save-as`, `--bridge`, `--gpu`, per-codec render flags, `--queue` and `--progress` (`apps/effectcraft-cli/src/main.rs:2-37,94-200`). **New:** `run --args-file F` reads one argument per line (Windows' 32,767-character limit; `apps/effectcraft-cli/src/main.rs:153,521-527`).
- - **MCP** over stdio: **36 tools** — 27 headless and 9 bridge-only (`screenshot`, `ui_inspect`, `ui_elements`, `ui_click`, `ui_drag`, `ui_key`, `ui_type`, `ui_set`, `control`) — plus resources `effectcraft://document` and `effectcraft://commands`; protocol versions 2025-06-18 / 2025-03-26 / 2024-11-05, with completion hints for clients declaring 2026-07-28 (`crates/automation/src/tools.rs:542`, `crates/automation/src/server.rs:14,278-284`, `docs/mcp.md`).
- - **Control channel**: `effectcraft --control <port>` (or `EFFECTCRAFT_CONTROL_PORT`) speaks newline JSON on `127.0.0.1`: **24 methods** — `engine.execute`, `ui.menu.invoke`, `engine.commands`, `ui.menu.list`, `render.frame`, `ui.inspect/elements/set/screenshot/click/move/drag/scroll/key/type`, `ui.panel.show/close`, `ui.playback`, `ui.resize`, `ui.focus`, `ui.viewer.locate/hit`, `ui.timeline.locate`, `app.quit` (`crates/ui-egui/src/control.rs:172-490`, `docs/control-protocol.md`).
- - **Two command tiers**: 672 engine ids run headless (CLI/MCP/`engine.execute`); 111 UI-only ids (`timeline.*` 53, `tool.*` 15, `view.*` 12, `window.*` 11, `viewer.*` 8, `playback.*` 5, `app.*` 4, `flowchart.*` 2, `markers.*` 1) dispatch only in the live app. No command is a disabled stub: `crates/engine/src/commands/stubs.rs` returns an empty list ("Every After Effects menu entry has a real command now").
- - **Scripts/ScriptUI**: `.jsx`/`.js` with the AE object model (`app.project`, match names, `Socket`, `$.evalFile`, `#include`, `app.scheduleTask`), dockable ScriptUI panels that reopen at launch; `script.run`, `file.runScript`, CLI `script FILE.jsx` and `script --eval CODE` (`docs/plugins.md`, `docs/scripting-parity.md`, `crates/script/src/lib.rs`).
- - **WebAssembly effect plug-ins**: `.wasm`/`.wat`, API v1, sandboxed in `wasmi` with no imports (`crates/plugin/src/lib.rs:4-35`, `docs/plugins.md` §Effect plug-ins).
- - **Web build** (`apps/effectcraft-web`) runs the same engine/UI as WASM with OPFS/IndexedDB storage and `window.effectcraft` (`request`, `execute`, `commands`, `inspect`, `renderFrame`, `screenshot`, `workerFrameCheck`, `addFile`, `files`, `readFile`, `saveToBrowser`, `listStored`, `removeStored`, `flush`, `info`); it is not the installed macOS app (`docs/web.md:387-399`, `apps/effectcraft-web/src/api.rs`).
- - **GPU**: a wgpu compute compositor for Metal/Vulkan/Direct3D 12/WebGPU (Direct3D 12 is reported failing on some Windows GPUs, `docs/hardware-parity.md`); CPU is the reference; the desktop attaches the GPU compositor whenever the device is created and falls back to CPU compositing with a note otherwise (`crates/ui-egui/src/lib.rs:780-806`); headless CLI/MCP render on the CPU and say so unless started with `--gpu`, which needs a usable adapter (`crates/gpu/src/lib.rs:1-3`, `apps/effectcraft-cli/src/main.rs:344-350,373-378`).
- - **CI**: `.github/workflows/ci.yml` now runs `cargo xtask ci` on every push to `main` and every pull request through the shared `storytold/craft-common-actions` gate (new in this release; `docs/gaps.md` gap 3 still claims no PR gate — stale).
- - **Localization**: menus in **11** languages (cs, de, es, fr, pt-br, ru, uk, ka, ja, zh-hans, zh-hant); panel/tooltip catalogs in **8** (ja, zh-hans, zh-hant, uk, pt-br, cs, es, ka). Georgian is new and native-speaker reviewed; Korean and Italian remain absent (`crates/ui-egui/src/i18n.rs:18`, `crates/ui-egui/src/i18n/ui.rs:69-78`, `ROADMAP.md:138`).
+ - **CLI** `effectcraft-cli`: the 12 subcommands are `info commands exec run props get set render-frame render bench script mcp`, with `--json`, `--project`, `--demo`, `--empty`, `--save`, `--save-as`, `--bridge`, `--gpu`, per-codec render flags, `--queue` and `--progress`; `run --args-file F` reads one argument per line.
+ - **MCP** over stdio: **36 tools** — 27 headless and 9 bridge-only (`screenshot`, `ui_inspect`, `ui_elements`, `ui_click`, `ui_drag`, `ui_key`, `ui_type`, `ui_set`, `control`) — plus resources `effectcraft://document` and `effectcraft://commands`.
+ - **Control channel**: `effectcraft --control <port>` (or `EFFECTCRAFT_CONTROL_PORT`) speaks newline JSON on `127.0.0.1`: **24 methods** — `engine.execute`, `ui.menu.invoke`, `engine.commands`, `ui.menu.list`, `render.frame`, `ui.inspect/elements/set/screenshot/click/move/drag/scroll/key/type`, `ui.panel.show/close`, `ui.playback`, `ui.resize`, `ui.focus`, `ui.viewer.locate/hit`, `ui.timeline.locate`, `app.quit`.
+ - **Two command tiers**: engine ids run headless (CLI/MCP/`engine.execute`); UI-only ids dispatch only in the live app. No command is a disabled stub.
+ - **Scripts/ScriptUI**: `.jsx`/`.js` with the AE object model, dockable ScriptUI panels; `script.run`, `file.runScript`, CLI `script FILE.jsx`.
+ - **WebAssembly effect plug-ins**: `.wasm`/`.wat`, API v1, sandboxed in `wasmi` with no imports.
+ - **Web build** (`apps/effectcraft-web`) runs the same engine/UI as WASM with OPFS/IndexedDB storage and `window.effectcraft`; it is not the installed macOS app.
+ - **GPU**: a wgpu compute compositor for Metal/Vulkan/Direct3D 12/WebGPU; CPU is the reference; headless CLI/MCP render on the CPU and say so unless started with `--gpu`. Software-GPU median timeouts were fixed this release.
+ - **Localization**: menus in **11** languages; panel/tooltip catalogs in **8**. Korean and Italian remain absent.
## Known limitations and not-implemented
- - **After Effects projects will not open.** No `.aep`/`.aepx` reader, no `.ffx` presets, no `.mogrt` templates, no `.aet`/`.ars`/`.aom`, no AE SDK plug-ins (`.aex`/`.plugin`). Only `.ecproj`/`.ecprojx`, `.ecpreset` and `.ectemplate` are native (`docs/file-format-parity.md:21-34`, `docs/gaps.md` gaps 1/19/23).
- - **Fidelity against After Effects is mostly unmeasured.** The one live comparison (scalar ease, 8 cases, 168 samples) still passes, and this release adds the first *effect* comparisons: Tint, Tritone, Black & White, Exposure, Levels (Individual Controls), Curves, Luma Key, Extract, 4-Color Gradient and Fill match After Effects 2026 renders within 1–3/255, Brightness & Contrast up to 6.2/255. Everything else is self-graded — never promise AE-identical renders (`docs/fidelity/README.md`, `docs/effects-parity.md:18`).
- - **Alpha stability.** `docs/gaps.md` still lists open crash reports (#580, #501, #426, #341, #234, #879); a PR CI gate now exists but this tag is not a stability guarantee. Re-check the tracker rather than quoting this skill (`docs/gaps.md` gaps 3/4, `.github/workflows/ci.yml`).
- - **No hardware video decode/encode** (VideoToolbox, NVENC, Quick Sync, VA-API). HEVC/AV1 have no B-frames, multi-reference or SAO/CDEF, so files are larger; Opus has no FEC/DTX (`docs/gaps.md` gaps 15/22).
- - **Output/import gaps**: no 16-bpc uncompressed, AVI, lossless single-file video, MXF output, DNxHR, DPX/Targa/Photoshop sequences or ProRes RAW; no USD/USDZ, Substance `.sbsar`, camera raw, HEIF/AVIF, BRAW/R3D/ARRIRAW (`docs/file-format-parity.md:99-112`, `docs/gaps.md` gap 17).
- - **FBX/DPX/PDF caveats**: FBX is 7.x only, with no animation, skinning, cameras or lights; DPX/Cineon log values are shown as stored (no log-to-linear); PDF/AI/EPS import lacks JPX/JBIG2 and Illustrator procset EPS (`crates/model/src/fbx.rs`, `docs/file-format-parity.md:37-58`).
- - **Scripting gaps**: no `.jsxbin`, no Startup/Shutdown script folders, no script shortcuts, no Legacy ExtendScript engine mode, no inter-application BridgeTalk messaging (`docs/scripting-parity.md`, `docs/plugins.md`).
- - **The macOS `.app` contains no CLI** — get the universal `effectcraft-cli` from its separate release zip or build it (`docs/agents.md` §From an installed release). **The control channel has no authentication**, binds `127.0.0.1`, serves at most 16 connections, times out at 60 s and rejects lines over 4 MiB; anyone who reaches it can run commands, read the project and take screenshots (`docs/control-protocol.md`, `apps/effectcraft/src/control_server.rs:21-23,63`).
- - **Docs drift — trust code/runtime.** `README.md`, `docs/gaps.md`, `docs/architecture.md`, `docs/effects-parity.md` (306/3017) and `docs/hardware-parity.md` say 306 effects; the registry is 307/3032. `docs/target-app-parity.md:72` says 411 commands; the registry has 672. `docs/gaps.md` gap 16 says 7 panel catalogs; the code has 8, and gap 3 says no PR CI gate; `.github/workflows/ci.yml` exists. `docs/file-format-parity.md:34` still says no relinking; `footage.relink`/`file.relinkFootage` exist (`crates/engine/src/commands/relink.rs`, `crates/engine/src/commands/relink_moved.rs`).
- - **`.prproj` is not read or written** — convert to FCP7 XML in Premiere first (`crates/interchange/src/lib.rs:24-26`).
+ - **After Effects projects will not open.** No `.aep`/`.aepx` reader, no `.ffx` presets, no `.mogrt` templates, no AE SDK plug-ins. Only `.ecproj`/`.ecprojx`, `.ecpreset` and `.ectemplate` are native.
+ - **Fidelity against After Effects is mostly unmeasured.** The live comparisons (scalar ease plus Tint, Tritone, Black & White, Exposure, Levels (Individual Controls), Curves, Luma Key, Extract, 4-Color Gradient and Fill) match After Effects 2026 renders within 1–3/255, Brightness & Contrast up to 6.2/255. Everything else is self-graded — never promise AE-identical renders (`docs/fidelity/README.md`).
+ - **Alpha stability.** `docs/gaps.md` still lists open crash reports; re-check the tracker rather than quoting this skill.
+ - **No hardware video decode/encode** (VideoToolbox, NVENC, Quick Sync, VA-API). HEVC/AV1 have no B-frames, multi-reference or SAO/CDEF; Opus has no FEC/DTX.
+ - **Output/import gaps**: no 16-bpc uncompressed, AVI, MXF output, DNxHR, DPX/Targa/Photoshop sequences or ProRes RAW; no USD/USDZ, Substance `.sbsar`, camera raw, HEIF/AVIF, BRAW/R3D/ARRIRAW.
+ - **FBX/DPX/PDF caveats**: FBX is 7.x only, with no animation, skinning, cameras or lights; DPX/Cineon log values are shown as stored; PDF/AI/EPS import lacks JPX/JBIG2 and Illustrator procset EPS.
+ - **Scripting gaps**: no `.jsxbin`, no Startup/Shutdown script folders, no script shortcuts, no Legacy ExtendScript engine mode, no BridgeTalk.
+ - **The macOS `.app` contains no CLI** — get the universal `effectcraft-cli` from its separate release zip or build it. **The control channel has no authentication**, binds `127.0.0.1`, serves at most 16 connections, times out at 60 s and rejects lines over 4 MiB.
+ - **Docs drift — trust code/runtime.** Several docs still say 306 effects and 411 commands; the registries say 307/3032 and 672+1 ids. `docs/gaps.md` gap 3's "no PR CI gate" is stale.
+ - **`.prproj` is not read or written** — convert to FCP7 XML in Premiere first.
## How to operate
### Launch (macOS)
```sh
open -a EffectCraft # empty Untitled Project (not the demo)
open -a EffectCraft Project.ecproj # open a project
open -a EffectCraft --args --control 9877 --demo # agent-driven window + demo project
/Applications/EffectCraft.app/Contents/MacOS/EffectCraft --control 9877 --demo
```
- Flags: `--control <1-65535>` (or `EFFECTCRAFT_CONTROL_PORT`), `--demo`, `--empty`, `--home`, `--version`; after `--` every argument is a file (a bare file opens a project or imports media). Unknown options exit 1 before the window opens; macOS `-psn_…` is ignored (`apps/effectcraft/src/main.rs:59-105`). The app starts **empty** by default, unlike most one-shot CLI commands, which start with the demo.
+ Flags: `--control <1-65535>` (or `EFFECTCRAFT_CONTROL_PORT`), `--demo`, `--empty`, `--home`, `--version`; after `--` every argument is a file. Unknown options exit 1 before the window opens. The app starts **empty** by default, unlike most one-shot CLI commands, which start with the demo.
### CLI
- `effectcraft-cli` is a separate macOS release zip (signed/notarized) or `cargo build --release -p effectcraft-cli`; it is **not** inside the `.app`. Verify with `effectcraft-cli --version`; this pin is 0.17.0. If the launcher updates the app, re-read `effectcraft-cli commands --json`. `<comp>` is an id, name or `-` (active comp); `<layer>` is an id, `#n` or name; values are JSON (`50`, `[960,540]`, `"#ff0000"`) or bare strings.
+ `effectcraft-cli` is a separate macOS release zip (signed/notarized) or `cargo build --release -p effectcraft-cli`; it is **not** inside the `.app`. `<comp>` is an id, name or `-` (active comp); `<layer>` is an id, `#n` or name; values are JSON or bare strings.
| Subcommand | What it does |
|---|---|
| `info` | version, mode (headless/bridge), command and effect counts, project and active comp |
| `commands` / `exec --list` | list engine commands; `--filter TEXT`, `--enabled`, `--schemas` |
- | `exec <id> [json]` | run one command (`--params` or positional JSON) |
- | `run <id> [json] …` | run several in order, stopping at the first error; `--args-file F` reads the arguments from a file |
+ | `exec <id> [json]` | run one command |
+ | `run <id> [json] …` | run several in order, stopping at the first error; `--args-file F` |
| `props <comp> <layer>` | property tree with paths; `--flat`, `--time S`, `--depth N` |
- | `get` / `set <comp> <layer> <path> [value]` | read/write one property; `--time S`, `--expression E` |
- | `render-frame` | PNG frame (`frame.png` by default); `--time/--frame`, `--max-side/--scale`, `--transparent` |
+ | `get` / `set <comp> <layer> <path> [value]` | read/write one property |
+ | `render-frame` | PNG frame; `--time/--frame`, `--max-side/--scale`, `--transparent` |
| `render` | one comp (`--comp`, `--out`, codec flags) or the project queue (`--queue`); `--progress` |
| `bench` | render timings, GPU vs CPU, or `--ops` everyday-operation timings |
- | `script FILE.jsx` | run AE-style JavaScript; `--eval CODE` for inline code |
+ | `script FILE.jsx` | run AE-style JavaScript; `--eval CODE` |
| `mcp` | MCP server on stdio; `--autosave`, `--bridge PORT`, `--automation-read-root`, `--automation-write-root` |
- - **Most commands open the demo project** unless you pass `--project F.ecproj`, a positional `*.ecproj`, or `--empty`. `commands`, `script` and `mcp` start empty; MCP starts empty by default.
- - `--save` writes back to the opened project, `--save-as F` writes a copy; without either, headless edits are memory-only. `--json` prints one compact JSON document; errors become `{"error": …}` and exit 1; usage errors exit 2 before running. `--gpu` attaches the GPU compositor; CPU is the default.
- - `--autosave` is MCP-only; `--bridge PORT` drives `effectcraft --control PORT` and cannot be combined with `--project`/`--demo`; `render` refuses bridge mode.
- - `render` needs `--out` (or `--queue`); a relative `--out` is against the **working directory**, while queued outputs resolve against the project folder. `render --comp …` renders exactly one item and disables the project's other queue items; `--progress` prints `progress <done> <total> <elapsed> <left>` lines on stderr when stderr is not a terminal.
- - `render-frame` accepts `--time`, `--frame`, `--max-side`, `--scale`, `--transparent`. `--template T.ectemplate --values values.json` renders one Essential Graphics instance (values ≤ 1 MiB); it cannot combine with `--project`, `--demo`, `--bridge`, `--queue` or `--comp` (`apps/effectcraft-cli/src/template.rs`).
- - `mcp --automation-read-root DIR / --automation-write-root DIR` (repeatable) confine agent file access; scripts and UI-driving tools are refused while confined (`docs/mcp.md`).
+ - **Most commands open the demo project** unless you pass `--project F.ecproj`, a positional `*.ecproj`, or `--empty`. `commands`, `script` and `mcp` start empty.
+ - `--save` writes back to the opened project, `--save-as F` writes a copy. `--gpu` attaches the GPU compositor; CPU is the default.
+ - `render` needs `--out` (or `--queue`); `render --comp …` renders exactly one item; `--progress` prints progress lines on stderr.
+ - `render-frame` accepts `--time`, `--frame`, `--max-side`, `--scale`, `--transparent`; `--template T.ectemplate --values values.json` renders one Essential Graphics instance.
+ - `mcp --automation-read-root DIR / --automation-write-root DIR` (repeatable) confine agent file access.
### MCP
`effectcraft-cli mcp` serves JSON-RPC 2.0 over stdio, one message per line. It has **36 tools**:
| Group | Tools |
|---|---|
| Commands | `list_commands`, `describe_command`, `execute_command`, `command_list`, `command_run`, `batch`, `command_batch` |
| Project / properties | `get_project`, `doc_inspect`, `get_comp`, `get_layer`, `get_property`, `set_property`, `add_keyframe`, `add_effect`, `list_effects`, `list_fonts`, `get_state` |
| Files / history / scripting | `open_project`, `save_project`, `undo`, `redo`, `history`, `run_script`, `script_ui` |
| Render | `render_frame`, `render_preview` |
| Bridge-only (9) | `screenshot`, `ui_inspect`, `ui_elements`, `ui_click`, `ui_drag`, `ui_key`, `ui_type`, `ui_set`, `control` |
- Resources `effectcraft://document` and `effectcraft://commands` return the same JSON as `doc_inspect` and `command_list` (`crates/automation/src/server.rs:278-279`). Unknown top-level arguments return JSON-RPC `-32602` naming the accepted keys; a tool failure, or any result with `failed` > 0, sets `isError`. `batch` is atomic (one undo step, `$N.key` references); `command_batch` stops at the first error by default, each step its own undo step. A `renderQueue.render` call with `_meta.progressToken` emits progress notifications and can be cancelled; a second render is refused while one runs (`docs/mcp.md` §Progress and cancellation).
+ Resources `effectcraft://document` and `effectcraft://commands`. `batch` is atomic (`$N.key` references); `command_batch` stops at the first error by default. A `renderQueue.render` call with `_meta.progressToken` emits progress notifications and can be cancelled.
### Control channel
- `effectcraft --control 9877` accepts one JSON object per line, `{"id":1,"method":"engine.execute","params":{...}}`, and replies `{"id":1,"ok":true,"result":...}`. **24 methods**: engine/menus (`engine.execute`, `ui.menu.invoke`, `engine.commands`, `ui.menu.list`), render (`render.frame`), inspect (`ui.inspect`, `ui.elements`, `ui.set`, `ui.screenshot`), input (`ui.click`, `ui.move`, `ui.drag`, `ui.scroll`, `ui.key`, `ui.type`), panels/playback (`ui.panel.show`, `ui.panel.close`, `ui.playback`, `ui.resize`, `ui.focus`), viewer/timeline (`ui.viewer.locate`, `ui.viewer.hit`, `ui.timeline.locate`) and `app.quit`. `engine.execute` runs engine commands exactly as headless; UI-only ids and `ui.menu.invoke` go through the menu dispatcher (some open dialogs). `ui.elements` lists widgets drawn in the last frame `{id,label,rect}`; `ui.screenshot` returns a PNG.
+ `effectcraft --control 9877` accepts one JSON object per line and replies per line. **24 methods**: engine/menus (`engine.execute`, `ui.menu.invoke`, `engine.commands`, `ui.menu.list`), render (`render.frame`), inspect (`ui.inspect`, `ui.elements`, `ui.set`, `ui.screenshot`), input (`ui.click`, `ui.move`, `ui.drag`, `ui.scroll`, `ui.key`, `ui.type`), panels/playback (`ui.panel.show`, `ui.panel.close`, `ui.playback`, `ui.resize`, `ui.focus`), viewer/timeline (`ui.viewer.locate`, `ui.viewer.hit`, `ui.timeline.locate`) and `app.quit`. `ui.elements` lists widgets drawn in the last frame `{id,label,rect}`; `ui.screenshot` returns a PNG. **Edit-menu keys now edit a focused text field** (docked or in a panel window).
### Document model and workflow
- One project per app process in one main window; compositions open as tabs and are restored with the project. Panels follow After Effects (Project, Composition, Timeline, Effect Controls, Properties, Effects & Presets, Character, Paragraph, Align, Info, Preview, Audio, Render Queue and more), dock/float, and any panel can move to its own native window. Each layer carries a property tree addressed by paths such as `transform/position`, `effects/#1/blurriness` or `@<uid>`.
+ One project per app process in one main window; each open composition is a tab in the timeline group's tab row. Panels follow After Effects, dock/float on craft-ui's shared docking, and any panel can move to its own native window. Each layer carries a property tree addressed by paths such as `transform/position`, `effects/#1/blurriness` or `@<uid>`.
- 1. Create a composition (`comp.new`) and add layers (`layer.new*`, `file.import`, `layer.replaceSource` to swap a source).
- 2. Animate (`prop.set`/`prop.addKey`; `timeBase: "comp"` for comp seconds on offset or stretched layers) and ease (`keys.select` + `keys.easyEase` in one session).
+ 1. Create a composition (`comp.new`) and add layers (`layer.new*`, `file.import`, `layer.replaceSource`).
+ 2. Animate (`prop.set`/`prop.addKey`) and ease (`keys.select` + `keys.easyEase`).
3. Apply effects (`effect.apply`), edit `effects/#1/...`, preview and save.
4. Export via `renderQueue.add` + `renderQueue.render`, or Lottie/timeline/template commands.
```sh
effectcraft-cli exec comp.new '{"name":"Main","width":1920,"height":1080,"frameRate":30,"duration":3}' --empty --save-as main.ecproj
effectcraft-cli exec layer.newText '{"text":"Hello","name":"Title","size":160,"fill":"#ffffff"}' main.ecproj --save
effectcraft-cli run main.ecproj \
prop.addKey '{"layer":"Title","path":"transform/position","time":0,"value":[-400,540]}' \
prop.addKey '{"layer":"Title","path":"transform/position","time":1,"value":[960,540]}' \
keys.select '{"keys":[{"layer":"Title","path":"transform/position","time":0},{"layer":"Title","path":"transform/position","time":1}]}' \
keys.easyEase '{"which":"both"}' --save
effectcraft-cli render main.ecproj --comp Main --out title.mp4 --format h264 --progress
```
- Each headless CLI invocation is a new session: edits survive only if saved, and editor state such as key selection never does; group selection-dependent steps in one `run` (or one MCP session).
+ Each headless CLI invocation is a new session: edits survive only if saved; group selection-dependent steps in one `run` (or one MCP session).
### Open / save / export, exact formats
- - **Open** `.ecproj`, `.ecprojx`, or `.xml` whose root is `EffectCraftProject`; `file.importProject` merges another project instead. **Save** `.ecproj`, or XML when the path is `.ecprojx`/`.xml`; Save a Copy, Save a Copy As XML and Increment and Save exist (`crates/engine/src/commands/file.rs:43-96,736-760`).
- - **Export** the 13 Render Queue formats, single-frame layered PSD or ProEXR, Lottie `.json`/`.lottie`, timeline formats, `.ectemplate`, `.ecpreset`. A `.xml` path is ambiguous: `file.open` expects an EffectCraft XML project, while `file.importTimeline` expects FCP7 XML — use `.ecprojx` for projects.
+ - **Open** `.ecproj`, `.ecprojx`, or `.xml` whose root is `EffectCraftProject`; `file.importProject` merges another project. **Save** `.ecproj`, or XML when the path is `.ecprojx`/`.xml`; Save a Copy, Save a Copy As XML and Increment and Save exist.
+ - **Export** the 13 Render Queue formats, single-frame layered PSD or ProEXR, Lottie, timeline formats, `.ectemplate`, `.ecpreset`. A `.xml` path is ambiguous: `file.open` expects an EffectCraft XML project, `file.importTimeline` expects FCP7 XML — use `.ecprojx` for projects.
- **Rejected**: `.prproj` and every AE-native format above.
### Keyboard shortcuts (only verified ones)
- `Cmd+N` New Composition (`crates/engine/src/commands/comp.rs:281`), `Cmd+K` Composition Settings (`crates/engine/src/commands/comp.rs:290`), `Cmd+Shift+X` Trim Comp to Work Area (`crates/engine/src/commands/comp.rs:296`), `Cmd+Alt+N` New Project, `Cmd+O` Open, `Cmd+S` Save, `Cmd+Alt+Shift+S` Increment and Save, `Cmd+Shift+S` Save As, `Cmd+I` Import (`crates/engine/src/commands/file.rs:736-747`), `Cmd+M` / `Cmd+Shift+/` Add to Render Queue (`crates/engine/src/commands/render_queue.rs:1001`), `F9` Easy Ease / `Shift+F9` in / `Cmd+Shift+F9` out, `Cmd+Alt+K` Keyframe Interpolation, `Cmd+Shift+K` Keyframe Velocity, `Cmd+Alt+H` Toggle Hold (`crates/engine/src/commands/prop.rs:1369-1389`), `Cmd+Alt+T` Enable Time Remapping (`crates/engine/src/commands/layer_time.rs:253`), `Cmd+G`/`Cmd+Shift+G` Group/Ungroup Shapes (`crates/engine/src/commands/paths.rs:529-530`), `Cmd+Alt+;` Settings (`crates/engine/src/menus.rs:492`), `Cmd+Opt+/` Replace Source with the selected Project item (`docs/footage.md:123-131`). Property reveals `A/P/S/R/T/M/F/E/L/U`, their double presses and `Alt+Shift+A/P/S/R/T` keying are in `docs/agents.md` §Timeline. Shortcuts are command data and can be listed/remapped with `shortcuts.list`/`shortcuts.set`; for anything else read `effectcraft-cli commands --json` — do not invent shortcuts.
+ `Cmd+N` New Composition, `Cmd+K` Composition Settings, `Cmd+Shift+X` Trim Comp to Work Area, `Cmd+Alt+N` New Project, `Cmd+O` Open, `Cmd+S` Save, `Cmd+Alt+Shift+S` Increment and Save, `Cmd+Shift+S` Save As, `Cmd+I` Import, `Cmd+M` Add to Render Queue, `F9` Easy Ease, `Cmd+Alt+K` Keyframe Interpolation, `Cmd+Shift+K` Keyframe Velocity, `Cmd+Alt+H` Toggle Hold, `Cmd+Alt+T` Enable Time Remapping, `Cmd+G`/`Cmd+Shift+G` Group/Ungroup Shapes, `Cmd+Opt+/` Replace Source. Property reveals `A/P/S/R/T/M/F/E/L/U` are in `docs/agents.md` §Timeline. Shortcuts are command data; for anything else read `effectcraft-cli commands --json` — do not invent shortcuts.
## Working with an AI agent
### Choose the surface
- - **Headless (preferred)**: `effectcraft-cli exec/run` for one-shots or `effectcraft-cli mcp` for a persistent session. Headless MCP starts empty; add `--demo` or `--project`; `mcp --autosave` checkpoints dirty work after each mutating call (`docs/agents.md` §MCP setup).
- - **Live app**: start `effectcraft --control 9877`, then `mcp --bridge 9877` or the raw control channel; bridge mode adds `screenshot`/`ui_*` to MCP, while the CLI has no UI-input tools (its `exec` can still invoke menu-style ids through the app). **Web**: no TCP channel; use `window.effectcraft` (`docs/web.md:387-399`).
+ - **Headless (preferred)**: `effectcraft-cli exec/run` for one-shots or `effectcraft-cli mcp` for a persistent session; `mcp --autosave` checkpoints dirty work after each mutating call.
+ - **Live app**: start `effectcraft --control 9877`, then `mcp --bridge 9877` or the raw control channel. **Web**: no TCP channel; use `window.effectcraft`.
### Prepare inputs
- Convert unsupported sources: camera raw/AVI/BRAW/R3D/USDZ/Substance → H.264/ProRes/WebM or PNG/EXR/TIFF sequences; 3D → glTF/GLB/OBJ/STL/FBX 7.x; audio → WAV/AIFF; data → JSON/CSV/TSV.
- - Image sequences: pass one numbered file with `sequence: true` (or import the folder) so the run is one footage item; missing numbers become colour bars. Keep media next to the project: relative paths resolve against the project folder on open (`docs/footage.md`, `crates/engine/src/doc_path.rs`).
- - Query `list_fonts`/`text.fonts` first and use a listed family and style (variable fonts expose named instances and accept PostScript names); extra fonts come from `EFFECTCRAFT_FONT_DIRS`.
+ - Image sequences: pass one numbered file with `sequence: true` (or import the folder); keep media next to the project (relative paths resolve against the project folder on open).
+ - Query `list_fonts`/`text.fonts` first; extra fonts come from `EFFECTCRAFT_FONT_DIRS`.
### Inspect and repair project files
- - `.ecproj` is documented by the code, not a public spec: `savedBy`, `schema`, `activeComp`, `openComps`, `settings`, `items`, `nextId`, `renderQueue` (`crates/project/src/lib.rs:35,54-80,1601-1640`). Item, layer and property ids share one counter; keep `nextId` above every id. Never bump `schema`; a newer schema is warned about rather than rendered wrongly.
- - Prefer commands over JSON surgery: `info`, `props`, `get` and `exec` validate through the same reader the app uses. After a hand edit, reopen and check the `repaired` list in `file.open`'s result (`crates/engine/src/commands/file.rs:49-70`).
- - For moved media: `footage.missing`, `footage.relink {item, path}` (also relinks other missing files in the same place) or `file.relinkFootage {folder, dryRun}`; do not rewrite paths blindly (`crates/engine/src/commands/relink.rs`, `crates/engine/src/commands/relink_moved.rs`). `.ectemplate` (ZIP) and `.ecpreset` (tagged JSON) are inspectable, but re-export through the app after edits.
- - `layer.revealSource` is the safe way to find a layer's Project item in the UI; text/shape/null/camera/light/3D-primitive layers have none and it refuses them. Headless it returns the item ids instead (`crates/engine/src/commands/layer_menu.rs:771-782`).
+ - `.ecproj` is documented by the code: `savedBy`, `schema`, `activeComp`, `openComps`, `settings`, `items`, `nextId`, `renderQueue`. Item, layer and property ids share one counter; keep `nextId` above every id. Never bump `schema`.
+ - Prefer commands over JSON surgery; after a hand edit, reopen and check the `repaired` list in `file.open`'s result.
+ - For moved media: `footage.missing`, `footage.relink {item, path}` or `file.relinkFootage {folder, dryRun}`.
+ - `layer.revealSource` is the safe way to find a layer's Project item in the UI.
### Scripting
- - Scripts run against the AE object model; `run_script`/`script` keep edits (wrap in `app.beginUndoGroup()`/`endUndoGroup()` to undo them all). The CLI `script` exits 1 with `file:line:col`; MCP `run_script` returns the error object.
- - Reads are allowed under the script's own folder and the project folder; writes and network require Settings ▸ Scripting & Expressions ▸ Allow Scripts to Write Files and Access Network (`crates/script/src/scriptfile.rs:41-50`).
- - Setting an array property to the wrong length throws `Array is not of the correct dimension`; two values are legal for a 3D property (`z = 0`), matching After Effects (`crates/script/src/prelude.js:876`). Pad or trim arrays in generated scripts.
+ - Scripts run against the AE object model; `run_script`/`script` keep edits (wrap in `app.beginUndoGroup()`/`endUndoGroup()`). The CLI `script` exits 1 with `file:line:col`.
+ - Reads are allowed under the script's own folder and the project folder; writes and network require Settings ▸ Scripting & Expressions.
+ - Setting an array property to the wrong length throws `Array is not of the correct dimension` — pad or trim arrays in generated scripts.
### Verify exports
- Read the result: `renderQueue.render` returns per-item statuses; CLI `render` exits 1 if any item failed.
- - Re-import the output and render a frame (`file.import`, `render_frame`) to prove it decodes; use `--transparent`/`transparent: true` for alpha.
- - Lottie: inspect `warnings`; timeline: inspect `warnings`, `missing` and `prerendered` (`prerender: none` omits layers a timeline cannot represent, `unsupported` (default) pre-renders those to ProRes 4444, `all` pre-renders every visual layer).
- - Odd comp sizes: H.264/HEVC dimensions round down to even (`crates/project/src/render_queue.rs:599-603`).
+ - Re-import the output and render a frame to prove it decodes; use `--transparent` for alpha.
+ - Lottie: inspect `warnings`; timeline: inspect `warnings`, `missing` and `prerendered`.
+ - Odd comp sizes: H.264/HEVC dimensions round down to even.
### Do not
- - Try to open `.aep`/`.aepx`/`.aex`/`.ffx`/`.mogrt`/`.prproj`; there is no reader. Convert timelines to FCP7 XML in Premiere first; expect only `.wasm` plug-ins, `.ecpreset` and `.ectemplate`.
- - Drive the GUI without `--control` (no UI-scripting fallback; discover widgets with `ui.elements`), edit a `.ecproj` the app has open (it will overwrite your changes), or expose the control port (no authentication).
+ - Try to open `.aep`/`.aepx`/`.aex`/`.ffx`/`.mogrt`/`.prproj`; there is no reader.
+ - Drive the GUI without `--control` (discover widgets with `ui.elements`), edit a `.ecproj` the app has open, or expose the control port (no authentication).
### Concurrency and safety
- Save early; `open_project`/`file.newProject` replaces a dirty project without a prompt. Without `--autosave`, MCP work is memory-only.
- - After a broken bridge connection, mutating calls have an **unknown outcome**; inspect before retrying. `batch` is atomic and rolls back on a failing step; `command_batch` stops at the first error by default.
- - Scripts may read files only in their own or the project's folder and cannot write or use the network unless Settings ▸ Scripting & Expressions allows it; confine agent file access with `--automation-read-root` / `--automation-write-root`.
+ - After a broken bridge connection, mutating calls have an **unknown outcome**; inspect before retrying.
+ - Scripts may read files only in their own or the project's folder; confine agent file access with `--automation-read-root` / `--automation-write-root`.
## Verification protocol
Alpha-software discipline; run this for every delegated task.
1. **Copy first.** Work on a scratch `.ecproj` (`--save-as` / Save a Copy), never the only copy; update the app only between projects.
- 2. **Round-trip in a fresh process.** New → comp/layers/keyframes/effects → save → reopen via `info --project` → render a frame and a short movie. A save that cannot be read back is a bug; report it.
- 3. **Check both artifacts.** A `render-frame` PNG at a known time and the final MP4/MOV/sequence; confirm dimensions, duration/frame count, audio and alpha, and for sequences every `#####` frame. Read `renderQueue.render` statuses; check the output file exists and is non-zero.
- 4. **Check the time span.** Queued items default to **Work Area**; CLI `render` defaults to the whole comp unless `--work-area`. An empty render is usually this, not a codec failure (`crates/project/src/render_queue.rs:368`).
+ 2. **Round-trip in a fresh process.** New → comp/layers/keyframes/effects → save → reopen via `info --project` → render a frame and a short movie.
+ 3. **Check both artifacts.** A `render-frame` PNG at a known time and the final MP4/MOV/sequence; confirm dimensions, duration, audio and alpha.
+ 4. **Check the time span.** Queued items default to **Work Area**; CLI `render` defaults to the whole comp unless `--work-area`.
5. **Check format constraints.** H.264/HEVC even dimensions; ProRes 4444 for alpha; WebM VP9 for VP9 alpha; WAV/AIFF are 16-bit PCM; EXR is 32-bit float.
- 6. **Interchange and Lottie:** inspect warnings/missing/prerendered, re-import the result, and open Lottie in an independent player if available. Record versions and `savedBy`.
+ 6. **Interchange and Lottie:** inspect warnings/missing/prerendered, re-import the result.
7. **Report, don't hide.** Give the exact command, version, file and observed result. Fidelity differences against After Effects are expected and are the most useful report.
## Sources & evidence
- - Repository: `https://github.com/storytold/effectcraft`; inspected tag `v0.17.0`, commit `362474a843ddb67555d4c2d19ad6a590388aad0e` (`git rev-parse HEAD`, 2026-10-26). Fast-moving alpha — verify against your installed version (`effectcraft-cli --version`, and `EffectCraftBuildCommit` in `/Applications/EffectCraft.app/Contents/Info.plist`).
- - Installed app at this pin: version 0.17.0, bundle `ai.storyteller.effectcraft`, build commit `362474a843ddb67555d4c2d19ad6a590388aad0e`, minimum macOS 11.0; `Contents/MacOS` holds only the app binary (no CLI). No GUI was launched; only `--version` was run.
- - Read: `README.md`, `Cargo.toml`, `ROADMAP.md`, `AGENTS.md`, `.github/workflows/ci.yml`; the `docs/` set (`agents`, `mcp`, `control-protocol`, `architecture`, `file-format-parity`, `gaps`, `target-app-parity`, `preferences`, `footage`, `plugins`, `scripting-parity`, `web`, `effects`, `effects-parity`, `fidelity/README`); the CLI and app entry points (`apps/effectcraft-cli/src/{main,template}.rs`, `apps/effectcraft/src/{main,control_server}.rs`); `crates/project/src/{lib,render_queue,styles,limits}.rs`; the engine command modules cited above; `crates/{media,interchange,lottie,expr,keyframe,plugin,automation,script}/src`, `crates/effects/src/{lib,catalog,crypto_select}.rs`, `crates/color/src/blend.rs`, `crates/ui-egui/src/{control,i18n,dock,menus}.rs`, `crates/engine/presets/*.json`; source, generated docs, tests and bundle metadata only. Counts were re-measured from code (and, for the change list, by comparing the `v0.15.0` tree with this one); no build, test or render was run.
+ - Repository: `https://github.com/storytold/effectcraft`; inspected tag `v0.18.0`, commit `c3600f9b59b109da97871a43a6898683a0163cb1` (`git rev-parse HEAD`, 2026-10-29). Fast-moving alpha — verify against your installed version.
+ - Installed app at this pin: version 0.18.0, bundle `ai.storyteller.effectcraft`; `Contents/MacOS` holds only the app binary (no CLI). No GUI was launched; only `--version` was run.
+ - Read: `README.md`, `Cargo.toml`, `docs/` (`agents`, `mcp`, `control-protocol`, `architecture`, `file-format-parity`, `gaps`, `effects`, `effects-parity`, `fidelity/README`, `footage`, `plugins`, `scripting-parity`, `web`); `apps/effectcraft-cli/src/main.rs`, `apps/effectcraft/src/{main,control_server}.rs`; `crates/project/src/{lib,render_queue,styles,limits}.rs`; `crates/engine/src/cmd.rs` (registry) and `crates/engine/src/cmd/`; `crates/{media,interchange,lottie,expr,keyframe,plugin,automation,script}/src`; `crates/effects/src/{lib,catalog}.rs`; `crates/color/src/blend.rs`; `crates/ui-egui/src/{control,commands,dock,menus}.rs`. Counts were re-measured from code and diffed against a fetched `v0.17.0` ref; no build, test or render was run.
+ - Delta method: `cmd!`/`query!` call sites 697 (v0.17.0) → 678 (v0.18.0) after the shared-module refactor; unique literal ids 563 → 564 (+`text.deleteSurrounding`, nothing removed); `static TOOLS` `ToolDef` entries 36 → 36; `docs/effects.md` 307/3032 unchanged (test-enforced).