diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 217f19d..2867514 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -13,6 +13,7 @@ "skills": [ { "name": "add-note", "path": "skills/add-note", "invoke": "/pai-pro:add-note" }, { "name": "groups-compose", "path": "skills/groups-compose", "invoke": "/pai-pro:groups-compose" }, + { "name": "cawcut-vn", "path": "skills/cawcut-vn", "invoke": "/pai-pro:cawcut-vn" }, { "name": "image-compose", "path": "skills/image-compose", "invoke": "/pai-pro:image-compose" }, { "name": "script-compose", "path": "skills/script-compose", "invoke": "/pai-pro:script-compose" }, { "name": "show-dag", "path": "skills/show-dag", "invoke": "/pai-pro:show-dag" }, diff --git a/Dockerfile b/Dockerfile index 491f704..11afcc4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,6 +50,7 @@ ARG CLAUDE_VERSION # Runtime system binaries. # ffmpeg reel stitching (reel_stitch.js) # poppler-utils pdftotext for script-compose skill +# zip packages a VN draft into .vn (cawcut vn project pack) # tini PID 1, signal forwarding # curl healthcheck + cloudflared install # bubblewrap Codex sandboxing on Linux @@ -57,6 +58,7 @@ ARG CLAUDE_VERSION RUN apt-get update && apt-get install -y --no-install-recommends \ ffmpeg \ poppler-utils \ + zip \ tini \ curl \ bubblewrap \ @@ -140,5 +142,12 @@ RUN echo "[build] codex install refresh ${CODEX_INSTALL_REFRESH}" >/dev/null && codex --version || \ echo "[build] codex CLI install failed - Codex PTY will be degraded") +# CawCut CLI β€” the bundled skills/cawcut-vn skill drives it to build VN +# drafts. `--ignore-scripts` keeps the package postinstall from writing its +# own skill set into ~/.claude/skills, which would shadow the bundled +# cawcut-vn the entrypoint links there. +RUN npm config set prefix /home/node/.local && \ + npm install -g @ubnt/cawcut --ignore-scripts + # tini β†’ entrypoint β†’ node. Three layers but each does one thing. ENTRYPOINT ["tini", "--", "/usr/local/bin/pai-entrypoint.sh"] diff --git a/README.md b/README.md index 0a502cd..2ba052f 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ Open Docker at or local at . - πŸ’¬ [Discord][discord-url] β€” questions, ideas, support, and show & tell - βš™οΈ [API Service](docs/api_service.md) β€” developer platform notes, BYOK counterparts, and exact media JSON payloads - 🎬 [Skills reference](docs/skills.md) β€” how filmmaking skills route agent requests +- βœ‚οΈ [Editing in VN](docs/vn-editing.md) β€” build a rough cut and open it in the VN app - πŸš€ [Setup and agents](docs/setup.md) β€” choose Docker or host mode, configure Claude/Codex, and understand ports, auth, and permissions - πŸ— [Architecture](docs/architecture.md) β€” viewer, CLI, canvas, and project-file layout - ❓ [FAQ](docs/faq.md) β€” common setup and generation issues diff --git a/agent-templates/PROJECT_AGENT.md b/agent-templates/PROJECT_AGENT.md index 58c1424..6475e40 100644 --- a/agent-templates/PROJECT_AGENT.md +++ b/agent-templates/PROJECT_AGENT.md @@ -18,14 +18,17 @@ Before any media-generation command, load the matching skill in the current turn | design a character voice, dialogue read, or narration/VO track (`generate_voice.js`) | `voice-compose` | | generate, animate, continue, restyle, edit, or render a video clip (`generate_video.js`) | `video-compose` | | group canvas nodes into scenes, act beats, or reference sets | `groups-compose` | +| assemble, cut, or finish generated clips, images, and audio into an edited timeline in the VN app | `cawcut-vn` | Inline recipes below cover only tiny operations: summarize the canvas and take a note. +`cawcut-vn` is authored for the Anthropic runtime and names `AskUserQuestion`; on Codex, use `request_user_input` (or its numbered fallback) for the same questions per Β§ "Recommendation and choice shape". + Use the skill when it matches; skills own canonical node grammar, refs, edges, metadata, and CLI shape. Stage generation by default: every media generation CLI passes `--stage`. The active project wrapper owns output collection. ## Keep momentum - recommend the next step -After a terminal media generation result, close with one concrete next step. Run the prompt alignment check first (Β§ "Prompt alignment check") and fold its verdict into the same reply. For story-to-video work, recommend the next missing filmmaking piece; when all planned clips are ready and order is unambiguous, assign Timeline order via `shot_id` before handing off in chat to Timeline inspection. Local reel export is only for explicit user requests. For ad-hoc one-offs, keep the suggestion local to what the user just made. +After a terminal media generation result, close with one concrete next step. Run the prompt alignment check first (Β§ "Prompt alignment check") and fold its verdict into the same reply. For story-to-video work, recommend the next missing filmmaking piece; when all planned clips are ready and order is unambiguous, assign Timeline order via `shot_id` before handing off in chat to Timeline inspection. When the user wants an actual edited cut rather than canvas inspection β€” trim, sequence, add music/text, or finish for delivery β€” recommend `cawcut-vn` once the media it needs has landed, and pass the landed `local_path` files; VN is the editing handoff outside this canvas, not another generation. Local reel export is only for explicit user requests. For ad-hoc one-offs, keep the suggestion local to what the user just made. Read `./workflow.json` when the recommendation depends on missing shots, references, voices, clips, or reel order. Draft-only, failed, and cancelled results do not advance the creative pipeline. diff --git a/docs/faq.md b/docs/faq.md index 7f353fc..6055d42 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -38,6 +38,8 @@ Per call: image ~$0.11–0.23 / voice $0.01 per 500 chars / asset upload $0.01 p [docs/setup.md](setup.md) has the full comparison. Short answer: Docker for trying it / daily filmmaking; host mode for hacking on pai-pro source itself. +One thing that differs: VN editing. The VN app runs on macOS/Windows only, so a Docker (Linux) container can build and pack a draft but cannot open it β€” you copy the `.vn` out to the host and open it there, or run host mode on macOS/Windows. See [Editing in VN](vn-editing.md). + ## Can I use a different AI coding agent? Claude Code and Codex CLI are wired with the embedded terminal. New projects default to Codex; start host mode with `PAI_DEFAULT_AGENT_ID=claude ./scripts/start.sh` or Docker with `PAI_DEFAULT_AGENT_ID=claude ./scripts/docker-start.sh` to create Claude-owned projects. diff --git a/docs/setup.md b/docs/setup.md index c93b843..b2bfa36 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -83,7 +83,9 @@ should clone into the WSL2 home directory for better file-system performance. The image includes: -- ffmpeg, poppler, cloudflared, Claude Code, and Codex CLI. +- ffmpeg, poppler, zip, cloudflared, Claude Code, Codex CLI, and the CawCut CLI + (`cawcut`, driven by the bundled `cawcut-vn` skill to build and pack VN + drafts). - Native Node modules rebuilt for Linux. - Bubblewrap for Codex's normal Linux sandbox path. - `/healthz` checks for media tools, volume writability, and the selected @@ -91,6 +93,10 @@ The image includes: - A Cloudflare quick tunnel so PAI can fetch local image/video references for server-side provider calls. +The CawCut CLI can build and pack VN drafts in the container, but VN itself is a +macOS/Windows app the Linux container cannot open β€” see +[Editing in VN](vn-editing.md) for the host handoff. + Docker stores project files in the `pai_projects` named volume. Docker-created Codex auth/config/session state lives in the `pai_codex` named volume; host `~/.codex` is mounted read-only and only `auth.json` is imported on first boot diff --git a/docs/skills.md b/docs/skills.md index 8ecdcef..5386426 100644 --- a/docs/skills.md +++ b/docs/skills.md @@ -17,6 +17,7 @@ There are two skill types: | `video-compose` | "Animate this", "Continue the clip", "Restyle the shot" | Wraps `generate_video.js`. Handles I2V, V2V continuation, voice-locked dubs, narrative sequencing. | | `voice-compose` | "Give the detective a voice" | Wraps `generate_voice.js`. Attaches to the character node in place. | | `groups-compose` | "Group these as Scene 2", "Frame the character refs" | Arranges related nodes and wraps them in visible canvas frames. | +| `cawcut-vn` | "Cut these clips into a rough cut", "Assemble the shots in VN", "Edit this in VN" | Vendored CawCut VN skill. Hands generated local media to the `cawcut vn` CLI to build, validate, and open an editable VN draft. Fully local; no CawCut account required. | Two more primitives β€” taking a note ("take a note", "jot down", "remember that") and summarizing the canvas ("what do we have?", "show the graph") β€” are tiny enough that they live inline in `agent-templates/PROJECT_AGENT.md` instead of as separate skill folders. @@ -30,6 +31,7 @@ The reference table above is the at-a-glance view. Each skill's full recipe β€” - [`skills/video-compose/SKILL.md`](../skills/video-compose/SKILL.md) - [`skills/voice-compose/SKILL.md`](../skills/voice-compose/SKILL.md) - [`skills/groups-compose/SKILL.md`](../skills/groups-compose/SKILL.md) +- [`skills/cawcut-vn/SKILL.md`](../skills/cawcut-vn/SKILL.md) The SKILL.md files are also the source of truth your AI agent reads β€” keep them updated, not this doc. @@ -45,7 +47,7 @@ In a new project, drop these into the terminal: > > *"Take a note: morgue scene opens with cold blue light."* -Each generation lands as a node, edges show provenance, assets mirror into `projects//assets/`. Open the **Timeline** tab to play clips back as a reel. +Each generation lands as a node, edges show provenance, assets mirror into `projects//assets/`. Open the **Timeline** tab to play clips back as a reel, or hand the landed files to `cawcut-vn` to build a rough cut you can open and edit in the VN app. See [Editing in VN](vn-editing.md) for the full flow, including the Docker handoff. ## Adding a new skill diff --git a/docs/vn-editing.md b/docs/vn-editing.md new file mode 100644 index 0000000..b3e46a6 --- /dev/null +++ b/docs/vn-editing.md @@ -0,0 +1,83 @@ +# Editing in VN + +[VN](https://vlognow.me/) ([download](https://vlognow.me/download/)) is a +cross-platform video editor for macOS, Windows, iOS, and Android. Its editor +covers the fine-grained work a canvas does not: multi-track editing, +picture-in-picture, text and subtitles, music, effects, AI transitions, +keyframes, speed curves, reverse, frame interpolation, voice change, auto +captions, image editing, and AI tools. + +PAI-Pro bundles CawCut's [`cawcut-vn`](../skills/cawcut-vn/SKILL.md) skill, which +bridges the two: once a project has script, image, voice, and clip nodes on the +canvas, the agent hands the landed local files to the `cawcut vn` CLI, which +builds and validates a VN draft you open and edit in VN. It runs fully local β€” no +CawCut account, no upload. + +The skill owns the VN conversation. This page covers the one thing that differs +by environment: **where the draft can actually be opened.** + +## Requirements + +- The `cawcut` CLI. Docker ships it; host mode installs it yourself: + `npm install -g @ubnt/cawcut`. +- The VN app, on **macOS** (1.4.0+) or **Windows**. +- Check both with `cawcut vn status` (add `--json` for machine-readable output). + +## Host mode (macOS / Windows) + +Everything works end to end. The agent builds the draft, then either opens it for +you or packs a `.vn` you can double-click: + +```bash +cawcut vn project pack --open --project projects//vn/ +``` + +## Docker mode (Linux container) + +The container is Linux and VN is a macOS/Windows app, so `cawcut vn status` +reports `"platform": "unsupported"`. Inside the container you can still **build +and package** the draft: + +```bash +cawcut vn project init --title "..." --aspect 16:9 --dir projects/<id>/vn/<title> +# add-clip / add-audio / add-sound-effect / add-text / add-srt / set-transition / set-cover +cawcut vn project validate --project projects/<id>/vn/<title> +cawcut vn project pack --project projects/<id>/vn/<title> +``` + +The container can never run `--open`, `install`, or `vn import --open`. When VN +is unsupported, the agent says so plainly instead of pretending to open the +draft. + +### Open it from the host + +1. Pack the draft in the container (above) so `<title>.vn` sits next to the draft + under `projects/<id>/vn/`. +2. Copy it out of the named volume to the host: + + ```bash + docker cp pai-pro:/repo/projects/<id>/vn/<title>.vn . + ``` + +3. Move the `.vn` to a macOS/Windows machine with VN installed and open it β€” + double-click, or `cawcut vn project pack --open`. + +After VN has opened and saved a draft, edit it further by copying it back out of +VN and re-importing with `cawcut vn project init --from <draft>`, not by pointing +the CLI at the installed copy. + +## What the skill refuses + +VN's draft format and the CLI are deliberately narrow β€” no clip delete, reorder, +or in-place edit, no overlays or extra tracks, no crop/reframe or speed changes, +no per-clip mute, no styled captions, and no speech recognition. +[`skills/cawcut-vn/references/capabilities.md`](../skills/cawcut-vn/references/capabilities.md) +is the authority; the agent turns an unsupported request down out loud rather +than faking it. + +## See also + +- [`skills/cawcut-vn/SKILL.md`](../skills/cawcut-vn/SKILL.md) β€” the full skill +- [`skills/cawcut-vn/references/capabilities.md`](../skills/cawcut-vn/references/capabilities.md) β€” supported operations and limits +- [`skills/cawcut-vn/references/install.md`](../skills/cawcut-vn/references/install.md) β€” delivery paths and troubleshooting +- [Setup and agents](setup.md) β€” Docker vs host mode diff --git a/server/__tests__/docker_config_static.test.js b/server/__tests__/docker_config_static.test.js index 5a88824..82dd443 100644 --- a/server/__tests__/docker_config_static.test.js +++ b/server/__tests__/docker_config_static.test.js @@ -49,6 +49,14 @@ test("Docker launcher refreshes the Claude install layer", async () => { assert.match(script, /build_args\+=\(--build-arg CLAUDE_INSTALL_REFRESH="\$claude_install_refresh"\)/); }); +test("Docker image installs the CawCut CLI without running its postinstall", async () => { + const dockerfile = await readFile(join(REPO_ROOT, "Dockerfile"), "utf8"); + assert.match(dockerfile, /npm install -g @ubnt\/cawcut --ignore-scripts/); + // `cawcut vn project pack` shells out to `zip` on Linux, so the container + // needs it to produce a deliverable .vn. + assert.match(dockerfile, /^\s+zip \\$/m); +}); + test("Docker launcher builds the current checkout and recreates the container", async () => { const compose = await readFile(join(REPO_ROOT, "docker-compose.yml"), "utf8"); const script = await readFile(join(REPO_ROOT, "scripts", "docker-start.sh"), "utf8"); diff --git a/skills/cawcut-vn/SKILL.md b/skills/cawcut-vn/SKILL.md new file mode 100644 index 0000000..06062a8 --- /dev/null +++ b/skills/cawcut-vn/SKILL.md @@ -0,0 +1,402 @@ +--- +version: 0.6.0 +name: cawcut-vn +description: | + The single entry point for every local VN action: opening a lightweight VN + preview import, or planning and building a rough cut as a local VN + video-editor draft with `cawcut vn`. Media can be the user's own local + files supplied directly, or another CawCut skill's generated output β€” both + are first-class. Always starts by asking whether the user wants a quick + preview-and-import or the full editing flow. The editing flow inventories + supplied media, confirms the project aspect ratio, recommends feasible + editing styles, resolves missing music or sound effects, presents a + shot-by-shot edit plan for approval, then assembles and validates the draft + before asking whether to open it in VN. Use when creating or editing VN + projects, assembling the user's own photos/clips or generated media into a + timeline, making a slideshow or rough cut, previewing local files in the VN + app, or invoking `/cawcut-vn`. Fully local; no login needed. NOT for + generating source media. +argument-hint: "choose preview-import or editing flow β†’ (editing: inventory media β†’ confirm aspect β†’ choose style β†’ resolve audio β†’ resolve text β†’ confirm cover β†’ approve edit plan β†’ build and validate) β†’ open in VN" +allowed-tools: Bash AskUserQuestion AskQuestion +--- + +# CawCut VN + +The single entry point for every local VN action. The skill owns the VN +conversation and sequencing β€” whether to open a lightweight preview or build a +rough cut β€” and the `cawcut vn` CLI owns draft file generation and app +launching. + +**Skill files are English-only.** Match the user's language in live replies via +`reply_language`, but keep this file and its references in English. + +## pai-pro adaptation + +Vendored from CawCut's `cawcut-vn` skill. Two things differ in pai-pro: + +- Generation handoffs route through pai-pro's `story-to-video-workflow` + orchestrator rather than any capability skill directly. +- Drafts are created inside the active project with `--dir` (see phase 8), + never the CLI's `~/Downloads/cawcut-vn-projects/` default. + +## References + +- Read [references/capabilities.md](references/capabilities.md) before proposing + an edit. It is the authority for supported `cawcut vn` operations, flags, and + editing limits. Never promise an effect that is absent there. +- Read [references/install.md](references/install.md) only when packaging, + opening, installing, or troubleshooting delivery to the VN app. + +Never edit VN JSON by hand. Use only documented `cawcut vn` commands. + +## Refusing what VN cannot do + +`references/capabilities.md` β€” its **Not supported** list and **Common +refusals** table β€” is the boundary. Check the user's request against it before +proposing a style or plan, say the refusal out loud in the user's language then, +and never absorb it silently into a finished draft. + +- Name the missing capability plainly ("`cawcut vn` cannot remove or mute the + audio a source video already carries β€” no flag does that"), never with a hedge. +- Never let a refusal read as done: no success report for an effect the CLI + never applied, no describing the source media as if it had been altered, no + hand-edited JSON to fake it. +- Offer the closest supported behaviour, or say the media must be prepared + outside this skill and re-supplied as a local file. If nothing close exists, + refuse outright instead of substituting another effect, and list whatever is + left unimplemented in the edit plan (phase 7). + +## Asking the user + +`AskUserQuestion` is for choices, so every call needs **at least two viable +options**. One option is not a question β€” state it in prose and carry on, never +padding it with a placeholder. Skip the call when the user or an upstream skill +already settled the answer, and use a numbered text menu only when the tool is +unavailable or the choice exceeds its cap. + +## Bootstrap + +Run this silently before the first `cawcut` operation in an AI session: + +1. If a `cawcut` command already succeeded in this session, skip bootstrap. +2. Otherwise run only `cawcut upgrade check --json`. If an update is available, + run `cawcut upgrade`. If the command is missing, ask the user to install it + with `npm install -g @ubnt/cawcut`. +3. Never run `cawcut auth login`; VN operations are local. + +## Workflow + +Follow the phases in order. Reuse facts already established by the user or an +upstream skill; do not ask the same question twice. + +### 0. Choose the VN action + +This skill is the only place that decides what to do with VN, regardless of +where the media came from: + +- The user invokes it directly (`/cawcut-vn` or an equivalent request) and + supplies their own local image/video/audio files to edit. This is a + first-class path, not a fallback β€” do not wait for or require an upstream + skill's output. +- The pai-pro `story-to-video-workflow` orchestrator hands off unconditionally + after delivering generated media β€” it does not pre-filter or ask its own VN + menu. + +1. Run `cawcut vn status` β€” see `references/capabilities.md` for exactly what + counts as "usable". Read `platform` as well: `"unsupported"` (the CLI is + running on Linux, e.g. inside the pai-pro Docker container) is a hard no β€” + local preview, `--open`, and `install` can never work there, whatever + `installed` says. Never offer or attempt them; phase 9 carries the + host-handoff message. +2. If an upstream skill or the user already stated the action (preview-only, + or editing), skip the question below and jump straight to the matching + branch β€” this is the same decide-once guard used for every other handoff. +3. Otherwise ask with `AskUserQuestion`, localized to `reply_language`: + - When VN is usable, offer three choices: **open a VN preview and import + now (recommended for a quick look)**, **enter the editing flow** (build a + rough cut), **not needed**. + - When VN is not usable (not installed, or below `VN_MIN_VERSION`), omit + the preview choice β€” offer only **enter the editing flow** and **not + needed**. Do not offer a choice that cannot succeed. + +Branches: + +- **Preview and import** (only reachable when VN is usable): run + `cawcut vn import <path_1> <path_2> ... --title "<name>" --open` with every + supplied local path. Reuse a name the conversation already established (the + user named the project, or an upstream skill delivered a titled result); + otherwise name it yourself from what the files are. Keep it to a few words β€” + this is the label VN shows in its project list, not a description. Report the + project URL and asset count. Stop β€” do not continue into the editing phases + below. +- **Not needed**: end without running any `cawcut vn` command. +- **Enter the editing flow**: continue to phase 1. This branch never depends + on VN being installed β€” drafting and validating a rough cut is fully local + and does not launch the app. + +### 1. Inventory the supplied media + +Before creating or changing a project: + +- Resolve every supplied path and classify it as image, video, music, or sound + effect. Record known video and audio durations and meaningful content/order + cues from the conversation, an upstream generation result when present, + filenames, or available local inspection tools. Never invent unseen + content. +- Identify missing, unreadable, duplicate, or unsupported files immediately. + Note here which videos carry their own soundtrack, and check the user's + request against the refusal list while the inventory is fresh β€” an + unsupported ask must be turned down now, not discovered at build time. +- Summarize the usable inventory to the user when it affects the edit. +- Do not generate replacement media. If generation is required, hand off to + `story-to-video-workflow`, then resume with its delivered output paths. + +### 2. Confirm the project shape + +For a new project, explicitly ask the user to choose `9:16`, `16:9`, or `1:1`. +Recommend one from the stated destination, but do not create the project until +the user chooses. Also establish the title, intended platform/use, and target +duration when they are not already known. + +For a copy of an existing draft, preserve its aspect ratio unless the user asks +for a new project; the CLI cannot change an existing draft's ratio in place. + +### 3. Recommend feasible editing styles + +#### Style + +Use the inventory and intended use to recommend a style. Offer the recommended +option first and at most two meaningful alternatives: + +- **Clean narrative** β€” chronological or semantic order, longer readable shots, + restrained `dissolve` transitions. Use `fade-black` or + `fade-white` only when a transition through color is intended. Fits + explanatory or product footage. +- **Rhythm montage** β€” shorter usable video ranges, stronger supported + transitions, and planned music/SFX cues. Offer only when enough varied + footage exists; beat detection and automatic beat sync are not supported. +- **Photo story** β€” paced still images and `dissolve` transitions. Fits + image-heavy sets. + +Adapt these rather than forcing a label. Keep every proposal within the +capability reference: no manual crop/reframe, overlays, speed ramps, styled +typography, audio fades, or other unsupported operations. + +Before continuing, use `AskUserQuestion` to let the user select the editing +style, unless they already named or clearly described one. Put the recommended +style first and label it recommended. Do not silently choose a style from the +inventory or defer this choice to the final plan approval β€” but offer only +styles this inventory can actually support, and when just one fits, say so in +prose instead of firing a one-option question (see **Asking the user**). + +#### Canvas fill mode and transitions + +Follow `references/capabilities.md` for `--scale-mode` and transition naming β€” +apply it, do not restate its reasoning here: default to `fill`, and only +switch to `fit` when the user explicitly wants the complete source frame kept +visible (call out the resulting letterbox/pillarbox space in the edit plan). +Do not infer `fit` merely because the source and project ratios look similar β€” +inspect their actual pixel ratios. Always name the exact canonical transition +type; never write or invoke the deprecated `fade` alias. + +A transition belongs to a **boundary**, not a clip (see capabilities.md β†’ +Transition model). Resolve "a transition on clip X" to X β†’ X+1 and always write +it that way. Never use `add-clip --transition`; append every clip first, then +set each boundary with `set-transition --index N`. + +### 4. Resolve music and sound effects + +Treat background music and sound effects as separate decisions. + +- If neither is supplied and the user has not requested silence, ask whether + they want to provide local BGM, local sound effects, both, or neither. +- If one category is supplied, ask only about the missing category when it + would materially improve the chosen style. +- If the user wants audio, wait for exact local paths. This skill does not find, + download, or generate audio by itself. +- Plan audio timing only after the visual sequence and approximate duration are + known. Do not claim automatic beat alignment. +- When an audio source is longer than the intended placement, select and show + a source range using `--trim-start` / `--trim-end` (see + `references/capabilities.md` for exact trim / `--duration` semantics). +- Added audio never replaces audio the source clips already carry. When any + supplied video has its own soundtrack and the user expects their BGM to + replace it, refuse that part here (see **Refusing what VN cannot do**) and + state in the plan that both tracks will play together. + +### 5. Resolve text + +**A supplied `.srt` is a separate path.** When the user hands over one or more +`.srt` files, do not ask the questions below β€” those cues already carry their +own wording and timings. Confirm only the placement (whether to shift the whole +block with `--at`), show the cue count and the resulting time span in the edit +plan, and import with `add-srt`. Never retype, re-time, or re-word the cues, and +never split a file into individual `add-text` calls. + +Ask whether the user wants text on this edit β€” this is a plain opt-in +question and belongs in the flow the same way phase 4 asked about music and +sound effects. Use `AskUserQuestion` once the visual sequence and style are +settled: **Add text** / **No text**. Skip the question only when the user +already requested text or supplied text content earlier in the conversation. + +If they decline, do not ask again β€” move on to phase 6 with no text. + +If they opt in (or already requested text), gather only what implementing it +needs: content, timing, and type (title/subtitle/caption). Do not invent +wording, timing, or type the user has not given or approved β€” asking whether +they want text at all is not the same as proposing what it should say. + +Text is an overlay and never extends the main-track or project duration. Infer +the intended placement from the user's wording; do not append an end-card clip +by default: + +- "during the final N seconds", "in the video's last N seconds", or equivalent + means overlay the text on the existing final visual clip; +- "for N seconds after all shots", "append a standalone end card", or + equivalent explicitly sequential wording means append a separate N-second + visual clip, then place the text over that clip; +- when the wording could mean either, ask one focused timing question before + presenting the edit plan instead of choosing silently. + +Only the second case authorizes an extra main-track clip. Never satisfy an +explicit "after all shots" request by shortening or covering the preceding +shot. Because the CLI cannot create a solid-color clip, require a supplied +end-card image or hand off its generation when that separate clip is requested. + +#### Font selection + +Every text type has a default font and point size (see +`references/capabilities.md`), but once text is confirmed, ask about the font +too β€” do not leave it silent. Skip straight to resolving the font when the user +already named or described one; otherwise use `AskUserQuestion` with: + +1. **Use the default (Recommended)** β€” the type's own default font and point + size; leave `--font`/`--font-file`/`--font-size` unset. +2. **Show me some recommendations** β€” run `cawcut vn fonts --json` and offer up + to 4 candidates (label = family + style, closest to the chosen edit style + first and marked **(Recommended)**). +3. **I'll provide my own font** β€” a font name, or a local file path via the + tool's free-text "Other" option (pass it to `--font-file`). + +Pass the user's point size through unchanged β€” it is the number VN's *Font Size* +box shows, never a canvas-relative basis. Font flag semantics and portability +are in `references/capabilities.md`. Whenever the edit plan includes text, list +its font and point size (phase 7). + +### 6. Confirm the cover + +For a new project, explicitly confirm the cover choice before presenting the +edit plan unless the user already specified it: + +- a supplied PNG used as an explicit photo cover via `set-cover`; +- another local PNG the user will provide; or +- VN's automatic first-frame cover. + +Use `AskUserQuestion` for this choice. Recommend a suitable supplied image when +one clearly represents the project; otherwise recommend the automatic first +frame. Do not confuse a timeline opening shot with an explicit VN cover. + +### 7. Present the edit plan and wait for approval + +Before running `init`, `add-clip`, `add-audio`, `add-sound-effect`, `add-text`, +`add-srt`, or `set-transition`, show a concrete plan containing: + +- project title, aspect ratio, selected style, cover choice, and estimated total duration; +- ordered shots with source path and image duration or video trim range; +- each transition as an explicit boundary `N β†’ N+1: <type> (<duration>s)`, + never attached to a single clip; +- only when text was explicitly requested: title/subtitle/caption content + with start time, duration, font, and point size (the same number VN's text + panel shows in its *Font Size* box); +- for a supplied `.srt`: the file name, cue count, any cues the parser will + skip, the resulting time span, and any `--at` shift; +- BGM and SFX paths with timeline start, source trim range, duration, and volume; +- every request this plan does **not** implement, as a named refusal or a + simplification β€” including one already refused earlier in the conversation. + Never let an unimplemented effect sit in the plan as if it were covered. + +A compact timeline table is preferred for multi-shot edits. After showing the +complete plan, **always use `AskUserQuestion` for the generation confirmation**; +do not ask for approval only in prose. Offer these choices, localized to the +user's language: + +1. **Create from this plan (Recommended)** β€” approve the displayed plan and + begin draft generation. +2. **Revise the plan** β€” collect the requested changes, update the full plan, + and call `AskUserQuestion` again. +3. **Cancel** β€” stop without creating or modifying a draft. + +Only the first choice authorizes phase 8. Do not treat silence, an unrelated +reply, or a previously stated general request to β€œmake a video” as approval. +This gate is important because the CLI appends clips and does not support +delete, reorder, or in-place clip editing. + +### 8. Build and verify the rough cut + +After approval: + +1. Initialize the draft, or copy an existing draft with `init --from`. Always + pass `--dir "$PWD/vn/<title-slug>"` so the draft lives inside the active + pai-pro project instead of the CLI's `~/Downloads` default, and reuse that + directory for every later `--project` flag. +2. Add clips in approved order, using video source trim where planned. +3. Apply the approved explicit cover when selected. Add BGM and SFX with the approved timeline start and source trim, then set + every approved transition with `set-transition --index N` (never + `add-clip --transition`). Add text only when it was explicitly requested and included in + the approved plan, and import an approved `.srt` with `add-srt` rather than + replaying its cues through `add-text`. +4. Run `show --json`, compare the result against the approved plan, and correct + any discrepancy that the CLI can express safely. A transition discrepancy is + always correctable in place with `set-transition` (`--type none` to clear) β€” + never rebuild the draft for a transition. If correction requires removing, + retiming, replacing, or reordering a clip, create a fresh draft from the + approved plan. +5. Run `validate`. Do not report completion until it passes. Never silently + drop a failed asset or substitute an unsupported effect. + +### 9. Ask whether to open VN + +Only after `show` matches the plan and `validate` passes, run `cawcut vn +status` again β€” do not reuse the phase 0 result, since the user may have +installed or upgraded VN since then (or phase 0 never checked it, because they +went straight into editing). + +- **Platform unsupported (`status.platform === "unsupported"` β€” Linux, e.g. + pai-pro in Docker):** never run `--open`, `install`, or `vn import --open`; + they cannot succeed here. Say plainly that VN cannot be opened in this + environment β€” the app runs on macOS and Windows only β€” then still run + `cawcut vn project pack --project <dir>` so the `.vn` deliverable exists (if + `pack` itself fails, relay the CLI's error, for example a missing `zip` on + Linux). Give the host handoff: the draft is at `<dir>`; copy it (or the + packed `.vn`) to a macOS/Windows machine with VN installed and open it there + (`cawcut vn project pack --open`, or double-click the `.vn`). In pai-pro + Docker that is `docker cp pai-pro:<dir> .` on the host, then opening the + copy. Do not ask a yes/no open question, and never imply the draft was + opened. +- **VN usable:** tell the user the rough cut is complete and ask whether to + open it in VN now. + - **Yes:** run `cawcut vn project pack --open --project <dir>`. If the open + fails, the CLI prints why and points at a route that works on that + platform β€” relay that rather than retrying the same command. + - **No:** run `cawcut vn project pack --project <dir>` so the `.vn` + deliverable is still ready without launching the app. +- **VN not usable (platform supported, but the app is missing or too old):** + skip the question β€” there is no working choice to offer. Run `cawcut vn + project pack --project <dir>` directly, and mention the VN download/upgrade + link as an optional aside. + +Report the draft directory, `.vn` path, duration, clip/audio/text counts, and +any deliberate simplifications. Do not open VN before this final choice. + +## Handoff boundaries + +- Media can arrive two ways: the user supplies their own local files directly + (treat this as the normal case, not an edge case), or an upstream skill + hands off its generated output. When it is the latter, trust its valid + paths, reuse any settled aspect, intent, or audio decisions, and trust a + pre-decided action (preview vs. editing) instead of re-asking phase 0. In + either case, still present the edit plan before timeline mutation. +- A request to only preview a few files, with no rough cut, is phase 0's + preview-and-import branch β€” it stays inside this workflow, it does not hand + off elsewhere. +- Keep commands, flags, and paths in English; converse in `reply_language`. diff --git a/skills/cawcut-vn/references/capabilities.md b/skills/cawcut-vn/references/capabilities.md new file mode 100644 index 0000000..48b69e6 --- /dev/null +++ b/skills/cawcut-vn/references/capabilities.md @@ -0,0 +1,442 @@ +# `cawcut vn` capability reference + +This file is the authority for what the VN skill can execute. All operations +are local. Project commands accept `--json`; changing project commands accept +`--project <dir>`, defaulting to the current directory only when it contains +`brief.json`. + +## Windows PowerShell: redirecting stderr can abort the script + +The CLI writes results to stdout, and progress, warnings and errors to stderr. Windows +PowerShell turns a native program's stderr into an ErrorRecord as soon as that stream is +redirected β€” `2>&1` and `2>$null` both count β€” and under `$ErrorActionPreference = 'Stop'` the +record is terminating. Measured: `cawcut vn project show 2>$null` aborts the script on a progress +line that reported no failure at all, while the same command without a redirect runs fine. + +When driving the CLI from PowerShell: + +- Leave stderr alone and read the result from stdout: `$out = cawcut vn project show --json`. +- If stderr has to be captured or silenced, relax the preference around the call and restore it: + + ```powershell + $prev = $ErrorActionPreference + $ErrorActionPreference = 'Continue' + $out = cawcut vn project show --json 2>$null + $code = $LASTEXITCODE + $ErrorActionPreference = $prev + ``` + +- Judge failure by `$LASTEXITCODE`, never by whether anything appeared on stderr. +- `--json` silences progress but not errors: a failing command still writes to stderr, so the + wrapper above is what makes the failure path safe. +- cmd.exe, Git Bash, macOS and Linux shells are unaffected. +## Capability boundary + +Supported rough-cut operations: + +- Create a `9:16`, `16:9`, or `1:1` project, or edit a copied draft. +- Append images and MP4/MOV/M4V videos to one main track. +- Fill the canvas by default, or preserve the complete source frame on request. +- Set image duration and video source in/out trim. +- Add background music and uploaded sound effects with timeline position, + source in/out trim, duration, and volume. +- Add semantic title, subtitle, or caption text with timeline position and + duration, each with a preset default font and point size. Optionally + override the font (a system font, a VN built-in font, or an imported + third-party font file) and the point size. +- Import one or more `.srt` files as VN subtitle objects, at their own + timings, optionally shifted as a block. +- Set one supported leaving (outgoing) transition per main-track clip + boundary. +- Set a supplied PNG as the explicit VN photo cover. +- Inspect and validate the resulting draft, package it as `.vn`, optionally + open it, or install it into the local VN draft library. + +Not supported: + +- Delete, reorder, replace, or edit an existing timeline item in place. A + clip's **transition** is the one exception β€” `set-transition` changes it in + place without a rebuild (see Transition model below). +- Multiple visual tracks, overlays, picture-in-picture, manual crop/reframe, arbitrary + position, speed changes, filters, color correction, or keyframe animation. +- Custom text color, alignment, stroke, or position. +- Audio fade, ducking, beat detection/sync, recording, or access to + VN's built-in music/SFX catalogs. +- Automatic speech recognition, or styled captions. +- Transcribing audio into subtitles: `add-srt` places text you already have, + it never listens to the media. There is no way to generate an `.srt`. +- Setting subtitle position, so subtitles that overlap in time render on top + of each other. +- Bilingual subtitle pairing (VN pairs translations by `captionID`, which + SRT-imported subtitles do not carry). +- **Removing, muting, or replacing the audio a source video already carries.** + An appended clip always plays its own audio track: there is no per-clip mute, + detach, or volume control, and `--volume` exists only on `add-audio` / + `add-sound-effect`, where it scales the *added* track. VN's draft format has a + `muteMainTracks` flag β€” every draft the CLI writes fixes it at `false` β€” but + no command exposes it, so hand-editing that flag is not a workaround this + skill may use. +- Separating one element of a source clip's sound from the rest (music vs. + speech vs. noise, vocal removal, stem separation), or balancing a source + clip's own audio against added BGM. + +Plan within these limits. If a confirmed edit must change item order or remove +an item, rebuild a fresh draft rather than editing JSON. + +## Transition model + +A transition is the **leaving (outgoing) transition of a clip**: it sits on the +boundary between clip N and clip N+1 and is stored on clip N in the draft. +There is no separate "incoming transition" on clip X β€” "the transition before +clip X" *is* clip X-1's leaving transition. + +- Always name a transition by its boundary (`N β†’ N+1`), never "on clip X". +- "A transition on clip X" resolves to clip X's leaving transition (X β†’ X+1). + When the user may mean the boundary *before* X (X-1 β†’ X), ask one focused + question and record the answer in the edit plan. +- The last clip has no leaving transition and must not be given one. + +Set every transition with `set-transition` after all clips are appended. It is +the in-place transition path: unlike clip order or content, a wrong transition +is fixed with `set-transition --index N` (`--type none` clears it) and **never** +by rebuilding the draft. + +## Common refusals β€” say it, never simulate it + +These are the requests most likely to be waved through as if they had worked. +Say the refusal out loud, in the user's language, before proposing a plan that +would look as though it had been fulfilled. + +| The user asks for | What is true | What to offer instead | +|---|---|---| +| "Remove the music / audio that comes with this video." | The clip's own audio always plays; nothing mutes, detaches, or replaces it. | A copy muted outside the skill and re-supplied as a local file. Adding BGM on top is supported, but it plays *alongside* the clip's own audio. | +| "Turn the original sound down, then put my music over it." | No ducking or per-clip audio automation; `--volume` on `add-audio` scales only the added track. | Add the BGM at the volume they want, and say the source audio has to be muted outside the skill first if it must drop. | +| "Keep only the music / cut out just the vocals." | No stem separation of any kind. | Nothing inside this skill β€” the media has to be re-prepared externally. | +| "Subtitle what they say in the video." | No speech recognition; `add-srt` only places a `.srt` that already exists. | They transcribe the audio elsewhere (or supply an `.srt`), then `add-srt` imports it. | +| "Delete that clip, or move it earlier." | Appends only β€” no delete, reorder, or in-place edit. | Rebuild a fresh draft from the corrected plan. | +| "Zoom into that part / add a decorative lower third / speed it up." | No crop/reframe, overlays, extra tracks, speed changes, or custom text styling β€” plain title/subtitle/caption text is supported, styling it is not. | The closest supported shot, transition, duration, or unstyled text; the difference is stated in the edit plan. | + +## Checking whether VN is usable + +```bash +cawcut vn status [--json] +``` + +Reports whether the VN app is installed and whether it meets the CLI's minimum +supported version. Two platforms are covered, each with its own install signal +and its own minimum: + +| Platform | "Installed" means | Minimum version | +|---|---|---| +| macOS | `/Applications/VN.app` carries the `maccatalyst.com.frontrow.vlog` bundle id | `1.4.0` (build `838`) | +| Windows | a VN uninstall entry exists (HKCU first, then HKLM and its WOW6432Node twin) **and** `vn_editor.exe` is present β€” at the entry's `InstallLocation`, else `%LOCALAPPDATA%\VN\` | `0.4.1.165` | + +A registry key without the executable is a stale uninstall, and reports as not +installed. On any other platform (Linux, …) local VN import is not supported at +all. On those platforms `status.platform` is `"unsupported"`; drafts can still +be built and packed, but opening them requires moving the draft (or its `.vn`) +to a macOS/Windows machine with VN installed. + +Treat VN as usable only when `"installed": true` and `"meetsMinVersion"` is +`true` or absent from the JSON; any other combination means the app cannot be +launched right now. Only `vn import --open`, `pack --open`, and `install` +actually need VN usable β€” every other command on this page is a pure local file +operation and works regardless of whether VN is installed. + +## Lightweight preview + +```bash +cawcut vn import <files...> [--title <title>] [--open] +``` + +Use only when the user wants to open source files in VN without assembling a +timeline. It is not a rough-cut project workflow. + +- `--title` becomes the manifest's `title` β€” the name VN gives the project it + creates. Unlike `init --title` it is optional, and nothing else in this path + carries a name, so an import without it lands unnamed. Keep it to a few + words: VN shows it in the project list. + +The files and their `manifest.json` are copied into a landing directory that +the `vn://createProject` link points at; `--open` hands that link to the OS +(`open` on macOS, PowerShell `Start-Process` on Windows β€” not `start`, which +fails in PowerShell). The manifest is identical on both β€” only +the landing root differs: + +| Platform | Landing directory | +|---|---| +| macOS | `~/Library/Containers/maccatalyst.com.frontrow.vlog/Data/tmp/url-scheme-create-project/<id>/` | +| Windows | `%TEMP%\vn-edit-project\<id>\` | + +## Discovering fonts + +```bash +cawcut vn fonts [--json] [--source system|vn|draft|all] [--query <text>] [--family <name>] [--project <dir>] +``` + +Lists fonts from up to three sources, merged and deduplicated by PostScript +name (`--source` narrows to one): + +- `system` β€” fonts installed on this machine. Not portable: a shared draft + falls back on a machine that doesn't have them. +- `vn` β€” VN's own bundled fonts, read from the installed app on both + platforms: the macOS app bundle's catalog and ODR language packs, and + `<install dir>\fonts\` on Windows (the Inter and Noto stack, including the + CLI's default `Inter-Regular`). Portable β€” every VN install on that platform + carries them. The list is empty when VN is not installed, and `--json` then + carries a `note` saying so; `"vnBuiltinFontsReadable": false` means the + platform has no catalog at all, which is neither macOS nor Windows. +- `draft` β€” fonts already imported into `<draft>/Font/` (needs `--project`). + Portable β€” the file travels with the draft. + +`--query` matches family or PostScript name case-insensitively; `--family` +matches a family name exactly. `--json` returns +`{ fonts: [{ family, style, postscriptName, source, portable }] }` β€” use it +to drive an `AskUserQuestion` font picker. + +## Project commands + +### `init` + +```bash +cawcut vn project init --title <title> --aspect 9:16|16:9|1:1 [--from <draft-dir>] [--dir <path>] +``` + +`--title` and `--aspect` are required unless `--from` is used. `--from` copies +an existing draft before editing. The CLI default output is +`~/Downloads/cawcut-vn-projects/<slug>-<id>/`; in pai-pro always override it with +`--dir "$PWD/vn/<title-slug>"` so the draft stays inside the active project. + +### `add-clip` + +```bash +cawcut vn project add-clip <files...> [--duration S | --trim-start S --trim-end S] [--scale-mode fill|fit] [--width PX --height PX] [--transition type:sec] [--project <dir>] +``` + +- Images: PNG/JPG/JPEG/GIF/WebP; default duration 3 seconds. +- Videos: MP4/MOV/M4V; duration and dimensions are probed automatically. +- Files append in argument order and are copied into `Asset/`. +- `--scale-mode` defaults to `fill`: images and videos cover the canvas without + distortion, cropping only the overflow. Use `fit` only when keeping the full + source frame is more important than avoiding letterbox or pillarbox space. +- Video trim uses source-media seconds. Either bound may be omitted; defaults + are source start/end. Trim is video-only and cannot combine with `--duration`. +- When video probing fails without trim, explicit `--duration`, `--width`, and + `--height` can provide fallback metadata. Trim requires successful probing. +- `--transition type:sec` sets the leaving transition of the **last existing + clip** (the boundary into the first newly appended file), not the new clip. + **Do not use it** β€” it is the wrong-boundary footgun this skill routes around. + Append clips first, then set every boundary explicitly with `set-transition`. +- When present, the same names, defaults, and duration safety checks as + `set-transition` apply, and it is ignored when the draft has no preceding + clip. + +### `add-audio` + +```bash +cawcut vn project add-audio <file> --at S [--duration S | --trim-start S --trim-end S] [--volume 0-1] [--project <dir>] +``` + +Adds BGM (`mType=0`) under `Music/Custom/`. `--at` is the timeline position. +`--trim-start` and `--trim-end` are source-audio coordinates; either bound may +be omitted and defaults to source start/end. Source trim requires successful +duration probing and cannot combine with `--duration`. Without trim, duration +defaults to the shorter of the remaining main track and the source audio when +the source duration is known. Volume defaults to 1. + +`mp3`, `m4a`, `wav` and `flac` are read from the file itself, so trim works on +every platform. Other formats fall back to an external probe β€” `afinfo` on +macOS, otherwise `ffprobe`, which a stock Windows machine does not have. When +the duration cannot be read, trim is refused outright: offer `--duration` +instead, or ask the user for one of the four formats above. + +### `add-sound-effect` + +```bash +cawcut vn project add-sound-effect <file> --at S [--duration S | --trim-start S --trim-end S] [--volume 0-1] [--project <dir>] +``` + +Adds an uploaded SFX (`mType=1`) under `SoundEffect/`. Timeline timing, source +trim, duration probing, and volume match `add-audio`. + +### `add-text` + +```bash +cawcut vn project add-text "<content>" --type title|subtitle|caption --at S \ + [--duration S] [--font <postscript-name>] [--font-file <path>] [--font-size <pt>] [--project <dir>] +``` + +`--type` is semantic: `title=2`, `subtitle=1`, `caption=0`; default is +`caption`. Each type carries a default point size and font, taken from VN's +own default-font-size rules: + +| Type | Default point size | Default font | +|------|---------------------|--------------| +| `title` | 36 | `Inter-Bold` | +| `subtitle` | 24 | `Inter-Regular` | +| `caption` | 17 | `Inter-Regular` | + +**Point sizes are VN's own text-size numbers, shown unchanged by VN.** The +value written into the draft is exactly the number the user reads in VN's +*Font Size* box, so pass the user's number straight through. Nothing in this +pipeline converts between units β€” see the `--font-size` bullet below. + +`--at` is required; duration defaults to 3 seconds. Text is an overlay: it +does not extend the main track or project duration. Only when the requested +timeline explicitly includes a standalone title/end card does it need its own +visual main-track clip, followed by text placed within that clip's time range. + +Font options (all optional β€” omit them to use the type's default): + +- `--font <postscript-name>` sets `fontName` to a font already available on + the machine or in VN (see `cawcut vn fonts`) β€” a system font, or one of + VN's own built-in fonts. When it does not match anything in the local + catalog, the command still succeeds (a warning is printed): `fontName` is a + free PostScript-name string in the VN format, and VN falls back if it + cannot resolve it. +- `--font-file <path>` imports a third-party `.ttf`/`.otf`/`.ttc` file into + `<draft>/Font/` (renamed to its PostScript name) and sets both `fontName` + and `fontFilePath`. Mutually exclusive with `--font`, except that a + multi-face `.ttc` requires `--font <postscript-name>` to pick which face to + import. +- `--font-size <pt>` overrides the type's default point size (a positive + number, capped at 400). **This is the same number VN's text panel displays in + its *Font Size* box, so pass the user's value verbatim.** Never convert it to + or from a canvas-relative "basis": a request for `32` means `--font-size 32`, + not `92.16` (`32 Γ— 1080 / 375`) and not `11.11` (`32 Γ— 375 / 1080`). VN's own + presets run from 13 (subtitles) to 54, so a request far outside that range is + usually a unit mistake β€” confirm with the user before running the command + rather than rescaling it yourself. + +A font from `--font` alone (no `--font-file`) is **not portable**: it relies +on the font already being present wherever the draft is opened. Only fonts +under `<draft>/Font/` (imported via `--font-file`) or VN's own built-in fonts +travel with the draft. + +### `add-srt` + +```bash +cawcut vn project add-srt <files...> [--at S] [--font-size <pt>] \ + [--allow-duplicate] [--force] [--project <dir>] +``` + +Imports one or more `.srt` files as **VN subtitle objects** β€” items with +`titleType = 81001` (`FRVE2TitleStickerTypeSRTCaption`) in `mSubtitleItems`, +each carrying its own start time and duration. This is what makes VN show them +as subtitles; adding an `.srt` any other way (for instance through +`vn import`) only files it as a plain asset. + +- Cues keep the timings in the file. `--at S` shifts **every** cue later by `S` + seconds (default `0`); it applies to all files at once. +- Each file's cues are appended in file order, one shared z-layer for the whole + batch, matching VN's own import. +- The SRT caption preset VN uses is **`Inter-Bold` at 13** with a black stroke, + anchored to the bottom of the frame. `--font-size` overrides only the point + size, and takes the same number VN's text panel shows β€” no unit conversion. +- The parser is deliberately lenient: a missing cue index, `\n` or `\r\n` + endings, a BOM, a one-digit seconds field, and `.` as the millisecond + separator are all accepted. Cues with no text, no parsable time range, or an + end at or before their start are **skipped and counted**, never written β€” + an empty cue would also fail `validate`. +- Re-importing into a draft that already has SRT subtitles is refused, because + the CLI cannot delete subtitle items. `--allow-duplicate` appends a second set + anyway. +- More than 200 cues prints a warning; more than 500 is refused unless `--force` + is passed. VN rebuilds the entire project on every edit, so a large subtitle + track makes the draft slow from that point on β€” not just during the import. +- Cues that overlap in time, or that run past the end of the main track, only + print a warning. Subtitles share one anchor, so overlapping cues draw on top + of each other; and text never extends the project duration. + +### `set-cover` + +```bash +cawcut vn project set-cover <file.png> [--project <dir>] +``` + +Sets an explicit VN photo cover (`type=2`), copies the source and final cover +under `Cover/`, and updates the page thumbnail. The input must currently be a +PNG. Without this command, VN uses its automatic first-frame cover. + +### `set-transition` + +```bash +cawcut vn project set-transition --index N --type <name> [--duration S] [--project <dir>] +``` + +Sets zero-based clip N's leaving (outgoing) transition into N+1. This is the +only transition entry point this skill uses, and the in-place fixup: a wrong +transition is corrected here without rebuilding the draft (`--type none` +clears it). It rejects the last clip, because there is no following clip, and +rejects durations that VN would disable or clamp for the adjacent clip +lengths. Explicit non-`none` durations must be 0.2–5 seconds; the effective +maximum can be lower for short clips. + +| Family (default) | CLI names β†’ VN types | +|------------------|----------------------| +| None (0s) | `none` β†’ 1000 | +| Fade/dissolve (0.8s) | `fade-black` β†’ 1001; `fade-white` β†’ 1002; `dissolve` β†’ 1003; `color-difference-dissolve` β†’ 2001 | +| Slide (0.6s) | `slide-top`, `slide-left`, `slide-bottom`, `slide-right` β†’ 1004–1007 | +| Wipe (0.6s) | `wipe-top`, `wipe-left`, `wipe-bottom`, `wipe-right` β†’ 1008–1011 | +| Blur/reveal (0.8s) | `blur` β†’ 1012; `reveal-vertical`, `reveal-horizontal` β†’ 1013–1014 | +| Zoom/rotate blur (0.8s) | `zoom-blur`, `zoom-blur-reverse`, `rotate-blur`, `rotate-blur-reverse` β†’ 1015–1018 | +| Circle (0.6s) | `circle-crop`, `circle-crop-reverse` β†’ 1019–1020 | +| Pixelate (0.8s) | `pixelate` β†’ 1021 | +| Push (0.6s) | `push-top`, `push-left`, `push-bottom`, `push-right` β†’ 1022–1025 | +| Blink (0.4s) | `blink` β†’ 2011 | +| Shake (0.8s) | `shake-top-left`, `shake-bottom-left`, `shake-bottom-right`, `shake-top-right` β†’ 2021–2024; `shake-top`, `shake-left`, `shake-bottom`, `shake-right` β†’ 2031–2034 | +| Spin/light/rotate zoom (0.8s) | `spin`, `spin-reverse` β†’ 2041–2042; `floodlight` β†’ 2051; `rotate-zoom`, `rotate-zoom-reverse` β†’ 2061–2062 | + +`fade` remains accepted as a deprecated compatibility alias for `dissolve`. +Plans and commands must use the unambiguous canonical name `dissolve`; use +`fade-black` or `fade-white` only when the transition through a color is +intended. Prefer the broadly supported 1000-series effects. Color difference, +blink, shake, spin, floodlight, and rotate zoom require VN's Metal-capable +renderer and may be unavailable on older/non-Metal environments. + +Script transitions (types 1101/1102) are intentionally unsupported because a +valid draft also needs a `scriptUUID` and its `.vntransition` asset package. + +### `show` + +```bash +cawcut vn project show [--project <dir>] [--json] +``` + +Returns project metadata, cover mode, and all main-track clips, video source trim ranges, +transitions, audio kinds/timing/source trim/volume, and text kinds/timing. + +### `validate` + +```bash +cawcut vn project validate [--project <dir>] [--json] +``` + +Checks the non-empty gapless main track, media references/files, geometry, +video trim and duration consistency, audio type/path and source-range validity, +and step/brief metadata. Any error returns exit code 1. + +### `pack` + +```bash +cawcut vn project pack [--project <dir>] [-o <output.vn>] [--open] [--json] +``` + +Validates and packages the complete draft as `.vn`. The default output is next +to the draft directory. `--open` hands the archive to the platform's file +handler (`open` on macOS, `Start-Process` on Windows) so VN can import it; use +it only after the user confirms opening. Archiving itself works on both +platforms. On Windows, VN PC has not been confirmed to register a `.vn` +handler β€” if the open fails the CLI says so and points at `cawcut vn import +--open`, which does not need one. + +### `install` + +```bash +cawcut vn project install [--project <dir>] [--open] [--force] [--json] +``` + +Special-case macOS delivery path that validates and copies the draft into +VN's local draft library. VN must be quit unless `--force` is used. Use it +only when `pack` does not work for some reason; read +[install.md](install.md) for details and troubleshooting. diff --git a/skills/cawcut-vn/references/install.md b/skills/cawcut-vn/references/install.md new file mode 100644 index 0000000..dafb845 --- /dev/null +++ b/skills/cawcut-vn/references/install.md @@ -0,0 +1,90 @@ +# Getting a CLI-built draft into the VN app + +Delivery and troubleshooting details for the capabilities documented in +[capabilities.md](capabilities.md). Path 1 is the normal route; paths 2 and 3 +are special-case fallbacks, not routine alternatives β€” only reach for them +when path 1 genuinely does not work. + +**Platform scope.** Path 1 (`pack`) runs on macOS and Windows. The archive is +written with the system `zip` on macOS and with `System.IO.Compression` through +PowerShell on Windows; both produce the same entry layout, and neither needs +anything installed. What differs is `--open`: on macOS VN imports the `.vn` +through the document handler it registers, and VN PC has not been confirmed to +register one. On Windows, prefer moving the `.vn` to the machine where it will +be opened, or use the lightweight preview β€” `cawcut vn import --open`, which +lands under `%TEMP%\vn-edit-project\` and runs on both platforms, see +[capabilities.md](capabilities.md) β†’ Lightweight preview. + +Paths 2 and 3 (`install`, and the manual copy) deliver into VN's Mac Catalyst +container, so they stay macOS only. + +## 1. Primary: pack a `.vn` and open it + +```bash +cawcut vn project pack --open --project <draft-dir> +``` + +`pack` zips the draft directory into `<draft-dir>.vn` (next to the draft, or +`-o <path>`), keeping the parent-directory layout VN expects. `--open` hands +the file to the platform's handler β€” `open` on macOS, `Start-Process` on +Windows β€” and VN imports it into its project list. **Verified on VN Mac +1.4.0-837**: a plain zip (no password, no `.projectConfig`) imports fine. VN +does not need to be quit first. That hand-verification is macOS-only; on +Windows the archive is written to the same layout, but whether the open +succeeds depends on VN PC registering a `.vn` handler. + +## 2. Alternative: copy into the draft library + +Only use this when path 1 does not work for some specific reason β€” it is not +a routine substitute for `pack --open`. + +```bash +cawcut vn project install --project <draft-dir> [--open] +``` + +Copies the draft into VN's draft library as the next numeric id +(`max existing + 1`): + +``` +~/Library/Containers/maccatalyst.com.frontrow.vlog/Data/Documents/FRVideoEditor/Draft/<n>/ +``` + +**VN must be quit** β€” the draft list may be cached at launch, and the command +refuses while VN is running (`--force` overrides; the draft may not appear +until VN restarts). Verified: VN discovers copied drafts at launch β€” the +sibling `config` plist (`latestId` etc.) does not need to be updated, and the +CLI leaves it alone. macOS only. + +## 3. Fallback: manual copy + +Same as path 2, by hand. **Quit VN completely first**, then: + +```bash +DRAFT="<draft-dir>" # the --dir passed to `cawcut vn project init` +VN_LIB="$HOME/Library/Containers/maccatalyst.com.frontrow.vlog/Data/Documents/FRVideoEditor/Draft" +NEXT=$(( $(ls "$VN_LIB" | grep -E '^[0-9]+$' | sort -n | tail -1) + 1 )) +cp -R "$DRAFT" "$VN_LIB/$NEXT" +``` + +Launch VN β€” the draft appears in the project list with the title from +`init --title`. Open it, edit, save: VN rewrites the directory in place, so +future CLI edits should target a **fresh copy** (`--from "$VN_LIB/<n>"`) +rather than the installed directory. + +## Notes and troubleshooting + +- **Keep the directory whole.** `brief.json`, `BriefSteps/`, the page + directory, `Asset/`, `Music/Custom/`, and `SoundEffect/` when present must + travel together β€” copying only some files produces a draft that fails to + open. +- **Do not rename internal files.** Only the outer directory name (`<n>`) + changes; everything inside stays as the CLI wrote it. +- **Draft does not appear in VN** β€” confirm VN was fully quit during the copy + (paths 2/3), the folder sits directly under `Draft/` with a numeric name, + and `cawcut vn project validate --project <dir>` passes on the source directory. +- **VN version** β€” drafts built by the CLI target the current VN desktop + format (v139). If VN refuses to open the draft, update VN from + https://vlognow.me/download/ and retry. +- **Round-trip editing** β€” after VN has opened and saved the draft, edit it + further by copying it out again (`cawcut vn project init --from + "$VN_LIB/<n>"`), not by pointing the CLI at the installed copy. diff --git a/skills/story-to-video-workflow/SKILL.md b/skills/story-to-video-workflow/SKILL.md index 392210d..81a75b6 100644 --- a/skills/story-to-video-workflow/SKILL.md +++ b/skills/story-to-video-workflow/SKILL.md @@ -30,7 +30,7 @@ Use this ladder unless the user skips, reorders, supplies refs, or asks for a ro 6. Confirm shot count, durations, continuity needs, and first blocker. 7. Default render path: straight-to-video from refs. Storyboard only if requested, hard to control, or needed for diagnosis. 8. Default dispatch: hybrid. Chain continuous dependent shots; render independent scenes/shots in parallel. -9. Render clips, assign Timeline `shot_id` when sequence order is unambiguous, then hand off to Timeline. +9. Render clips and assign Timeline `shot_id` when sequence order is unambiguous. Then hand off to Timeline inspection, and to `cawcut-vn` when the user wants an editable cut. Plan ahead internally, but only ask the next meaningful user-facing choice; the Consent and gates ladder fixes when render path and dispatch become askable. @@ -43,6 +43,7 @@ Plan ahead internally, but only ask the next meaningful user-facing choice; the | Narration, dialogue read, character voice, or audio node | `voice-compose` | | Clip render, continuation, audio refs, storyboard animation, or video prompt | `video-compose` | | Scene/ref grouping or canvas layout frames | `groups-compose` | +| Assemble the landed media into an edited cut in the VN app | `cawcut-vn` | Capability skills own CLI flags, node grammar, refs, and recovery hints. `PROJECT_AGENT.md` owns shared failure handling. @@ -126,6 +127,7 @@ Typical priority: - Voice landed -> recommend using it with the matching visual ref in the next dialogue/narration clip. - Storyboard landed -> recommend review or animating the matching clip. - Video clip landed -> recommend the next clip, or Timeline handoff when all planned clips are ready. +- All planned clips landed and the user wants a finished cut -> recommend `cawcut-vn` (VN editing) with the landed `local_path`s. ## Final handoff @@ -137,4 +139,13 @@ node "$PAI_REPO_ROOT/server/cli/canvas_mutate.js" \ --payload-json '{"updates":[{"id":"<video_1>","patch":{"shot_id":1}},{"id":"<video_2>","patch":{"shot_id":2}}]}' ``` -Do not use `generate_video.js --shot-id` for speculative/partial ordering. Assign after clips land. Local export uses `reel_stitch.js` only on explicit request. Then tell the user to open Timeline to inspect and preview. +Do not use `generate_video.js --shot-id` for speculative/partial ordering. Assign after clips land. Local export uses `reel_stitch.js` only on explicit request. + +When the run is complete β€” every planned clip landed and Timeline order set β€” run `cawcut vn status` before offering VN, then close with one next step using the project choice shape (Β§ "Recommendation and choice shape"): + +- **Open in VN** β€” hand the landed clip `local_path`s to `cawcut-vn` to build, validate, and open an editable VN draft. Offer it only when VN is usable (`installed` and `meetsMinVersion`); when the app is missing or too old, still offer it as "build a draft" but say the app must be installed or updated before it can open. +- **Inspect Timeline** β€” review and preview the reel order on the canvas first. + +When `cawcut vn status` reports `platform` as `"unsupported"` (Linux, e.g. Docker), drop **Open in VN** entirely β€” VN cannot open there. Offer **Inspect Timeline**, or building a draft to move to a macOS/Windows host. + +`cawcut-vn` owns the VN conversation from there (draft assembly, validation, opening the app), so stop the story orchestration once the user picks it. VN editing is a separate handoff, not another generation; never route it back through `video-compose`.