# 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 `--project` as the video file path or its `cutzal.json` path.
- The video must have been opened in CutZal once. That creates `<video folder>/edit/cutzal.json`. If you get `project_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 `--json` to 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**: `cutzal` talks 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, `cutzal` must run outside it. In Claude Code, add `"cutzal *"` to `sandbox.excludedCommands` in settings. A blocked socket shows up as `connection_disabled` with `details.reason: "socket_blocked"`.

## Workflow

1. `cutzal status --project <video>`: `project.route` is `app` (CutZal has it open) or `headless`.
2. `cutzal context --project <video>`: current `revision`, durations, tracks and clips, assets, output segments, and the person's current screen state (`ui`; `null` headless).
3. `cutzal transcript --project <video>`: every word with its source time and output time.
4. Write the edit as JSON (see [references/edits.md](https://cutzal.ai/docs/en/agents/edits.md)) and check it without applying: `cutzal preview --project <video> --revision <N> --edit edit.json`.
5. Apply it: `cutzal apply --project <video> --revision <N> --id <new id> --label "<what and why>" --edit edit.json`.
6. 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](https://cutzal.ai/docs/en/agents/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](https://cutzal.ai/docs/en/agents/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](https://cutzal.ai/docs/en/agents/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](https://cutzal.ai/docs/en/agents/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](https://cutzal.ai/docs/en/agents/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](https://cutzal.ai/docs/en/agents/edits.md) has the track edits and a full example.

## Rules

- **“Here” and “this part”**: read `context.ui.selection` before 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: null` means headless, not an empty screen selection. Source times are on the original master timeline, not a physical file's clock. A deleted playhead position has `output: null`; repeated footage can have several `outputCandidates`, 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 fresh `context` before 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. Prefer `session status` for the current control decision: `ui` describes the snapshot just after its initial save, not continuous live state. If context returns `busy` or `stale_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` (or `claude-code`, `other`). Pass its `session` as `--session` on subsequent project commands. Only that session can write while it holds control. When `locked` has `reason: "human_control"`, wait and poll `session status` every 15 seconds. Resume only at `control: "agent"`; preserve the returned `handback` cuts, restores and settings changes. A null handback or `handbackIncomplete` requires rereading context/transcript. End with `session 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 use` and `rewind` increases it. On `stale_revision`, read `error.details.changes` before replanning. Rows with `actor.kind` of `human` or `phone` are the person’s intent: preserve those edits, especially restored ranges (`action: "keep"`). Never just replace the revision and replay the old plan. Use `cutzal changes --project <video> --since <last revision>` between editing turns. If `incomplete` is true, any row has `rangesIncomplete: true`, or the actor is `unknown`, read current `context` and `transcript` and check with the person when intent is unclear. Empty ranges are not evidence that nothing changed.

- **Request ID**: use a new `--id` for each new edit. Sending the same id with the same edit again is safe and returns `duplicate`. The same id with a different edit is `id_conflict`.

- **Label**: `--label` names the checkpoint the person sees in CutZal. Say what you did in a few words.

- **Do not write project files**: never edit `cutzal.json` or anything under `edit/` 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](https://cutzal.ai/docs/en/agents/coordinates.md).

- **Human confirmation**: `export`, and a `rewind` that 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. `declined` means 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 when `transcript` says the project is not transcribed yet.

- **Review is the person's**: `cutzal review` only 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 checkpoints` lists them and `cutzal rewind` goes back to the state before one of them. An `insert` is not a checkpoint and `rewind` cannot 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

| Command                 | What it does                                                                                                                      |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `cutzal session start`  | Declares an external editing task and returns its session ID                                                                      |
| `cutzal session status` | Reads whether the agent or person has control and any returned human edits                                                        |
| `cutzal session end`    | Ends the external session after its in-flight writes finish                                                                       |
| `cutzal status`         | Lists running CutZal apps and, with `--project`, which route the project uses                                                     |
| `cutzal context`        | Project summary plus playhead, selections and editing control in the open app; `ui: null` headless                                |
| `cutzal changes`        | Reads who changed what since a revision, including missing history                                                                |
| `cutzal transcript`     | Words with source and output times, in a time window                                                                              |
| `cutzal preview`        | Checks an edit without applying it                                                                                                |
| `cutzal apply`          | Applies an edit and makes a checkpoint                                                                                            |
| `cutzal asset add`      | Registers video, image or audio files as project assets                                                                           |
| `cutzal insert`         | Inserts a video with its own sound at a point in the edited video, matches its volume to the main audio and pushes the rest later |
| `cutzal checkpoints`    | Lists checkpoints (the undo history)                                                                                              |
| `cutzal rewind`         | Goes back to the state before a checkpoint                                                                                        |
| `cutzal capture`        | Renders frames of the edited video to PNG files                                                                                   |
| `cutzal export`         | Asks the person to export in CutZal and returns output files and delivery quality checks                                          |
| `cutzal sfx list`       | Lists the person's sound effect library                                                                                           |
| `cutzal sfx add`        | Adds audio files to the sound effect library                                                                                      |
| `cutzal sfx use`        | Copies a library sound effect into the project as an audio asset                                                                  |
| `cutzal transcribe`     | Transcribes the project for free on this Mac (local model)                                                                        |
| `cutzal review`         | Lists the places the person should listen to first after an AI cut, and which ones they already confirmed                         |
| `cutzal finish-rules`   | Experimental, off by default. Reads the person's versioned finishing rules and pending candidates without changing them           |
| `cutzal finish-check`   | Experimental, off by default. Checks saved audible sound effect placements against confirmed limits; warnings do not block export |
| `cutzal project clone`  | Copies a project as another project of the same media, optionally with a vertical 1080x1920 canvas for a short                    |

Options and result fields: [references/commands.md](https://cutzal.ai/docs/en/agents/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. With `details.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. For `apply` and `rewind` keep 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_edit` or `invalid_argument`: fix the input using the message.

All codes and what to do: [references/errors.md](https://cutzal.ai/docs/en/agents/errors.md).
