[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6HQMY9FZPYTJ27C7P20X43D":3,"skill-same:01M6HQMY9FZPYTJ27C7P20X43D":81},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":53,"linkOnly":54,"markdownContent":55,"markdownBody":56,"frontMatter":57,"refName":58,"commitSha":59,"skillDirectory":60,"forksCount":61,"repositoryHtmlUrl":62,"repositoryHomepageUrl":63,"ownerHtmlUrl":64,"ownerBlogUrl":65,"ownerDescription":6,"updatedAt":66,"install":67,"resources":80},"01M6HQMY9FZPYTJ27C7P20X43D","filmcraft",null,"Headless non-linear video editing with FilmCraft (~700 commands), provisioned on demand by pi-craft - import clips, build a sequence, place\u002Finsert\u002Ftrim media on the timeline, add effects, export h264+aac mp4. Use for \"assemble these clips into one video\", \"put clip B after clip A\", \"cut a section out of this mp4\", \"export the edit as an mp4\", \"make a rough cut from these shots\".","BlackBeltTechnology\u002Fpi-agent-dashboard","BlackBeltTechnology","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F8101592?v=4","01M6HQDX7QAJA0QP4NYRYBMJ8A","BlackBelt Technology","01M6HQDXCC7719N9YDMQVH9JS6",348,"MIT","https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology\u002Fpi-agent-dashboard\u002Fblob\u002Fdevelop\u002Fpackages\u002Fcraft-apps\u002F.pi\u002Fskills\u002Ffilmcraft\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[5,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52],"craft","effects","sequence","trim","for","headless","with","make","add","import","cut","as","commands","linear","build","clip","export","assemble","use","pi","edit","editing","mp4","video editing","Media","Video","2026-11-10 10:12:30",false,"---\nname: filmcraft\ndescription: Headless non-linear video editing with FilmCraft (~700 commands), provisioned on demand by pi-craft - import clips, build a sequence, place\u002Finsert\u002Ftrim media on the timeline, add effects, export h264+aac mp4. Use for \"assemble these clips into one video\", \"put clip B after clip A\", \"cut a section out of this mp4\", \"export the edit as an mp4\", \"make a rough cut from these shots\".\n---\n\n# FilmCraft — headless video editing\n\nFilmCraft is ArtCraft's non-linear editor (projects, bins, sequences, tracks, clips, effects). `pi-craft`\nprovisions the headless `filmcraft-cli` (no desktop app). Choosing between apps, the discover → execute →\nsave → verify loop and the MCP policy live in `craft-studio`; this skill is the FilmCraft dialect and its traps.\n\n## What it is for \u002F formats\n\nEdits video + audio on a timeline. Reads common media (mp4, mov, wav…; `probe \u003Cmedia>` shows what it sees);\nexports `h264 hevc prores dnxhr mjpeg gif png tiff wav aac …` (`export --list-presets`). Project file: `.fcproj`.\nFor motion graphics \u002F alpha titles use EffectCraft (hand-off: mov ProRes 4444); for audio work SoundCraft (wav).\n\n## CLI dialect\n\n`pi-craft filmcraft exec \u003Cargs…>` passes `\u003Cargs…>` to `filmcraft-cli`. **`exec` appears twice** for a single\ncommand: the first is `pi-craft`'s verb, the second is `filmcraft-cli`'s subcommand.\n\n```bash\npi-craft filmcraft exec commands [filter] [--json]        # list ids (filter = substring; --filter word also works)\npi-craft filmcraft exec describe \u003Cid>                     # one command: params + \"enabled\" right now\npi-craft filmcraft exec [opts] exec \u003Cid> key=value …      # one command (value is JSON if it parses: seconds=3.5 item=2)\npi-craft filmcraft exec [opts] exec \u003Cid> '{\"seconds\":3}'  # same, params as one JSON object\npi-craft filmcraft exec [opts] run script.jsonl [--keep-going]   # one {\"id\",\"params\"} per line, # comments ok\npi-craft filmcraft exec import a.mp4 b.mp4                # prints the new project item ids\npi-craft filmcraft exec [opts] export out.mp4 --format h264 [--start S --end S] [--scale 0.5] [--no-audio]\npi-craft filmcraft exec [opts] inspect [project|sequence] [--compact]    # JSON tree; ids, clip times (ticks)\npi-craft filmcraft exec probe clip.mp4\n```\n\n**Options go before the subcommand**: `--project p.fcproj`, `--save`, `--save-as new.fcproj`, `--keep-going`\n(for `run`), `--compact` (one-line JSON). Times are ticks (254016000000 per second); most commands also take\n`seconds=`, `frame=` or `timecode=`.\n\n## State and persistence\n\nEach invocation loads the project, runs, and exits. **The project file is the only state**: pass\n`--project p.fcproj` to open it, add `--save` (in place) or `--save-as other.fcproj` to write it back.\nWithout a save flag, edits are discarded. A new project is made by `--save-as p.fcproj import …`\n(`--project` on a missing file fails). **Transient editor state is *not* saved**: the Source monitor\n(`source.open`) and the targeted tracks do not survive to the next invocation, so\n`source.open` → `source.overwrite` must be steps of one `run` script — separate `exec` calls fail with\n\"no clip in the Source monitor\". Rendering also creates a `FilmCraft Previews\u002F` folder next to the cwd.\n\n## Discovery first\n\nIds change between releases — never guess them.\n\n```bash\npi-craft filmcraft exec commands razor                    # id, label, shortcut, param schema\npi-craft filmcraft exec describe source.overwrite         # \"enabled\": false + why, when it cannot run now\npi-craft filmcraft exec inspect project --compact         # item ids (use these for source.open item=…)\npi-craft filmcraft exec export --list-presets h264\n```\n\n## Traps\n\n- **`source.overwrite` and `source.insert` place the source clip at the *current playhead*** — not at \"the end\"\n  and not at a time you meant. Always set it explicitly with `playhead.set` (`seconds=`) immediately before each\n  placement. Where it sits after `file.newSequence` depends on the steps before (seen at 0 s inside one run, at the sequence end after a reopen); never rely on it.\n- **A mistyped id in a script fails only that line.** With `--keep-going` the run continues and exits 1 *after*\n  the later steps ran: `source.overwrite` then lands at the wrong time and the export is a **valid but wrong**\n  file (observed 3 s instead of 5 s, an `a.mp4` overwritten by `b.mp4`). Without `--keep-going` the run stops at\n  the first failure, exits 1 and saves nothing. So: discover ids, avoid `--keep-going` for assembly,\n  and always `pi-craft verify` the duration \u002F a frame of the export.\n- `source.overwrite` replaces what is under the placed range; `source.insert` ripples later media to the right.\n  Use overwrite to extend at the end, insert to splice in the middle.\n- `file.newSequence` with `fromItem=\u003Cid>` already places that item at 0; `source.open item=\u003Cid>` is then needed\n  for every *further* clip. Targeting (`timeline.setTargeting`) decides which tracks receive the source's video\n  and audio; the default of a fresh sequence takes both.\n- `--project` must exist; use `--save-as` for the first save. `export` renders the *active* sequence.\n- Exit `1` also means \"offline media rendered as placeholders\" — fix paths instead of `--allow-offline`.\n- Linux needs glibc ≥ 2.35. `.pi\u002Fmcp.json` is only written by `pi-craft filmcraft mcp --register` (opt-in).\n\n## MCP route (opt-in)\n\n`pi-craft filmcraft mcp --register` adds a `craft-filmcraft` entry (17 tools: `command_run`, `command_batch`,\n`media_import`, `render_frame`, `render_preview`, … plus `ui_*`). **The FilmCraft MCP server is not path-confined**\n(it reads and writes any path you can), so `mcp` \u002F `--register` need `--allow-unconfined` — tell the user\nfirst. Prefer the bash route. Bridging to a running GUI (`--bridge`) is out of scope.\n\n## Recipes\n\n```bash recipe: assemble-two-clips\n# a.mp4 (3 s) then b.mp4 (2 s) in one sequence; b is placed at an explicit time.\nffmpeg -v error -y -f lavfi -i testsrc=size=640x360:rate=25:duration=3 -f lavfi -i sine=frequency=440:duration=3 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac a.mp4\nffmpeg -v error -y -f lavfi -i smptebars=size=640x360:rate=25:duration=2 -f lavfi -i sine=frequency=880:duration=2 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac b.mp4\npi-craft filmcraft exec --save-as p.fcproj import a.mp4 b.mp4        # items 1 and 2\ncat > assemble.jsonl \u003C\u003C'EOF'\n{\"id\":\"file.newSequence\",\"params\":{\"name\":\"cut\",\"width\":640,\"height\":360,\"fps\":25,\"fromItem\":1}}\n{\"id\":\"source.open\",\"params\":{\"item\":2}}\n{\"id\":\"playhead.set\",\"params\":{\"seconds\":3}}\n{\"id\":\"source.overwrite\",\"params\":{}}\nEOF\npi-craft filmcraft exec --project p.fcproj --save run assemble.jsonl\npi-craft filmcraft exec --project p.fcproj export out.mp4 --format h264\npi-craft verify out.mp4 --duration 5±0.1 --has-video --has-audio --size 640x360 --codec video=h264 --codec audio=aac \\\n  --frame-diff 4:b.mp4@1\\\u003C2\n```\n\n```bash recipe: place-at-explicit-time\n# Leave a 1 s gap: b is placed at 4 s, so the 3 s clip a ends before it. The explicit playhead.set decides.\nffmpeg -v error -y -f lavfi -i testsrc=size=640x360:rate=25:duration=3 -f lavfi -i sine=frequency=440:duration=3 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac a.mp4\nffmpeg -v error -y -f lavfi -i smptebars=size=640x360:rate=25:duration=2 -f lavfi -i sine=frequency=880:duration=2 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac b.mp4\npi-craft filmcraft exec --save-as p.fcproj import a.mp4 b.mp4\ncat > gap.jsonl \u003C\u003C'EOF'\n{\"id\":\"file.newSequence\",\"params\":{\"name\":\"gap\",\"width\":640,\"height\":360,\"fps\":25,\"fromItem\":1}}\n{\"id\":\"source.open\",\"params\":{\"item\":2}}\n{\"id\":\"playhead.set\",\"params\":{\"seconds\":4}}\n{\"id\":\"source.overwrite\",\"params\":{}}\nEOF\npi-craft filmcraft exec --project p.fcproj --save-as gap.fcproj run gap.jsonl\npi-craft filmcraft exec --project gap.fcproj export gap.mp4 --format h264\npi-craft verify gap.mp4 --duration 6±0.1 --has-audio --frame-diff 4.5:b.mp4@0.5\\\u003C2\n```\n\n```bash recipe: insert-and-range-export\n# source.insert at 1 s splices b into the middle of a (3 s + 2 s = 5 s); then export only 1 s … 3 s.\nffmpeg -v error -y -f lavfi -i testsrc=size=640x360:rate=25:duration=3 -f lavfi -i sine=frequency=440:duration=3 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac a.mp4\nffmpeg -v error -y -f lavfi -i smptebars=size=640x360:rate=25:duration=2 -f lavfi -i sine=frequency=880:duration=2 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac b.mp4\npi-craft filmcraft exec --save-as p.fcproj import a.mp4 b.mp4\ncat > ins.jsonl \u003C\u003C'EOF'\n{\"id\":\"file.newSequence\",\"params\":{\"name\":\"ins\",\"width\":640,\"height\":360,\"fps\":25,\"fromItem\":1}}\n{\"id\":\"source.open\",\"params\":{\"item\":2}}\n{\"id\":\"playhead.set\",\"params\":{\"seconds\":1}}\n{\"id\":\"source.insert\",\"params\":{}}\nEOF\npi-craft filmcraft exec --project p.fcproj --save-as ins.fcproj run ins.jsonl\npi-craft filmcraft exec --project ins.fcproj export full.mp4 --format h264\npi-craft verify full.mp4 --duration 5±0.1 --frame-diff 0.5:a.mp4@0.5\\\u003C1 --frame-diff 1.5:a.mp4@1.5\\>30\npi-craft filmcraft exec --project ins.fcproj export part.mp4 --format h264 --start 1 --end 3\npi-craft verify part.mp4 --duration 2±0.1 --has-video\n```\n\n```bash recipe: typo-fails-loudly\n# A mistyped id (plahead.set) aborts the script: exit 1, nothing saved. --keep-going instead yields a wrong file.\nffmpeg -v error -y -f lavfi -i testsrc=size=640x360:rate=25:duration=3 -f lavfi -i sine=frequency=440:duration=3 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac a.mp4\nffmpeg -v error -y -f lavfi -i smptebars=size=640x360:rate=25:duration=2 -f lavfi -i sine=frequency=880:duration=2 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac b.mp4\npi-craft filmcraft exec --save-as p.fcproj import a.mp4 b.mp4\ncat > typo.jsonl \u003C\u003C'EOF'\n{\"id\":\"file.newSequence\",\"params\":{\"name\":\"cut\",\"width\":640,\"height\":360,\"fps\":25,\"fromItem\":1}}\n{\"id\":\"source.open\",\"params\":{\"item\":2}}\n{\"id\":\"plahead.set\",\"params\":{\"seconds\":3}}\n{\"id\":\"source.overwrite\",\"params\":{}}\nEOF\nif pi-craft filmcraft exec --project p.fcproj --save-as bad.fcproj run typo.jsonl; then echo \"typo went unnoticed\"; exit 1; fi\ntest ! -e bad.fcproj\npi-craft filmcraft exec --project p.fcproj --save-as bad.fcproj run typo.jsonl --keep-going || true\npi-craft filmcraft exec --project bad.fcproj export wrong.mp4 --format h264\n# the surviving line placed b at the playhead (0 s), over a: 3 s, not the intended 5 s — only verify catches it\npi-craft verify wrong.mp4 --duration 3±0.1\n```\n","# FilmCraft — headless video editing\n\nFilmCraft is ArtCraft's non-linear editor (projects, bins, sequences, tracks, clips, effects). `pi-craft`\nprovisions the headless `filmcraft-cli` (no desktop app). Choosing between apps, the discover → execute →\nsave → verify loop and the MCP policy live in `craft-studio`; this skill is the FilmCraft dialect and its traps.\n\n## What it is for \u002F formats\n\nEdits video + audio on a timeline. Reads common media (mp4, mov, wav…; `probe \u003Cmedia>` shows what it sees);\nexports `h264 hevc prores dnxhr mjpeg gif png tiff wav aac …` (`export --list-presets`). Project file: `.fcproj`.\nFor motion graphics \u002F alpha titles use EffectCraft (hand-off: mov ProRes 4444); for audio work SoundCraft (wav).\n\n## CLI dialect\n\n`pi-craft filmcraft exec \u003Cargs…>` passes `\u003Cargs…>` to `filmcraft-cli`. **`exec` appears twice** for a single\ncommand: the first is `pi-craft`'s verb, the second is `filmcraft-cli`'s subcommand.\n\n```bash\npi-craft filmcraft exec commands [filter] [--json]        # list ids (filter = substring; --filter word also works)\npi-craft filmcraft exec describe \u003Cid>                     # one command: params + \"enabled\" right now\npi-craft filmcraft exec [opts] exec \u003Cid> key=value …      # one command (value is JSON if it parses: seconds=3.5 item=2)\npi-craft filmcraft exec [opts] exec \u003Cid> '{\"seconds\":3}'  # same, params as one JSON object\npi-craft filmcraft exec [opts] run script.jsonl [--keep-going]   # one {\"id\",\"params\"} per line, # comments ok\npi-craft filmcraft exec import a.mp4 b.mp4                # prints the new project item ids\npi-craft filmcraft exec [opts] export out.mp4 --format h264 [--start S --end S] [--scale 0.5] [--no-audio]\npi-craft filmcraft exec [opts] inspect [project|sequence] [--compact]    # JSON tree; ids, clip times (ticks)\npi-craft filmcraft exec probe clip.mp4\n```\n\n**Options go before the subcommand**: `--project p.fcproj`, `--save`, `--save-as new.fcproj`, `--keep-going`\n(for `run`), `--compact` (one-line JSON). Times are ticks (254016000000 per second); most commands also take\n`seconds=`, `frame=` or `timecode=`.\n\n## State and persistence\n\nEach invocation loads the project, runs, and exits. **The project file is the only state**: pass\n`--project p.fcproj` to open it, add `--save` (in place) or `--save-as other.fcproj` to write it back.\nWithout a save flag, edits are discarded. A new project is made by `--save-as p.fcproj import …`\n(`--project` on a missing file fails). **Transient editor state is *not* saved**: the Source monitor\n(`source.open`) and the targeted tracks do not survive to the next invocation, so\n`source.open` → `source.overwrite` must be steps of one `run` script — separate `exec` calls fail with\n\"no clip in the Source monitor\". Rendering also creates a `FilmCraft Previews\u002F` folder next to the cwd.\n\n## Discovery first\n\nIds change between releases — never guess them.\n\n```bash\npi-craft filmcraft exec commands razor                    # id, label, shortcut, param schema\npi-craft filmcraft exec describe source.overwrite         # \"enabled\": false + why, when it cannot run now\npi-craft filmcraft exec inspect project --compact         # item ids (use these for source.open item=…)\npi-craft filmcraft exec export --list-presets h264\n```\n\n## Traps\n\n- **`source.overwrite` and `source.insert` place the source clip at the *current playhead*** — not at \"the end\"\n  and not at a time you meant. Always set it explicitly with `playhead.set` (`seconds=`) immediately before each\n  placement. Where it sits after `file.newSequence` depends on the steps before (seen at 0 s inside one run, at the sequence end after a reopen); never rely on it.\n- **A mistyped id in a script fails only that line.** With `--keep-going` the run continues and exits 1 *after*\n  the later steps ran: `source.overwrite` then lands at the wrong time and the export is a **valid but wrong**\n  file (observed 3 s instead of 5 s, an `a.mp4` overwritten by `b.mp4`). Without `--keep-going` the run stops at\n  the first failure, exits 1 and saves nothing. So: discover ids, avoid `--keep-going` for assembly,\n  and always `pi-craft verify` the duration \u002F a frame of the export.\n- `source.overwrite` replaces what is under the placed range; `source.insert` ripples later media to the right.\n  Use overwrite to extend at the end, insert to splice in the middle.\n- `file.newSequence` with `fromItem=\u003Cid>` already places that item at 0; `source.open item=\u003Cid>` is then needed\n  for every *further* clip. Targeting (`timeline.setTargeting`) decides which tracks receive the source's video\n  and audio; the default of a fresh sequence takes both.\n- `--project` must exist; use `--save-as` for the first save. `export` renders the *active* sequence.\n- Exit `1` also means \"offline media rendered as placeholders\" — fix paths instead of `--allow-offline`.\n- Linux needs glibc ≥ 2.35. `.pi\u002Fmcp.json` is only written by `pi-craft filmcraft mcp --register` (opt-in).\n\n## MCP route (opt-in)\n\n`pi-craft filmcraft mcp --register` adds a `craft-filmcraft` entry (17 tools: `command_run`, `command_batch`,\n`media_import`, `render_frame`, `render_preview`, … plus `ui_*`). **The FilmCraft MCP server is not path-confined**\n(it reads and writes any path you can), so `mcp` \u002F `--register` need `--allow-unconfined` — tell the user\nfirst. Prefer the bash route. Bridging to a running GUI (`--bridge`) is out of scope.\n\n## Recipes\n\n```bash recipe: assemble-two-clips\n# a.mp4 (3 s) then b.mp4 (2 s) in one sequence; b is placed at an explicit time.\nffmpeg -v error -y -f lavfi -i testsrc=size=640x360:rate=25:duration=3 -f lavfi -i sine=frequency=440:duration=3 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac a.mp4\nffmpeg -v error -y -f lavfi -i smptebars=size=640x360:rate=25:duration=2 -f lavfi -i sine=frequency=880:duration=2 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac b.mp4\npi-craft filmcraft exec --save-as p.fcproj import a.mp4 b.mp4        # items 1 and 2\ncat > assemble.jsonl \u003C\u003C'EOF'\n{\"id\":\"file.newSequence\",\"params\":{\"name\":\"cut\",\"width\":640,\"height\":360,\"fps\":25,\"fromItem\":1}}\n{\"id\":\"source.open\",\"params\":{\"item\":2}}\n{\"id\":\"playhead.set\",\"params\":{\"seconds\":3}}\n{\"id\":\"source.overwrite\",\"params\":{}}\nEOF\npi-craft filmcraft exec --project p.fcproj --save run assemble.jsonl\npi-craft filmcraft exec --project p.fcproj export out.mp4 --format h264\npi-craft verify out.mp4 --duration 5±0.1 --has-video --has-audio --size 640x360 --codec video=h264 --codec audio=aac \\\n  --frame-diff 4:b.mp4@1\\\u003C2\n```\n\n```bash recipe: place-at-explicit-time\n# Leave a 1 s gap: b is placed at 4 s, so the 3 s clip a ends before it. The explicit playhead.set decides.\nffmpeg -v error -y -f lavfi -i testsrc=size=640x360:rate=25:duration=3 -f lavfi -i sine=frequency=440:duration=3 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac a.mp4\nffmpeg -v error -y -f lavfi -i smptebars=size=640x360:rate=25:duration=2 -f lavfi -i sine=frequency=880:duration=2 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac b.mp4\npi-craft filmcraft exec --save-as p.fcproj import a.mp4 b.mp4\ncat > gap.jsonl \u003C\u003C'EOF'\n{\"id\":\"file.newSequence\",\"params\":{\"name\":\"gap\",\"width\":640,\"height\":360,\"fps\":25,\"fromItem\":1}}\n{\"id\":\"source.open\",\"params\":{\"item\":2}}\n{\"id\":\"playhead.set\",\"params\":{\"seconds\":4}}\n{\"id\":\"source.overwrite\",\"params\":{}}\nEOF\npi-craft filmcraft exec --project p.fcproj --save-as gap.fcproj run gap.jsonl\npi-craft filmcraft exec --project gap.fcproj export gap.mp4 --format h264\npi-craft verify gap.mp4 --duration 6±0.1 --has-audio --frame-diff 4.5:b.mp4@0.5\\\u003C2\n```\n\n```bash recipe: insert-and-range-export\n# source.insert at 1 s splices b into the middle of a (3 s + 2 s = 5 s); then export only 1 s … 3 s.\nffmpeg -v error -y -f lavfi -i testsrc=size=640x360:rate=25:duration=3 -f lavfi -i sine=frequency=440:duration=3 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac a.mp4\nffmpeg -v error -y -f lavfi -i smptebars=size=640x360:rate=25:duration=2 -f lavfi -i sine=frequency=880:duration=2 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac b.mp4\npi-craft filmcraft exec --save-as p.fcproj import a.mp4 b.mp4\ncat > ins.jsonl \u003C\u003C'EOF'\n{\"id\":\"file.newSequence\",\"params\":{\"name\":\"ins\",\"width\":640,\"height\":360,\"fps\":25,\"fromItem\":1}}\n{\"id\":\"source.open\",\"params\":{\"item\":2}}\n{\"id\":\"playhead.set\",\"params\":{\"seconds\":1}}\n{\"id\":\"source.insert\",\"params\":{}}\nEOF\npi-craft filmcraft exec --project p.fcproj --save-as ins.fcproj run ins.jsonl\npi-craft filmcraft exec --project ins.fcproj export full.mp4 --format h264\npi-craft verify full.mp4 --duration 5±0.1 --frame-diff 0.5:a.mp4@0.5\\\u003C1 --frame-diff 1.5:a.mp4@1.5\\>30\npi-craft filmcraft exec --project ins.fcproj export part.mp4 --format h264 --start 1 --end 3\npi-craft verify part.mp4 --duration 2±0.1 --has-video\n```\n\n```bash recipe: typo-fails-loudly\n# A mistyped id (plahead.set) aborts the script: exit 1, nothing saved. --keep-going instead yields a wrong file.\nffmpeg -v error -y -f lavfi -i testsrc=size=640x360:rate=25:duration=3 -f lavfi -i sine=frequency=440:duration=3 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac a.mp4\nffmpeg -v error -y -f lavfi -i smptebars=size=640x360:rate=25:duration=2 -f lavfi -i sine=frequency=880:duration=2 \\\n  -shortest -c:v libx264 -pix_fmt yuv420p -c:a aac b.mp4\npi-craft filmcraft exec --save-as p.fcproj import a.mp4 b.mp4\ncat > typo.jsonl \u003C\u003C'EOF'\n{\"id\":\"file.newSequence\",\"params\":{\"name\":\"cut\",\"width\":640,\"height\":360,\"fps\":25,\"fromItem\":1}}\n{\"id\":\"source.open\",\"params\":{\"item\":2}}\n{\"id\":\"plahead.set\",\"params\":{\"seconds\":3}}\n{\"id\":\"source.overwrite\",\"params\":{}}\nEOF\nif pi-craft filmcraft exec --project p.fcproj --save-as bad.fcproj run typo.jsonl; then echo \"typo went unnoticed\"; exit 1; fi\ntest ! -e bad.fcproj\npi-craft filmcraft exec --project p.fcproj --save-as bad.fcproj run typo.jsonl --keep-going || true\npi-craft filmcraft exec --project bad.fcproj export wrong.mp4 --format h264\n# the surviving line placed b at the playhead (0 s), over a: 3 s, not the intended 5 s — only verify catches it\npi-craft verify wrong.mp4 --duration 3±0.1\n```\n",{"name":5,"description":7},"develop","fb9a5af356f540de4839ef851ebbe14bcce64c0f","packages\u002Fcraft-apps\u002F.pi\u002Fskills\u002Ffilmcraft",55,"https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology\u002Fpi-agent-dashboard","https:\u002F\u002Fpi-dashboard.dev\u002F","https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology","https:\u002F\u002Fwww.blackbelt.hu","2026-11-12 23:37:37",{"source":8,"skillName":5,"commands":68,"prompt":78,"zipAvailable":79},[69,72,75],{"tool":70,"command":71},"npx","npx skills add BlackBeltTechnology\u002Fpi-agent-dashboard --skill filmcraft",{"tool":73,"command":74},"bunx","bunx skills add BlackBeltTechnology\u002Fpi-agent-dashboard --skill filmcraft",{"tool":76,"command":77},"pnpm","pnpm dlx skills add BlackBeltTechnology\u002Fpi-agent-dashboard --skill filmcraft","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology\u002Fpi-agent-dashboard\u002Fblob\u002Fdevelop\u002Fpackages\u002Fcraft-apps\u002F.pi\u002Fskills\u002Ffilmcraft\u002FSKILL.md",true,[],[82,107,136,158,180,201],{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":89,"publishedAt":53},"01M6HQYQ5B82AM5T9Y299S9CFY","youtube-srt","Download YouTube subtitles (SRT + timestamped TXT) for a channel, playlist or video via yt-dlp, no media download; optionally mine them into a categorized catalog (e.g. AI models by function, open weights, local runnability, hardware tier). Use on \"download the srt of this video\", \"transcripts of channel X for the last N months\", \"catalog the models AI Search talked about\", \"update docs\u002Fmodels\".","https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology\u002Fpi-agent-dashboard\u002Fblob\u002Fdevelop\u002Fpackages\u002Fvideo-transcription\u002F.pi\u002Fskills\u002Fyoutube-srt\u002FSKILL.md",[88],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[90,91,92,93,94,95,96,52,51,97,98,45,99,100,101,102,103,104,105,31,106],"youtube","srt","youtube srt","update","search","subtitles","dlp","open","catalog","update docs","yt-dlp","local","mine","via","ai models","last","media download",{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":117,"publishedAt":53},"01M6HQYJBKSXR55T5RSYNJQG4D","video-transcription","Transcribe video and audio files to SRT subtitles with speaker diarization using the Soniox (default) or AssemblyAI API. Use when the user wants to transcribe meetings, videos, or audio files. Supports MKV, MP4, MOV, M4A, MP3. Triggers on \"\u002Ftranscribe\", \"transcribe my meetings\", \"transcribe videos in ~\u002FMovies\", or any request to convert audio\u002Fvideo to text.","https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology\u002Fpi-agent-dashboard\u002Fblob\u002Fdevelop\u002Fpackages\u002Fvideo-transcription\u002F.pi\u002Fskills\u002Fvideo-transcription\u002FSKILL.md",[113,116],{"categoryNo":114,"categoryName":115,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[52,118,119,120,95,121,122,123,49,124,125,45,126,127,128,129,33,130,131,91,132,133,134,135],"transcription","video transcription","transcribe","api","Audio","convert","text","meetings","files","default","mp3","triggers","videos","assemblyai","my","srt subtitles","request","when",{"skillNo":137,"name":138,"nameZh":6,"description":139,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":140,"categories":141,"keywords":142,"publishedAt":53},"01M6HQYECMVDC5TDHHW22VC5RX","speaker-id","Name anonymous diarized SRT speakers from a persistent local voiceprint library and repair speaker drift; local, CPU-only, no API key. Triggers \"who is speaker 2\", \"put names on the transcript\", \"label the meeting SRT\", \"the diarization split one person into three\", \"enroll my voice\". Backed by the pi-voiceid CLI.","https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology\u002Fpi-agent-dashboard\u002Fblob\u002Fdevelop\u002Fpackages\u002Fvideo-transcription\u002F.pi\u002Fskills\u002Fspeaker-id\u002FSKILL.md",[],[143,144,145,146,121,147,46,148,149,150,151,101,129,152,153,154,155,91,156,157],"id","speaker id","cli","transcript","voice","only","three","drift","repair","name","CPU","meeting","anonymous","split","person",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":162,"categories":163,"keywords":165,"publishedAt":53},"01M6HQYAZWDXE9QJEG592E5PXK","veo-showreel-production-kit","Turn a video timeline + voiceover into a reproducible, sliceable Veo 3.1 prompt package with a consistency anchor and AI storyboard sketches.","https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology\u002Fpi-agent-dashboard\u002Fblob\u002Fdevelop\u002Fpackages\u002Fvideo-production\u002F.pi\u002Fskills\u002Fveo-showreel-production-kit\u002FSKILL.md",[164],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[166,167,168,169,170,171,172,52,173,174,175,176,33,177,178,179],"Production","veo","kit","veo showreel production kit","package","Reproducible","timeline","prompt","voiceover","storyboard","anchor","consistency","veo 3","2026",{"skillNo":181,"name":182,"nameZh":6,"description":183,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":184,"categories":185,"keywords":188,"publishedAt":53},"01M6HQY6TJ0X1ADDKYPMVJY6NV","veo-generator","Render a scripted video project into mp4 clips with the Google Veo 3.1 API. Reads a project's shot package (shots\u002F*.md), one clip per camera cut, reusing each shot's prompt and sketch — world anchor only with `--with-reference`, seed unless `--no-seed`. Use on \"render the Veo video for \u003Cproject>\", \"generate the videos from the shot scripts\", \"make the clips with Veo\".","https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology\u002Fpi-agent-dashboard\u002Fblob\u002Fdevelop\u002Fpackages\u002Fvideo-production\u002F.pi\u002Fskills\u002Fveo-generator\u002FSKILL.md",[186,187],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[167,189,190,191,192,170,121,52,49,173,45,42,148,193,194,195,196,197,176,37,198,34,199,33,200,31,178],"generator","veo generator","google","generate","sketch","camera","world","seed","project","scripts","render","google veo",{"skillNo":202,"name":203,"nameZh":6,"description":204,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":205,"categories":206,"keywords":207,"publishedAt":53},"01M6HQY1K2NZJ3B2YHSJJPPVAF","hyperframes-showreel","Turn real screen and site footage into a rhythmic showreel rendered with HyperFrames: sampling, edit script, redacted clips, a generated composition, Veo only as additive FX, music edit + camera punches, delivery render and export QA. Use on \"make a showreel from these recordings\", \"build the HyperFrames video\", \"assemble the footage edit\", \"render and QA the showreel\".","https:\u002F\u002Fgithub.com\u002FBlackBeltTechnology\u002Fpi-agent-dashboard\u002Fblob\u002Fdevelop\u002Fpackages\u002Fvideo-production\u002F.pi\u002Fskills\u002Fhyperframes-showreel\u002FSKILL.md",[],[208,209,210,52,211,47,212,45,44,43,213,148,41,214,38,215,216,199,33,217],"hyperframes","hyperframes showreel","qa","Composition","generated","sampling","delivery","fx","screen","script"]