CutZal for AI agents
CutZal is a local desktop editor for talking-head videos. The cutzal command reads and edits one CutZal project at a time.
- When CutZal has the project open and its external connection is on, your edits go through the open app. The person sees them right away and can undo them.
- When CutZal is closed, the command edits the project files directly (headless).
Desktop channels
Each app installs its own command and Skill from Settings. CutZal Dev uses a separate command and does not import Stable or Beta personal settings. Use the command installed by the app you want to use. Project files and the ~/.cutzal/run/ registry remain shared: a command routes to the app that owns the project, regardless of channel. Do not edit the same project simultaneously with older apps that do not enforce GUI ownership.
Before you start
- Pass
--projectas the video file path or itscutzal.jsonpath. - The video must have been opened in CutZal once. That creates
<video folder>/edit/cutzal.json. If you getproject_not_found, ask the person to open the video in CutZal first. - stdout is one JSON object:
{"ok": true, "result": ...}or{"ok": false, "error": {"code", "message", "details"?}}. Pass--jsonto always get JSON. Exit code 0 is success, 2 is a refusal you can read and fix, 1 is an unexpected failure. - Progress and "waiting for the person" lines go to stderr. Do not parse them.
- Sandboxes:
cutzaltalks to the open app through a local socket in~/.cutzal/run/and writes project files next to the video. If your commands run in a sandbox,cutzalmust run outside it. In Claude Code, add"cutzal *"tosandbox.excludedCommandsin settings. A blocked socket shows up asconnection_disabledwithdetails.reason: "socket_blocked".
Workflow
cutzal status --project <video>:project.routeisapp(CutZal has it open) orheadless.cutzal context --project <video>: currentrevision, durations, tracks and clips, assets, output segments, and the person's current screen state (ui;nullheadless).cutzal transcript --project <video>: every word with its source time and output time.- Write the edit as JSON (see references/edits.md) and check it without applying:
cutzal preview --project <video> --revision <N> --edit edit.json. - Apply it:
cutzal apply --project <video> --revision <N> --id <new id> --label "<what and why>" --edit edit.json. - Check the result:
cutzal capture --project <video> --at <seconds>and look at the PNG it writes.
--edit - reads the JSON from stdin.
To change the layout (camera only, screen with the camera in a corner, camera size or position), send a placement edit. context.placement names the screen and camera tracks. See references/edits.md.
To fast-forward a long process that should be seen but not heard in full (reading a prompt aloud, typing, waiting), or to slow a part down, send a speed edit instead of cutting it. The screen, camera and clips that follow the speech change speed together and the sound is muted above 2x unless you keep it. { "speed": { "global": 1.2 } } speeds up the whole video. See references/edits.md.
To change the frame size of the whole video (for example a horizontal video from a screen recorded on a vertical monitor), send { "canvas": "1920x1080" } (or "1080x1920", "original"). Recordings that were shown whole stay whole; then pick layouts with placement.
To fix the sync of a recording (for example the voice is heard after the mouth moves), send an offset edit with the new value for that source from context.sources. Do not ask the person to close CutZal and do not edit cutzal.json: the edit goes through the open app and can be undone there. See references/edits.md.
A video inserted with cutzal insert starts with its volume matched to the main audio. To make it louder or quieter, send a gain edit with the new value in dB for that take (inserted: true in context.sources). See references/edits.md.
To make a vertical short (1080x1920) from a long video, clone the project with a vertical canvas: cutzal project clone --project <video> --name <short name> --canvas 1080x1920. It returns the new projectKey; run every later command on that path. The original project does not change, and the copy appears at the top of Recent projects in CutZal, where the person opens it. In the clone, cut everything outside the parts you keep (range edit with action: "cut" on output time; add the motion tracks that use output anchors to rippleTrackIds), then place the screen and camera for a 9:16 frame with placement edits (pos as {x, y}, sizePct: 1). Assets on the screen follow the screen frame when their clip uses placement: "screen". Check every part with capture before asking the person to export. Put the header as a text clip (title) so the person can edit the words in CutZal, and fix product names in subtitles with captionTerms on the transcript track (see references/edits.md).
To place an image, B-roll or sound file, register it first with cutzal asset add (a sound from the library: cutzal sfx use) and use the returned id as assetId. Add the destination track before registering: images are refused until the project has had a track edit, and sound files (asset add of audio, sfx use) are refused until it has an audio track (track.add with "kind": "audio"). references/edits.md has the track edits and a full example.
Rules
-
“Here” and “this part”: read
context.ui.selectionbefore interpreting a pointing instruction. Selections preserve separate source ranges and their output ranges; do not replace them with their outer bounds. Ask the person to select a range when the selection is empty.ui: nullmeans headless, not an empty screen selection. Source times are on the original master timeline, not a physical file's clock. A deleted playhead position hasoutput: null; repeated footage can have severaloutputCandidates, so ask which occurrence when it is ambiguous. Reading context does not move the playhead, select, scroll or transfer control. -
Resume after the person edits: read
session status,changes --since <lastRevision>and freshcontextbefore continuing. Preserve human changes and the current selection; do not reapply an old plan over them.ui.control: "human"with a work means wait for return to the agent. Prefersession statusfor the current control decision:uidescribes the snapshot just after its initial save, not continuous live state. If context returnsbusyorstale_revision, discard the snapshot and reread. Reading it never consumes the returned handback. -
Editing session: for a sequence of edits in an open app, start with
cutzal session start --project <project> --label "<task>" --agent codex --json(orclaude-code,other). Pass itssessionas--sessionon subsequent project commands. Only that session can write while it holds control. Whenlockedhasreason: "human_control", wait and pollsession statusevery 15 seconds. Resume only atcontrol: "agent"; preserve the returnedhandbackcuts, restores and settings changes. A null handback orhandbackIncompleterequires rereading context/transcript. End withsession end. An ended or unknown session needs a new start, never a blind retry.mode: "headless"has no registered lock, expiry or status/end; use per-command revision checks. If the app opens later, start a fresh app session. -
Revision: always pass the latest
--revision. Every applied edit,asset add,sfx useandrewindincreases it. Onstale_revision, readerror.details.changesbefore replanning. Rows withactor.kindofhumanorphoneare the person’s intent: preserve those edits, especially restored ranges (action: "keep"). Never just replace the revision and replay the old plan. Usecutzal changes --project <video> --since <last revision>between editing turns. Ifincompleteis true, any row hasrangesIncomplete: true, or the actor isunknown, read currentcontextandtranscriptand check with the person when intent is unclear. Empty ranges are not evidence that nothing changed. -
Request ID: use a new
--idfor each new edit. Sending the same id with the same edit again is safe and returnsduplicate. The same id with a different edit isid_conflict. -
Label:
--labelnames the checkpoint the person sees in CutZal. Say what you did in a few words. -
Do not write project files: never edit
cutzal.jsonor anything underedit/directly, especially while CutZal is open. Use the commands. -
Time: all times are seconds. Output time is the edited video; source time is the original recording. See references/coordinates.md.
-
Human confirmation:
export, and arewindthat would also undo the person's own edits, open a confirmation in CutZal and wait up to 10 minutes. Tell the person what you asked for, then wait. Do not retry in a loop.declinedmeans they said no. -
Costs: ElevenLabs transcription, the local model download, and sound effect generation cost money or gigabytes. The person starts them in CutZal. This command never starts them. Free local transcription (
cutzal transcribe) runs on this Mac once the person has downloaded the model, so you may run it whentranscriptsays the project is not transcribed yet. -
Review is the person's:
cutzal reviewonly reads. Tell the person which places to listen to first, but never mark them as confirmed. Only the person confirms in CutZal, so there is no command for it. -
Undo: every applied edit is a checkpoint.
cutzal checkpointslists them andcutzal rewindgoes back to the state before one of them. Aninsertis not a checkpoint andrewindcannot go back across it: to take an inserted video out, cut its block with a range edit, or ask the person to undo it in CutZal.
Commands
Options and result fields: references/commands.md.
When something fails
Read error.code. The common ones:
stale_revision: the project changed. Use the new revision and retry.connection_disabled: CutZal has the project open with the external connection off. Ask the person to turn it on in CutZal Settings > Agent, or to close the project. Withdetails.reason: "socket_blocked"the connection is on but your sandbox blocks it (see Sandboxes above).busy: CutZal is in the middle of something or the person is looking at a dialog. Wait, then run the same command again. Forapplyandrewindkeep the same--id; the other commands take no--id.app_required: this needs the open app. Ask the person to open the project in CutZal and turn on the external connection.invalid_editorinvalid_argument: fix the input using the message.
All codes and what to do: references/errors.md.