BetterEdits app iconBetterEdits
Manual
Markdown

Command line

The same tools the MCP server serves, for scripts and for trying things by hand, with their options and one example each.

The command line is the app's own binary run with cli. Agents use the MCP server instead (see Connect an agent); the command line is the same tools for people and scripts, and the one place a person sees the whole list. It is part of Pro: outside a few commands, a call is refused until this Mac is signed in and entitled.

Running commands

/Applications/BetterEdits.app/Contents/MacOS/BetterEdits cli <command> [value…] [--option value…]
  • Commands talk to the running app over a Unix socket at ~/Library/Application Support/BetterEdits/cli.sock. If the app is not running the command launches it in the background and waits up to 20 seconds.
  • Positional values fill a command's required options in order, then its listed positionals: project-open Hook is project-open --project Hook.
  • A bare --flag means true. --key=value works too. Numbers and true/false are typed; everything else is text.
  • Relative paths resolve from the folder you run in.
  • help lists every command; help <command> or <command> --help prints its options.
  • A failing command prints why and exits 1. Exit 2 means the app could not be reached.
  • Every command that changes the video is an undo step in the app. Each shell (and each agent session) has a current video of its own: project-open sets it, and a shell that has none starts on the video whose window is in front. With none at all, the commands that need a video are refused until project-open or project-new. --project <name> on any command acts on that video instead, in the background, without changing the current one.
  • Without Pro, every command outside status, help, guide, getting-started, lint, login, logout and account is refused with how to sign in or start the trial. status says so first (pro, pro_message).

Settings › Agent integration shows the invocation with a copy button. BETTEREDITS_NO_CLI=1 in the app's environment keeps the socket closed.

Refs and times

WhatHow to write it
Tracksv1, v2 (video, top first), a1 (audio), t1 (text), s1 (subtitles); a track's name; or its 8-character id
Clipsv1:2 is the second clip on v1 in time order; or the clip's 8-character id from timeline
Subtitle barss1:3 or the bar id
AssetsThe file's display name (its filename without extension), its id, or its path
TimesSeconds (4.5), 1:02.5, end, playhead

Every clip and caption bar also carries an id in the document (its first key; the agent may name a new one, such as hook, and the app assigns the rest), and every command that takes a clip or a bar takes that id. A time in the document can point at one instead of being worked out: "hook.end", "hook.start", "a3@23.38" (the moment second 23.38 of a3's file plays) or "end" (the end of the picture), each with an optional offset ("hook.end-0.2"), in a clip's at and until (where it ends) and a bar's s and e. lint and project-commit say what each became; only "until": "end" stays live, so music keeps ending with the picture. Times are whole ticks of 1/600 s, which every common frame rate lands on. A track is its name, a file its name.

The document

CommandOptionsDoes
project-getThe open video as one JSON document: project, timeline, styles, plus revision, computed (every clip with its start and end) and reference (the rules)
project-commit[file], --project, --timeline, --styles, --revision, --actionApply an edited document as one undo step. Give the parts as JSON text, or a file holding them (the shape project-get prints); a part left out keeps what the app has. A stale revision or a mistake is refused with every problem and its place, and nothing changes
lint[name], --project, --timeline, --styles, --fileThe same check without applying: every error and warning at once. Without a document, the open video's files, or a workspace project by name or folder
be project-get > edit.json
# change edit.json
be project-commit edit.json --revision 12
be lint --timeline '{"tracks":[…]}'

be stands for the full command above. There is no be on your Mac. The document's shape is in the guide (guide) and in reference; see How an agent edits. Colour work is part of it: filter, intensity and adjust on a media clip, and adjustment layers, tracks of "kind": "filter" whose clips colour everything beneath them (guide filters; see Filters).

Read

CommandOptionsDoes
getting-startedBetterEdits explained for someone new to it: the workspace, videos, media folders, how the agent edits, analysis, and a first exercise. The agent reads it and teaches step by step. Works without Pro
statusYour current video: name, folder, revision, duration, playhead, selection, tracks with clip counts, shown (a window is up); the workspace; other_open, the other videos open, and in_front; open, false when no video is yours (project-open one); whether this Mac has Pro; and the analysis queue, with credits_needed (what the files still waiting are estimated to cost) and credits_left (your balance); app, the version that is answering, and for_agents, what changed for agents in the last releases
timeline--track, --detail (summary or full)Every track with every clip and bar as short rows (ref, id, start, end, in, what), or with --detail full each as an object (ref, id, start, end, source in and out, text and position)
mediaThe media files the video uses, with copied: true for files copied into media/, missing: true for files gone from disk, analysis and words for files that have been analysed, and status for every file: analysed, queued, analysing, not_analysed (a library file waiting for your ok, or a file that arrived a moment ago) or failed (with error). Whether the queue runs, is paused or held is status's own analysis, never a file's. A file you dropped on the video by hand is listed with registered: false until media-import registers it (nothing is copied again)
probe--path (required)A file on disk before it is imported: kind, duration, size, frame rate, codec, sound, HDR, and a warning when the analysis cannot read it (VP9 and AV1, what downloads from the web often are: fetch H.264 instead)
favoritesThe text favorites and caption styles a document can name, with font, size and colors
guide[topic]What an agent reads. Alone: what the app is, the analysis rule, the work loop, what the app does not do, and the list of topics. With a topic, that part of the manual: document, footage, cut, sound, captions, graphics, graphics-css (the scene format beside the CSS it corresponds to, every built-in motion as keyframes), export, workflows, clone-reel, tools; all is the whole of it. With a tool's name, that tool's notes and options
levels--path (required), --every, --silence, --min_silenceA file's loudness over time (RMS in dBFS per step) and its silences, from the sound alone
help[command]The command list, or one command's options
be probe ~/Footage/talk.mov
be timeline --track v1

Workspace and videos

CommandOptionsDoes
workspace[path], --aboutShow the workspace, or set one up at a path (a .betteredits package) and switch to it; --about says what gets made there and sets the canvas new videos start with
media-folders--add, --remove, --consolidate, --force, --files, --folderThe workspace's libraries, each with how many files it holds and how many are analysed, queued, analysing or failed; --files lists the files with their analysis status and words. --add asks the person in the app, with the count, the length and the credits, and adds the folder only if they say so; everything in it, now and later, is then analysed. --remove stops its analysis
projectsThe videos in the workspace, newest first, each with how it stands: open, shown, current (yours), clients, exporting, agent
project-new[name], --brief, --canvas, --structure, --showStart a video in its own folder, with a brief, a canvas (9:16, 16:9, 1:1, 4:5) and a built-in structure (talking-head, tutorial, montage, before-after, listicle). It becomes your current video, in the background unless --show
project-open--project (required), --showMake a video yours, by name or folder: opened in the background if it is not open, joined if it is; --show puts a window on it
project-closeLet go of your current video; it leaves memory unless a window or another client holds it
be project-new "Hook v2" --structure tutorial
be media-folders --remove ~/Footage/June --consolidate

Media

CommandOptionsDoes
media-import--paths (required, several bare values work)Register files with the video without placing them; the document can then name them. Files outside the video and its libraries are copied into the video, with their analysis, and analysed by themselves when they have none
media-relinkasset, pathPoint an asset (a missing one, say) at another file; every clip of it follows, cut to the file's length if shorter
alpha-clipasset (optional)The titles and graphics rendered from HTML in this video: name, size, length, description. With a name, that clip's page as well
alpha-clip-create--name, --duration (required); --html or --file; --fps (30), --width, --height (the canvas), --scale (1 or 2), --description (required for a new clip: what it shows, which is how it is found again), --replaceRender a page of HTML into a clip with a transparent background, in the video's media/ folder, and register it as media. The page's animations are stepped frame by frame, never played, so the clip is the same on any Mac. --replace renders over the clip of that name; every clip of it follows. Returns a sheet of frames over a checkerboard
alpha-clip-preview--html or --file, or --asset (a clip that exists); --duration, --times (seconds of the clip to look at; left out, where the page moves), --at (where on the timeline it will start), --background (checker, timeline or both), --frame-width, --width, --heightPictures of a page where it moves, over a checkerboard and over the video's frames, before anything is rendered: how the agent looks at a title while it designs it
graphicsid (optional)The generated graphics of this video: id, what each shows, length, its elements, what happens in it and when (events), the files its scene references (assets), where its clips sit, whether each still follows the footage it covers (follows, cut, gone), and unsupported for one the app cannot draw. With an id, the graphic's whole document as well
graphics-create--graphics (a list of { id, scene or file, files, at or covers, until, follow, clip }, as JSON; at and until are seconds, a word word:It's@31.82, a bar's word bar-45/w2 or a reference, and a word makes the footage there the graphic's covers) or --file (a JSON file holding that list); --look (the video's look, a .json path or JSON text, written to graphics/_shared/look.json); --track (Graphics), --replaceWrite scenes as generated graphics (graphics/<id>/graphic.json in the video's folder, plus the picture files handed over in files) and place them on a graphics track, made when missing, as one undo step. Every scene is checked whole first and refused with the path of each problem. covers ({ media, in, out, lead }, seconds of that file) makes a graphic follow that stretch of footage through every re-cut; a scene without a canvas.duration is as long as the stretch plus lead, rounded up to a whole frame Also --draft <name> (write into a draft: a track of its own that draws in the viewer and stills, never in the export), --revision (refused when the video changed since), --detail (summary or full). Inside a scene a motion's at or an element's time.from may name a word (word:solved, arrive: true); the answer says what each became
graphics-preview--scene or --file, or --id (a graphic that exists); --look, --times, --report-at, --at or --covers (placed where the stretch plays; what it would displace is named), --track (the graphics track it will sit on, stacked in the real order), --faces, --detail, --background (checker, timeline, wire or all), --frame-width, --outA scene the way the app will draw it, before anything is written: layout (every element's frame and bounds in canvas pixels, every text's lines and word boxes) and placed (the same on the video's canvas), under (what plays beneath it at each sample: graphics, caption bars, texts, and the overlaps between them), events on the graphic's clock and the timeline's, warnings, and pictures over a checkerboard, over the video's frames with the other graphics and the captions on, and as a wireframe with the ids
graphics-remove--ids (required), --revisionTake graphics off the timeline (one undo step) and put their folders in the Trash
graphics-frames--scene, --file or --id; --look, --times (list) or --every with --from/--to, --width, --background (transparent, a colour, { "media", "at" } or { "timeline" }), --filmstrip, --inline, --outRender a scene by itself to PNG files at chosen seconds, with no video, timeline or placement: straight-alpha PNGs (one per time, <id>_<seconds>.png) in a folder the answer names, the layout at each time and any warnings; --filmstrip adds one labelled strip. Works with no video open; a scene's footage then draws as a labelled placeholder. The same renderer as the export
measure--at, --source (render, export or viewer), --boxes (a list of [x, y, w, h] in canvas pixels), --elements (a list of graphic/element ids), --width, --outNumbers read off a frame: the luma (0–255) inside each box or on each element, what is behind an element (the frame with its graphic left out), the contrast of its ink on what it sits on (WCAG; 4.5 is the floor for text) and, for a dim or a scrim, the uncovered margin at each edge of the canvas (0 when it reaches the frame). The measured frame is written as a PNG (path)
draft-commitname (required), --track (default Graphics), --revisionMove a draft's graphics (graphics-create --draft <name>) onto the real graphics track as one undo step; the draft track goes, other drafts stay
draft-discardname (required), --revisionDrop a draft: its clips leave the timeline, the track goes (one undo step), the graphics only it held go to the Trash
be media-import ~/Desktop/intro.mov
be media-relink take-3 ~/Footage/June/take-3.mov
be alpha-clip-create --name "Lower third" --file ~/Desktop/lower-third.html --duration 3

An alpha clip is a .mov (HEVC with alpha) with its document next to it like every other file (hidden, .<name>.mov.bettereditsanalysis), which the app writes itself: it holds the page the clip was rendered from, the words on it and your agent's description of it, and library-search finds the clip by them. The two travel together like any file and its analysis. Alpha clips are never sent for analysis and cost no credits. Clips made before 0.5.6 kept their page in an .alphaclip file; the app converts each the first time it reads it.

Generated graphics are scenes, not pages: a JSON document of elements (a group, a row or a stack; a rect, a text, an image, a path) with motions named from the video's look, which the app lays out and draws itself on every frame, in the viewer, in frame and in the export alike, at whatever size the frame is. Nothing is rendered ahead, so there is no movie and nothing is ever stale; a change to the look reaches every graphic at once. Each graphic is its own small clip on a graphics track, kept whole in the video's graphics/<id>/ folder (graphic.json and any picture files it names), and not counted as media: it is not in project.json, the Sources panel or the library, and is never analysed. graphics/_shared/look.json is the video's look (fonts, colours, shadows, text styles, easings, motions, shared pictures), which graphics-create --look writes. A graphic with covers starts where that stretch of footage plays, less its lead, and the app keeps that true at every edit, so most re-cuts move graphics and nothing is redone; lint and graphics name the ones a cut landed inside. Designed captions are one small graphic per bar (role: caption); a takeover (role: takeover) replaces the bars it covers, which the app hides under it, so the words are on screen once. The scene document, key by key, and the rules for designing one are in guide graphics.

Footage and pictures inside a scene are assets that reference files ({ "media": "take", "frame": 12.3 } for a frame of a file of the video, from for it playing, { "file": "logo.png" } for a picture in the graphic's folder, { "timeline": "live" } for the cut itself), drawn by the app from those files, so a frame of HDR footage inside a card stays HDR and as sharp as the export. In an HDR piece a standard-range file sits at standard white unless the element (or a media clip) says "luminance": "graphic", which lifts it with the captions (for screenshots and screen recordings beside white type). A graphic made by BetterEdits 0.6 from a page of HTML is unsupported from 0.7 on: it shows as such on the timeline, draws nothing, and the agent makes it again as a scene or removes it. Alpha clips are unchanged.

be graphics-preview --file ~/bars/bar-07.json --covers '{"media":"take","in":49.245,"out":50.30}'   # the layout, the events, warnings, and pictures over the timeline
be graphics-create --look ~/bars/look.json --file ~/bars/all-bars.json   # every bar in one call, each with its covers
be graphics                  # where each one sits, and which are cut or gone

Captions

CommandOptionsDoes
transcript--trackThe words spoken on a track with start and end times, from its clips' analysed footage; names the files that have not been analysed. Adds nothing to the video
captions-generate[source], --replaceMake a subtitles track with bars from the transcript of a track's analysed footage, or refill one with --replace s1 (every bar replaced). The bars are linked to nothing and do not follow later cuts
captions--trackThe bars of a subtitles track

Fixing a bar's words or times, merging, splitting, the caption style and the position are edits of the document (bars, style, position, offset on the subtitles track). See Captions.

be captions-generate v1
be captions

Analysis

An agent cannot watch video. On Pro the app analyses every file in the workspace by itself and writes the analysis next to it as a hidden file (.<file>.bettereditsanalysis); transcript and captions-generate read it, and so does the agent, whole. Analysis is part of Pro. The Mac signs in once through login; the credential lives in the app's own settings, never in a workspace. The agent runs all of this for you; see Analysing footage.

CommandOptionsDoes
reanalyzefiles (one or more files of the workspace), --type (only to overrule what the service read a file as: video/footage, video/edit, audio/speech, audio/music, audio/sfx, image/scene, image/graphic; left out, the service decides), --force, --waitPut a file of the workspace back at the front of the analysis queue: one that failed, or a re-run with --force (with --type when the service read it as the wrong kind of thing). The app queues every file in the workspace by itself, so nothing analyses a file for the first time: a file outside the workspace is refused, and media-import or media-folders --add bring it in; a file that is queued or running is refused too, since it is coming. Called analyze before 0.5.7, when it also took files outside the workspace. Waits up to --wait seconds (60) and reports where each file stands, with the words found, the charge and the balance left once all are done
analyze-pause, analyze-resumeAsk the person, in the app and with the agent's name, to pause or resume analysis; the answer comes back. Paused, nothing new starts and files running now finish; what analyze names still runs
analysispath, --section (one or several), --from, --to, --fullThe analysis document of a file (a path or an asset name): source and camera facts, summary, shots or scenes, people, actions, moments, on-screen text, speech, music, events. --from and --to cut every timed list in it to a stretch of the file, in seconds, so only that stretch is read. Words and events are left out unless a stretch or --section brings them, or --full
library--type, --folder, --video, --duration, --orientation, --speech, --people, --setting, --recorded, --tempo, --instrumental, --category, --unused; --list, --sort, --limit, --offsetWhat the workspace's media holds, from an index of its documents kept on this Mac: files and hours per library and per video, per kind, and what they can be narrowed by (settings, places, months, lengths, sound categories, tempos). With a filter or --list, the files themselves
library-searchqueries (one or more wordings; several bare values work), the same filters, --kinds, --limitFind files by what is in them: what happens, what is said, on-screen text, people and objects, a song's name, tempo and shape, a sound's kind, a title's description and words. Answers with files and the stretches in them that matched, in the file's seconds. Matches word stems, not meaning, which is why it takes several wordings at once
analyze-status--wait, --jobsThe analysis queue, the same one the app shows: one line per file (waiting with its cost, analysing, done with its words and credits, failed with why), then its summary: still counting, paused, held (waiting for credits, with the link). Waits up to --wait seconds (60) for unfinished files first
loginSigns this Mac in: opens the dashboard in the browser, waits for the approval, keeps the credential in the app's settings
logoutSigns this Mac out
accountWho is signed in, the plan (Pro, trial, free), the credits left and about how many minutes of raw footage they analyse, and the dashboard links
be login
be analyze ~/Footage/talk.mov
be account

Look

CommandOptionsDoes
revealwhat (a clip ref, asset or path)Show a file in the Sources sidebar, folders opened, tile lit, as Show in Sources does
preview[path], --offShow a media file in the main player, as a click on it in the Sources sidebar does; --off brings the timeline back
frame--at (required), --width (default 360), --source (render, export or viewer), --outThe picture at a time, with text and captions drawn on, as a PNG written to disk (path, the first line of the answer). --source export renders through the export pipeline (its size and range, HDR colour-matched to sRGB); --source viewer captures the player on screen, at the display's scale, after moving the playhead there
mix--audio <path> (write the mixed sound as a 16-bit stereo WAV at 48 kHz, its second 0 the timeline's, for your own measuring), --track (with --audio: one track's own sound, a1 or its name, instead of the master)How loud the timeline is, measured from the sound the viewer and an export play: for the master and for every track on its own, lufs (integrated loudness; speech on a reel sits near -16), peak (the highest sample in dBFS; keep the master under -1) perSecond, whole (the seconds the track's clips fill from start to end; a second a clip only partly fills reads several decibels off) and under: how many decibels the track sits under the loudest one, that one's loudness over this track's loudest whole second, which is the number music is set by (20 or more under a voice). A track that is muted, or silenced by a solo elsewhere, says muted and reads silent. closest, the nearest the two come in any one second, is still there and swings with every quiet syllable. An agent cannot hear: it sets volume or gain on clips, reads mix, and adjusts
contact-sheet--out, --path, --against <file>, --offset, --clips, --every, --times (list), --start, --end, --columns (default 4), --width (default 240), --labels (under, over or none), --sourcePNGs of many frames, each with its time under it, the true composite (every visible track, graphics and overlays included). Default: 12 frames over the piece; up to 480 a call, 48 a sheet (more come as numbered sheets); --clips one frame per clip with its ref; --path a source file's frames, to look before cutting; --against a reference file's frame beside the timeline's at each time, in pairs (--offset when the file's second 0 is not the timeline's), to compare a copy with what it copies
screen--at, or --from/--to with --every; --graphic, --elements, --people, --faces, --min-overlapWhat is on the canvas at a second, or sampled over a range, as boxes in canvas pixels: the picture and the person in it (the face and body found in the frame as framed, with face_confidence; --faces false for the analysis's estimate), every graphic's elements, caption bars, texts, the overlaps between them, what a takeover hides and words shown twice

Over MCP the picture comes back as an image and the answer's first line is path: (or paths:), where the same PNG was written, at --out or in a temporary folder, so a script can read its pixels; measure reads them for you. On the command line, without --out the PNG goes to a temporary file named after the command and the time; the command prints the path.

be contact-sheet --clips
be contact-sheet --path ~/Footage/talk.mov --every 2
be levels ~/Footage/talk.mov
be frame 3.2 --out ~/Desktop/hook.png

Drive the app

CommandOptionsDoes
seek--at (required), --playMove the playhead, optionally start playing
select--target (required)Select a clip or bar, or none to clear
app-showPut a window on your current video and bring it to the front (the tools above need it)
undoUndo the last change, yours or the user's
redoRedo the last undone change
export[path], --resolution (hd, 2k, 4k) or --height, --fps (24, 30, 60), --hdr, --preview, --clip <id>Render an MP4 with text, captions and graphics burned in: what the export sheet offers, and told nothing, the file the sheet's defaults make. The answer says the resolution, size, fps and hdr it made, and hdr_refused when HDR was asked for and no footage is HDR; notes lists what the file leaves out or carries that nobody watched: tracks that are muted or hidden, unsupported graphics (drawn as nothing), standard-range clips at picture level in an HDR piece, and the export sheet's own warnings. --out is taken as the path. --clip renders one clip's stretch of the timeline, or an audio clip's sound as M4A. Default path: your export folder (Movies, or where you last exported) as <Name> <date>.mp4. --preview is a quick 540p standard-range file; otherwise the footage's own size, HDR kept. --height is the short side, as the export sheet counts (1080, 1440, 2160). See Export
be select v1:2
be export --preview