aectl
v1.1.0
Published
Drive Adobe After Effects from the shell or an AI agent: lint + run ExtendScript as one undo step, dump comps, render frames to contact sheets.
Maintainers
Readme
aectl: drive After Effects from the shell (and from your AI agent)
A small CLI (ae) and an ExtendScript library (lib.jsx) for scripting a running Adobe After Effects on macOS.
It is built for coding agents such as Claude Code, but works just as well by hand.
ae sel # what the user selected, and where the playhead is
ae tree # every comp, plus the layers of the active one
ae dump "Main" --layer "Title" # transforms, keys with easing, expressions, text
ae run retime.jsx --diff # lint, run as ONE undo step, print the log and what changed
ae snap "Main" 0-120:30 --sheet # render frames to PNG + a labelled contact sheet
ae graph "Main" "Title" pos # value + speed curves: see the easingWhy
- Nothing to install inside AE. No CEP panel, no bridge to keep open. Scripts go through
osascripttoDoScriptFile, so AE only has to be running. - Lint before AE sees the script. ExtendScript is ES3. A stray
let,=>or{in: 1}opens a modal error dialog that blocks AE until someone clicks it.ae runparses the script against the rules of the real engine (checked in AE 2026), catches typos likeAE.setTxt("did you mean AE.setText?"), and refuses to send a broken script. As a Claude Code plugin it also lints every.jsxthe agent writes, right after it is written. - Safe edits. Every run is one Cmd+Z step, with dialogs suppressed and errors caught. Errors are reported with the line in your file.
- Visual feedback.
ae snaprenders frames to PNG and waits until the files are complete.--sheettiles them into one image that an agent (or you) can look at.ae graphdraws the motion curves of a property. - Works with you, not around you.
ae selreads your selection and playhead,ae markersreads the notes you leave as markers, andae run --diffshows exactly what a script changed. - A tutor in your timeline. Ask the agent to teach you instead of doing the work: it leaves the tasks as markers where they happen, checks what you did, and hints before it takes over. See below.
- Helpers for the parts AE gets wrong. Trims that silently don't stick, Source Text read through a typewriter
expression, keyframe ease arrays of the wrong length,
"x" + arraythrowing. See the pitfalls in the reference.
Install
Requirements: macOS, After Effects (tested with 2026), node 22+, ffmpeg (brew install ffmpeg) for the image and video tools.
As a Claude Code plugin (the agent also gets the full API reference as a skill):
/plugin marketplace add thdctrt/aectl
/plugin install aectl@aectlFrom npm (puts ae and aectl on your PATH):
npm install -g aectlTo get the lint hook without the plugin, add this to ~/.claude/settings.json:
{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit|MultiEdit", "hooks": [ { "type": "command", "command": "ae hook" } ] } ] } }Install it once instead of calling it through npx: an agent makes many calls, and npx resolves the package on every one.
From git:
git clone https://github.com/thdctrt/aectl && ln -s "$PWD/aectl/ae" /usr/local/bin/aeThen run the check. It tells you exactly what to fix:
ae doctor # node, ffmpeg, the AE scripting pref, macOS Automation permission, a round trip to AE
ae selftest # 76 checks + a rollback round trip in a throwaway comp, cleaned up afterwardsTwo things usually need a one-time click:
- In AE: Settings > Scripting & Expressions > Allow Scripts to Write Files and Access Network. The CLI reads the log file your script writes.
- macOS asks whether your terminal may control After Effects. Allow it (System Settings > Privacy & Security > Automation).
zsh completion (commands, options, .jsx and video files, and comp/layer names from the running AE):
echo 'eval "$(ae completion zsh)"' >> ~/.zshrcWriting a script
// retime.jsx: ae run retime.jsx
AE.run("Retime intro", function (log) {
var c = AE.comp("Main");
var L = AE.layer(c, "Title");
AE.trim(L, 144, 194); // comp frames; WARNs if AE won't keep the trim
AE.setText(L, "Hello world", "#806600"); // keeps font/size, safe with typewriter expressions
log("now", AE.span(L));
AE.snap(c, [150, 190]); // ae run waits for the PNGs
});Everything is in skills/after-effects/SKILL.md: every command and option, the
full AE.* API, and the pitfalls. It doubles as the skill that tells an agent how to use the tool.
Learning After Effects with your agent
An agent that can see and edit your project can also teach you, in your own timeline. Ask it to ("teach me easing", "show me how parenting works, I want to do it myself") and it turns the lesson into markers: a short task at the frame and on the layer where it happens, which you then do by hand.
$ ae markers "Practice"
COMP 'Practice' id=12
f0 #1 'Box' 'try 1/3: select both Position keys (P), make them ease' label=9
f45 #1 'Box' 'try 2/3: make it land softer (Graph Editor)' label=9
f55 comp 'try 3/3: start the move 10 frames later' label=9- It works on your project, or on a practice comp it builds with the problem already in it: a box on linear keys to ease, a title to parent to a null, a layer to trim to the beat.
- It checks what you did. When you say "done", it compares the layer with how it was before the step (
ae dump), reads the ease you made (ae graph: peak/avg 1.00 means still linear), looks at the frame (ae snap) and tells you what worked and what is off. - Hints come in steps: first the goal, then where to look (panel, property, shortcut), then the exact clicks. Only
if you ask does it do the step itself, with a before/after sheet (
ae run --ab), and Cmd+Z lets you try again. - It works both ways. Leave a marker starting with
?("? why does it jump here") and ask the agent to read your markers: it looks at that frame and explains. To learn from someone else's template, ask for a tour: it leavesnotemarkers on the interesting parts (an expression, a control layer, a precomp) and walks you through them. - Nothing sticks around. The lesson markers share one prefix and label, and the agent removes them (and the practice comp) at the end. It never saves the project.
This is a separate skill, after-effects-tutor, installed with the plugin. Only
its one-line description sits in the agent's context until you ask to learn, so it costs nothing in other sessions.
Commands
| command | what |
|---|---|
| ae run script.jsx | lint, run as one undo step, print the log, wait for PNGs; exit 1 on ERR; a run that throws is undone |
| ae eval 'js' | run a one-liner; the value of the last expression is printed |
| ae check script.jsx | syntax + ExtendScript lint only, no AE needed |
| ae run edit.jsx --diff / --ab "Comp" --frames 0,60 | also print what changed in the project / a before-after contact sheet |
| ae sel | the active comp, playhead frame, work area, selected layers, properties and keyframes |
| ae tree / ae dump "Comp" | project overview / one comp in detail (read-only) |
| ae markers / ae mark "Comp" 120 "note" | read comp and layer markers (notes from the user) / add one |
| ae health / ae find "text" | missing footage and fonts, expression errors, off-screen layers, text past the edge / search names, text, expressions, files, effects, markers |
| ae effects blur | installed effects with their matchNames |
| ae snap "Comp" 0-90:15 --sheet | render frames to PNG, optionally as a contact sheet or cropped to a region (--crop) |
| ae graph "Comp" "Layer" "Transform/Position" | value and speed curves as a PNG, plus the numbers of each ease |
| ae sheet out.png a.png b.png | contact sheet from any images |
| ae measure a.png [b.png] | ink bounding box, centre and mean colour of an image; with two, how far b is off from a |
| ae frames video.mp4 / ae probe video.mp4 | sample a clip into a sheet / size, fps (flags VFR), duration |
| ae beats "Comp" --layer "Music" | music accents (onsets) in the comp's frames, to key animation to the beat; --mark adds them as markers |
| ae export "Comp" --preset youtube-1080 | render and encode for YouTube, Shorts/Reels, web, ProRes, alpha, GIF (--list); --ame uses Media Encoder; --quick takes snapped frames for a fast preview |
| ae save [--backup] | save the project (only when you mean it) |
| ae doctor / ae selftest | check the setup / run the self-test |
| ae completion zsh | print the zsh completion script |
| ae mcp | the same tools as an MCP server (see below) |
Exit codes: 0 ok, 1 the script logged ERR, 2 usage or lint error, 3 AE not running or timeout (usually a modal dialog in AE).
MCP server (Claude Desktop and other clients without a shell)
Claude Code needs nothing more than the plugin: it calls the CLI. Clients that cannot run shell commands on your Mac
(the Claude Desktop chat, other MCP-capable agents) can use ae mcp, an MCP server over stdin/stdout. It is part of
the same CLI: no extra package, no network, nothing fetched at start. The commands above are tools (ae_run,
ae_snap, ae_selection, ...); ae_snap, ae_graph and ae_run with ab_comp return their images directly, and
ae_docs hands the model the full reference.
In Claude Desktop: Settings > Developer > Edit Config, then add (absolute paths: GUI apps start servers with a minimal
PATH; which node and the path to your ae give them):
{
"mcpServers": {
"aectl": { "command": "/opt/homebrew/bin/node", "args": ["/path/to/aectl/ae", "mcp"] }
}
}With a global npm install the second path is $(npm root -g)/aectl/ae. Restart Claude Desktop. The first call makes
macOS ask whether Claude may control After Effects: allow it (System Settings > Privacy & Security > Automation).
Limitations
- macOS only: the transport is AppleScript. Windows would need
AfterFX.exe -rand a port of the CLI. - Tested on After Effects 2026 (26.x). Older versions probably work, but some pitfalls are version-specific.
- AE runs one script at a time, and a modal dialog in AE blocks everything until it is closed.
Development
The CLI is TypeScript in src/, bundled into dist/ae.mjs. pnpm install && pnpm check type-checks, runs the
tests (vitest) and verifies that the committed dist/ is up to date. See AGENTS.md.
