$npx -y skills add pandazki/pneuma-skills --skill skillAI-orchestrated video production on @pneuma-craft. Use whenever the user wants to generate, edit, or compose video clips, audio tracks, captions, or background music — including text-to-video / image-to-video generation, TTS narration, music generation, provenance tracking, and t
| 1 | # ClipCraft |
| 2 | |
| 3 | ClipCraft is a video-production mode where the **source of truth is a |
| 4 | structured domain model**, not a file. The in-memory model is an |
| 5 | event-sourced craft store from `@pneuma-craft`: an Asset registry, a |
| 6 | Composition with Tracks and Clips, and a Provenance DAG that tracks |
| 7 | how each asset was generated (and from what). The file `project.json` |
| 8 | at the workspace root is a projection of that store — when you edit |
| 9 | it with Write/Edit, the viewer auto-re-hydrates. No reload, no |
| 10 | refresh signal. |
| 11 | |
| 12 | ClipCraft is built for **AIGC workflows**: assets are generated, not |
| 13 | uploaded. You orchestrate image / video / TTS / BGM generation by |
| 14 | running bundled scripts, then record the lineage in `project.json`. |
| 15 | |
| 16 | ## Working with the viewer |
| 17 | |
| 18 | The viewer is the exploded 3D timeline rendering of `project.json`. |
| 19 | It is the user's source of truth for what's currently selected, what |
| 20 | the playhead is on, and what they're pointing at. Four channels link |
| 21 | the three actors (you, the user, the viewer): |
| 22 | |
| 23 | ### Reading what the user sees |
| 24 | |
| 25 | Every user message arrives wrapped in `<viewer-context>` and (if the |
| 26 | user just clicked / dragged / seeked) `<user-actions>`. Read them |
| 27 | before you act. Typical clipcraft payloads: |
| 28 | |
| 29 | - `<viewer-context>` — `selectedClipId`, `selectedAssetId`, |
| 30 | `selectedTrackId`, `playheadTime` (seconds), `composition.duration`, |
| 31 | the active asset's metadata. Use this to disambiguate a vague |
| 32 | request like "try another take" — there's almost always a clip |
| 33 | selected that tells you which one. |
| 34 | - `<preview-frames>` (nested inside `<viewer-context>`) — summary of |
| 35 | the planning layer: `total="N"` attribute + per-track `<track id="..." |
| 36 | name="..." count="..." />` lines for tracks that carry preview frames. |
| 37 | When `total="0"` (or the tag is absent), the timeline has no |
| 38 | planning layer yet — a fresh project. Use this to decide whether |
| 39 | to start with sketch overlay (see `references/storyboard-workflow.md`) |
| 40 | vs jumping straight to generation. |
| 41 | - `<user-actions>` — recent UI events: `playhead:seek` |
| 42 | (`{time}`), `clip:select` (`{clipId, trackId}`), |
| 43 | `asset:select` (`{assetId}`), `clip:drag` (`{clipId, startTime, |
| 44 | trackId}`), `track:toggle-mute`, `track:toggle-visible`. Treat |
| 45 | these as hints, not commands — the user usually expects you to |
| 46 | read them and act, not echo them back. |
| 47 | |
| 48 | If both are absent (cold start, command-button click), inspect |
| 49 | `project.json` directly and ask if intent is ambiguous. |
| 50 | |
| 51 | ### Locator cards |
| 52 | |
| 53 | After creating or editing assets, clips, or moving the playhead, |
| 54 | embed `<viewer-locator>` cards so the user can jump straight to the |
| 55 | change. Emit one card per distinct thing you changed — a newly |
| 56 | generated asset, a clip you just placed, a time beat you built |
| 57 | around — not one per response. The user sees these as clickable |
| 58 | chips in chat. Use short concrete labels — "新的 VO 开场", |
| 59 | "panda clip on Main", "3.5s — punchline beat" — not generic ones |
| 60 | like "see asset". |
| 61 | |
| 62 | #### ViewerAddress — naming an object in the preview |
| 63 | |
| 64 | A `<viewer-locator>` carries an **address** — a small JSON object that |
| 65 | names one referent inside the viewer. clipcraft's address vocabulary |
| 66 | is timeline-oriented; every key below is **coarse** (each one stands |
| 67 | alone and drives navigate-then-flash — clipcraft has no element-level |
| 68 | fine handle): |
| 69 | |
| 70 | | Key | Coarse/fine | Names | |
| 71 | |---|---|---| |
| 72 | | `clipId` | coarse | a clip on a track — selects it + seeks the playhead to its start | |
| 73 | | `previewFrameId` | coarse | a planning sketch / anchor frame — selects its asset + seeks to its anchor time | |
| 74 | | `assetId` | coarse | an entry in the asset library — scrolls + flashes the tile/row | |
| 75 | | `trackId` | coarse | a track header — scrolls + flashes it | |
| 76 | | `time` | coarse | a moment on the timeline — seeks the playhead only | |
| 77 | |
| 78 | Supply exactly one key per address. `clipId` / `previewFrameId` / |
| 79 | `assetId` / `trackId` are stable ids (survive move / re-pace), so |
| 80 | locator references stay valid; `time` is a raw second. |
| 81 | |
| 82 | ```html |
| 83 | <!-- assetId — scrolls the asset library to the asset and flashes it. --> |
| 84 | <viewer-locator address='{"assetId":"asset-vo-tagline"}'>新的 VO 开场</viewer-locator> |
| 85 | |
| 86 | <!-- clipId — selects the clip on the timeline AND seeks the playhead |
| 87 | to its startTime, so the us |