Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ 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
# ca-certificates HTTPS to providers
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg \
poppler-utils \
zip \
tini \
curl \
bubblewrap \
Expand Down Expand Up @@ -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"]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Open Docker at <http://localhost:7588> or local at <http://localhost:7443>.
- 💬 [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
Expand Down
5 changes: 4 additions & 1 deletion agent-templates/PROJECT_AGENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 2 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 7 additions & 1 deletion docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,20 @@ 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
default agent CLI.
- 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
Expand Down
4 changes: 3 additions & 1 deletion docs/skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.

Expand All @@ -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/<slug>/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/<slug>/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

Expand Down
83 changes: 83 additions & 0 deletions docs/vn-editing.md
Original file line number Diff line number Diff line change
@@ -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/<id>/vn/<title>
```

## 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
8 changes: 8 additions & 0 deletions server/__tests__/docker_config_static.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Loading