--- name: start description: Make and review videos and storyboards with the user in the loop, with any video tool (Hyperframes, Remotion, Motion Canvas, ffmpeg, screen recordings). Use when the user wants to make or edit a video, motion graphic or animation, wants a storyboard or shot list reviewed before animating, wants to pick a visual style or a playbook (a method for one kind of video, like a music video, a talking-head short or a product launch), or wants to review a render and send timestamped feedback. Requires the framejam MCP server. metadata: internal: true --- # FrameJam: styles, playbooks + one-click video and storyboard review Skill version: 5 FrameJam runs next to your chat at http://localhost:2400. It gives you three things the chat is bad at: **choosing a style** (a structured `style.json` that describes a look, plus an example video), **following a playbook** (the full method for one kind of video: what the user brings, the steps, the tools, the quality bar) and **precise feedback** (timestamped, pinned comments with a frame image of the render). FrameJam works with whatever makes the video. **The user reviews the rendered video** (`videoPath`), so what they see is exactly what you rendered. Use whatever tool fits: the project's own, or what you'd normally reach for (Hyperframes, Remotion, Motion Canvas, ffmpeg…). FrameJam is a toolkit, not a process. Do what the user asks, with sensible defaults, and reach for these tools when they help. Don't add steps, questions or approvals the user didn't ask for. **Words to use with the user:** FrameJam calls each review a **project** (the start page is "Projects"). Say "I opened the project in FrameJam", not "I opened a review". Tool and field names (`open_review`, `reviewId`) stay as they are. ## 0. Open FrameJam in the built-in browser (always do this first) As soon as this skill is used, open FrameJam where the user can see it, without being asked: - **Cursor:** use the built-in browser tool (`cursor-ide-browser` → `browser_navigate`) with `position: "side"` so it opens beside the chat. - **Other harnesses:** use their browser/preview tool if there is one. Otherwise give the user the link. - **Which page:** the project's `url` when there is one, the pick page when you opened one, and `http://localhost:2400` (Projects) otherwise. - If the page doesn't load, nothing is hosting the UI. Run `npx -y framejam start` (it starts the UI in the background, or reuses a running one, and prints the URL), then open that URL. Never ask the user to start it. - If the framejam tools themselves aren't available, run `npx -y framejam install ` with the app you're running in (`cursor`, `claude` or `codex`). It adds the MCP server and the skill (or confirms them) and starts the UI. Open the link it prints, then tell the user the last step it prints (restarting the app once so the tools load). ## 1. Do what the user asked Act on the request right away with sensible defaults (length, format, tool, look); ask only when something can't be guessed, like the footage for an edit. Useful shortcuts when the request is short: - **`/framejam playbook` or `/framejam style`:** open the picker for just that (`open_picker({ kinds: ["playbook"] })` or `["style"]`), see loop step 0. - **Continuing:** if you've been making a video in this chat, render the next version into the same project (`add_version` with its `reviewId`, or `open_review` with the same `title`). - **Feedback waiting:** `list_reviews()` shows every project. If one is `sent_not_delivered` or `user_commenting`, call `get_feedback({ reviewId })` and apply it. If one is `awaiting_user`, open its `url` and `wait_for_feedback`. - **A video already in this folder** (a render, or a Hyperframes, Remotion or Motion Canvas project) and the user wants to review it: open the newest render as a project. - **`/framejam` alone with nothing to go on:** ask in one line what the video should be about. ## The loop 0. **Pick (only when asked).** If the user names a playbook or style, use it (`get_playbook(id)` / `get_preset(id)`). Open the pick page only when they ask to choose (`/framejam playbook`, `/framejam style`, "let me pick a style") or when the request is too open to start from. Otherwise just build. - Call `open_picker({ title, kinds })` (`kinds` defaults to both), open the returned `url` in the built-in browser, post it in chat as a clickable link, then call `wait_for_pick({ pickId })`. While it returns `pending`, call it again; after 6 pending results in a row, ask in chat instead. - When the pick arrives, say in one line what you're using ("Using the Clean Cut playbook and no style"), with links to their pages. - A `null` playbook or style means the user chose none. Pass `playbookId` and `styleId` to `open_review` so later versions keep them; existing projects return them from `get_feedback` and `wait_for_feedback`. 1. **Follow the playbook and the style.** - **Playbook:** its `skill` is a recipe for this kind of video, with defaults the user's request overrides. Use the loop below for every render. Read files in `playbookDir` when the method points to them, and copy its scripts, engine or template into the project before using them; never edit `playbookDir`. `stylesSentence` says how the style combines with it. Some playbooks list styles made for them (`featuredStyles`); when the picked style is one, call `get_playbook({ id, styleId })` and use `styleForThisPlaybook.settings` where the playbook's skill says, since they match its example video. - **A style is a look, not a starting video.** First plan the video from what the user asked for (a rocket launch is about the rocket), then build that subject in the style's visual language: its `palette`, textures and backgrounds, `fonts`, `easing`, `transitions`, `textAnimations` and pacing, as `guide` describes them. Treat it as a strong starting point, not a rulebook: bend it where the subject or the user's request needs it. - `example` says what the style's preview video shows. Don't reuse that scene (its subject, props, layout or story, like the computer in Cyanotype Mac) unless the user asks for it. - The example composition in `templateDir` is a reference, not a skeleton. Don't copy it in and swap the text. Open a file from `templateFiles` (or call `get_preset({ id, includeTemplate: true })`) only to see how a technique is built, such as a texture filter or a title reveal, and rebuild that technique in your own composition. 2. **Build** the video with the project's tool (Hyperframes details below, other tools further down). 3. **Render** to a *new file per version*, e.g. `renders/v1.mp4`, `renders/v2.mp4`, so earlier versions stay comparable. **Check it yourself first:** before opening a review, check the render against the request (and the playbook's quality bar, if there is one) with whatever you can check yourself, and fix what you find. 4. **Open the review:** `open_review({ title, videoPath: "/renders/v1.mp4" })`. Use an absolute path, and the same `title` every time so later renders become new versions of this project. **Speech in the video?** If you already have its word timings (captions you burned in, a voiceover with timestamps, a transcription you ran), add `transcriptPath` (an `.srt`, `.vtt` or word-timings JSON such as Whisper's). The user then reads along under the timeline and can comment on words. Without one, FrameJam transcribes the render itself when whisper.cpp is installed, so don't transcribe only for this. Open the returned `url` in the built-in browser (step 0), and **always post it in chat as a clickable markdown link**, for example `[Open v1 in FrameJam](http://localhost:2400/review/rev_…)`, so the user can open it from the chat if the browser isn't showing it. Then tell them in one line: "Click the video to point at something (or pause and type), then press **Finish review**." 5. **Wait, in the same turn:** call `wait_for_feedback({ reviewId })` right after opening the review. Don't end your turn first — the review page shows "Agent listening" only while you're in this loop. - `status: "pending"` means nothing arrived in the last ~50s. Call it again right away, without asking the user. - **Stop after 12 `pending` results in a row (about 10 minutes).** Don't wait forever. End your turn with one short message: the project's `url`, "Press **Finish review** when you're done, then paste the line the page shows (or say `apply my FrameJam feedback`) and I'll pick it up." The comments are saved, so nothing is lost. When the user comes back, call `get_feedback`. Stop earlier if the user tells you to in chat. - `status: "feedback"` returns `comments[]`, a markdown summary, and frame images. - `revised: true` means the user reopened their review after you got it and changed the comments. The new list **replaces** the old one: drop changes from the old list that aren't in the new one. - **Done?** If the comments only say the video is done, approved or good to go, with nothing to change, don't make another version. Tell the user where the final file is, with the project link, and stop waiting. 6. **Apply every comment.** - `at` / `time` / `endTime` is where in the video. `position` is where in the frame. - The frame image for a pinned comment has the pin drawn on it: look at what's under the pin, then find that element in your source at that time. - `wholeVideo: true` means a global note (pacing, colour, music, and so on). - With a transcript, `spoken` quotes the words in a range comment and `spokenAround` those near a moment. They come from the transcript, so names and rare words may be misheard: use them to find the spot, not as the script. 7. **Ship the next version.** Re-render to `renders/v2.mp4`, then call `add_version({ reviewId, videoPath, note })` (or `open_review` again with the same `title`), plus a new `transcriptPath` if the timing of the speech changed. The `note` is a one-line summary of what you changed; the user sees it when the new version arrives and in the version menu. In chat, say the version is ready and **post the returned `url` as a clickable link** again, e.g. `[Open v2 in FrameJam](http://localhost:2400/review/rev_…)`, with the same one-line summary. Each version is one round: the user's "Finish review" locked the previous version, and the new one starts with no comments. If you couldn't address a comment, say why in chat and in the note. **Keep rounds fast:** re-render only what changed and reuse the rest. 8. Go back to step 5. ## Long jobs - Start anything that takes more than a minute so it keeps running after your command returns (a background job, not a subshell `&` that dies with the shell), log to a file, and check on it every 30–60 s. Never wait a fixed long time on a job you could poll. - Run one heavy job at a time: two renders or a render plus transcription at once make both slower. - Tell the user what's running and how long it should take, in one line, before you wait on it. ## Hyperframes - **First run on this machine:** run `npx hyperframes doctor` once. It checks Node, ffmpeg and Chrome, which rendering needs. If something is missing, tell the user how to install it before you build. - **Build** the composition: `index.html` with a root `data-composition-id` + `data-width`/`data-height`, `.clip` elements with `data-start`/`data-duration`/`data-track-index`, and a paused GSAP timeline registered in `window.__timelines[""]`. Run `npx hyperframes lint` and fix errors. - **Render:** `npx hyperframes render -o renders/v1.mp4`. - **Open:** pass `videoPath`, like any other tool. The user reviews the render. ## Other tools (Remotion, Motion Canvas, ffmpeg, recordings, …) - Keep the project's own tool and render command (`npx remotion render`, the Motion Canvas exporter, an ffmpeg script, a screen recording). **Don't convert the project to Hyperframes.** - Render each version to a new mp4/webm/mov file and call `open_review({ title, videoPath })`. - Use the style's look (`guide`, `palette`, `fonts`, `easing`, pacing) in the tool's own terms, for the user's own subject. ## Storyboards (before there's a video) Use a storyboard review when the user wants to agree on the shots first, or asks for a storyboard, shot list, or "frames before we animate". Same loop, but each version is a set of still panels instead of a render. 1. **Make the panels.** One image per shot (png, jpg, webp, gif or svg), all the same aspect as the final video, in one folder, named so they sort in order: `storyboard/01.png`, `02.png`, … Good sources, in order of preference: - Hyperframes snapshots of a rough composition (`npx hyperframes snapshot --at 0.5,2.5,4.5 -o storyboard`), or stills from the project's own tool. - Images you generate, one per shot. - Quick HTML mockups you screenshot. 2. **Open it:** `open_review({ title, panelsDir: "/storyboard", panels: [{ path: "01.png", title: "Cold open", caption: "Slow push in. VO: 'Every team ships faster now.' 2s" }, …] })`. `panels` is optional; without it every image in the folder is used in file-name order. Titles and captions are what the user reads under each panel, so put the shot name, the action or camera move, the voiceover line and the duration there. 3. **Wait** with `wait_for_feedback`, exactly as for videos (open the URL in the built-in browser first, and stop after 12 `pending` results in a row). 4. **Read the comments by panel.** `at` is `"panel 3 (Cold open)"`, `panel` has the number, title, caption and the panel's `imagePath`, and `position` is where on the panel the user clicked. The attached image shows the panel with the pin drawn on it. `wholeVideo: true` means the whole storyboard (order, pacing, adding or cutting shots). 5. **Next version:** update or regenerate the images, then `add_version({ reviewId, note })`. With no media it re-reads the same `panelsDir` and keeps titles and captions for files that are still there; pass `panels` again to change the order, titles or captions. Earlier versions keep their own copies of the images. 6. When the user approves the storyboard, build the video shot by shot from the panels and captions, and open it as a normal video review (a new `open_review` with the render as `videoPath`). ## Other ways in - If the user says "apply my FrameJam feedback" (with or without a review id), call `get_feedback()`. Without a `reviewId` it picks the review whose comments haven't reached you yet. If the user typed comments but never pressed Finish review, they're handed over now. `list_reviews()` shows every project and where its round stands. - If MCP isn't connected, the user can use **Copy comments as text** in the review page's version menu and paste the result into chat. ## Updating FrameJam If the user asks to update FrameJam, run `npx -y framejam@latest install ` with the app you're running in (`cursor`, `claude` or `codex`; without it, every app found on the computer is set up). It updates the skill, the MCP server entry and the FrameJam page that's running. Then tell the user to restart the app once: your FrameJam tools load the new version only after a restart. ## Rules - Don't ask the user to describe timestamps in chat. Send them to the review page. - Never fake a render. If a render fails, fix it before opening the review. - Keep the review URL stable: one project per video, with a new version for each render. - Call it a "project" when you talk to the user.