---
name: Clip Cutter
description: Use to trim, order and render short cuts from video/audio in files/ or media/ — a rough cut, a highlight reel, a 30-second teaser, or joining a few takes into one MP4 under projects/cuts/.
tagline: Turn your videos into short clips: pick the moments, put them in order, get one finished video.
version: 1
situations: cut this video, trim a clip, make a highlight reel, join these takes, rough cut, render an mp4, shorten the video, teaser edit, edit list, clip cutter
requires-secrets:
requires-packages: ffmpeg
---

# Clip Cutter

## Why a screen

You change the edit list; they see the cut — the clips in order, each one's in and out point, the
running time — and drag a handle or nudge a frame instead of writing you a paragraph about which
half-second was wrong. Frame-accurate taste is the one thing a chat round-trip cannot carry: they
press Render when it looks right, and tell you in four words what to change when it does not.

## Where the media is

Sources live in **two** folders and this skill reads both:

- **`files/`** — where everything you generate lands (the image, video, speech and music tools all
  write there). This is the usual answer.
- **`media/`** — what the operator dropped in by hand, when they have one.

Renders land in **`projects/cuts/`** as `.mp4`. Nothing else is touched — the `render_cut` tool
refuses any clip path outside `files/` or `media/`, and any output outside `projects/cuts/`.

This skill also ships an **AI App** (`Clip Cutter`, in the agent's Apps list) and a **New Session**
button ("Cut a video"). The app is the fast path for anything the operator wants to *see*: it lists
`files/` and `media/` as two labelled groups, takes dropped files (they are saved into `files/`), has
a timeline with drag-to-reorder and trim handles, previews the cut, and runs `render_cut` for them.
You still do the work from here when they'd rather just describe it.

## The edit list

One JSON value, the same shape whether you write it to a file or hand it to the tool:

```json
{
  "version": 1,
  "clips": [
    { "path": "files/interview.mp4", "in": 12.5, "out": 19.0 },
    { "path": "media/broll.mov",     "in": 0,    "out": 3.2 }
  ]
}
```

- `in`/`out` are **seconds from the start of that file**, `out` strictly greater than `in`.
- Order in the array is order in the render.
- Every `path` is inside `files/` or `media/`.
- A bare array of clips is accepted too.

Keep an edit list you're iterating on at `projects/cuts/<name>.edits.json` so the next turn (or the
app) can pick it up. Read it with the `read` tool; it's plain JSON.

## Rendering

```
render_cut(edits: "<the JSON above, as a string>", out: "projects/cuts/highlights.mp4")
```

Every clip is re-encoded to H.264/AAC at the first clip's frame size, so mixed sources concat
cleanly. The tool prints `##progress N` as clips complete and exits **2** with a one-line reason on a
bad edit list, a path outside scope, or a missing `ffmpeg`. It needs `ffmpeg` on PATH (`ffprobe` is
used when present, and guessed around when it isn't).

## Working with it

- **Find the media first.** `ls files/` and `ls media/` before you invent filenames. If both are
  empty, make something — generate the clip, or ask them to drop one in the app — rather than
  guessing at a name.
- **Get durations before guessing `out`.** `ffprobe -v error -show_entries format=duration -of csv=p=0 files/x.mp4`,
  or ask. An `out` past the end of the file just ends the clip early — it is not an error, but it is
  rarely what they meant.
- **Suggest the app** when the operator says anything about *seeing*, *scrubbing*, *finding the right
  moment*, *nudging* a cut, or when they've asked for more than two adjustments in a row: "Open the
  Clip Cutter app and drag the handles — I'll render whatever you land on." Frame-accurate taste is
  their job, not a chat round-trip.
- **Don't render on every tweak.** Settle the edit list, then render once.
- After a render, tell them the path. The app opens the result itself.
- **Never write outside `projects/cuts/`.** `files/` and `media/` are theirs and the app's to read;
  a "cleaned up" source file is a deleted take.
