@officexapp/vidfarm-devcli 0.21.55 → 0.21.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/vidfarm/SKILL.md +87 -1
- package/.agents/skills/vidfarm/harnesses/README.md +1 -0
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +6 -1
- package/.agents/skills/vidfarm/references/reviewing-renders.md +11 -2
- package/SKILL.director.md +104 -4
- package/SKILL.md +77 -5
- package/clipper.md +36 -4
- package/dist/src/cli.js +168 -4
- package/dist/src/devcli/clipper-run.js +941 -0
- package/dist/src/devcli/doctor.js +23 -1
- package/dist/src/devcli/local-frontend-server.js +10 -2
- package/dist/src/devcli/local-render.js +28 -2
- package/dist/src/devcli/marketplace-gigs.js +545 -39
- package/dist/src/devcli/qa-check.js +27 -1
- package/dist/src/devcli/update-check.js +340 -0
- package/dist/src/lib/engine-globals.js +138 -0
- package/dist/src/lib/frozen-render.js +130 -0
- package/dist/src/services/composition-lint.js +16 -0
- package/experimental/engaging-chat-convo.md +1370 -0
- package/experimental/flash-harness.md +549 -0
- package/marketplace.md +544 -14
- package/package.json +7 -2
- package/public/serve-shells/editor.html +27 -0
- package/public/serve-shells/library-files.html +28 -1
- package/public/serve-shells/library-raws.html +27 -0
- package/public/serve-shells/tools-clipper.html +27 -0
- package/public/serve-shells/tools-image.html +27 -0
- package/public/serve-shells/tools-video.html +27 -0
- package/update.md +27 -2
package/SKILL.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vidfarm
|
|
3
|
+
skill_version: 0.21.57
|
|
3
4
|
description: Entry point for making, editing, and rendering short-form video with Vidfarm. Use when the task is to create a video, ad, explainer, or social clip; to edit or re-theme an existing composition; to source footage or audio; or to run a batch of video variants. Routes to the full director skill and the `vidfarm` CLI.
|
|
4
5
|
---
|
|
5
6
|
|
|
@@ -9,6 +10,65 @@ Vidfarm builds video from HTML. You fork a published template, edit it on a time
|
|
|
9
10
|
|
|
10
11
|
This page is the router. It holds the setup, one worked example, the standards, and the file index. It does **not** hold the workflows — fetch those from the index at the bottom when a task needs them.
|
|
11
12
|
|
|
13
|
+
## 0a. Version — check it once a day, and it is skippable
|
|
14
|
+
|
|
15
|
+
**The `skill_version` in the frontmatter above is this document's version** — machine-readable, and the number to compare against. Vidfarm ships as **two moving parts on one semver**: the `vidfarm` agent skill and the `@officexapp/vidfarm-devcli`. Updating one without the other is the single most common cause of *"that command doesn't exist"* and *"the skill says to do X but it fails"* — the skill documents routes and flags the installed CLI may not have yet.
|
|
16
|
+
|
|
17
|
+
**Check at the top of a session, at most once every 24 hours, in one command:**
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
vidfarm update-check --if-stale --quiet
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
- `--if-stale` makes it a **no-op** unless the last check is older than 24h (`--hours N` to change it), so an agent can run it every session for one network round-trip a day.
|
|
24
|
+
- `--quiet` prints **nothing at all when you are current**. It costs no context to leave in.
|
|
25
|
+
- `--json` gives `{devcli, skill, update_available, offline, hours_since_last_check}`.
|
|
26
|
+
- Offline, or the registry unreachable? It says so and **does not** claim you are out of date. Everything local keeps working.
|
|
27
|
+
|
|
28
|
+
**If it reports an update, tell the user and offer it — do not update silently, and do not nag.**
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm i -g @officexapp/vidfarm-devcli@latest && vidfarm skills add vidfarm # BOTH halves, together
|
|
32
|
+
vidfarm update-check --skip # "not now" — this version stays quiet, a NEWER one asks again
|
|
33
|
+
vidfarm update-check --unskip # undo that
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Honour a skip.** A prompt the user cannot dismiss gets ignored, and then the real one is ignored too. `--skip` is recorded against the version number, so you are not silencing the next release.
|
|
37
|
+
|
|
38
|
+
`vidfarm --version` prints both halves. Read the breaking-change notes at <https://vidfarm.cc/update.md> **before** assuming an old workflow still applies — especially on a major bump. Any 404 on a route this page documents means you are out of date; check there first rather than working around it.
|
|
39
|
+
|
|
40
|
+
## 0b. Two questions about YOU, before anything else
|
|
41
|
+
|
|
42
|
+
Answer both once, at the top of the session, and write them into `CONTEXT.md`. They change which road you take through this document, so getting them wrong costs a whole video.
|
|
43
|
+
|
|
44
|
+
### Am I a strong model or a weak one?
|
|
45
|
+
|
|
46
|
+
| Tier | You are | Then |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| **strong** | Claude Opus / Sonnet (4.5+), GPT-5 class, Gemini Pro (3+ thinking), Fable | Use this page as written. The `vidfarm.cc/experimental/*` format harnesses are build instructions for you. |
|
|
49
|
+
| **weak** | **Gemini Flash / Flash-Lite, DeepSeek, Kimi, GLM, MiniMax, Grok, Qwen, Llama**, any "-mini" / "-lite" / "-turbo" / distilled tier | **Go to `https://vidfarm.cc/flash-harness.md` and follow it.** Come back here for the standards in §6. |
|
|
50
|
+
| **unsure** | you cannot name your own model, or you are a router that may swap tiers mid-task | **Treat yourself as weak.** |
|
|
51
|
+
|
|
52
|
+
**Why the split is real, and not a slight.** The `experimental/*` harnesses ask an agent to author a format from its written description — 300 lines of prose to a rendered composition in one held-in-mind pass. That works on frontier agents and reliably fails below them. It is a synthesis problem, not a knowledge problem, so more instructions do not fix it. The flash harness removes the synthesis instead: **fork an already-decomposed template off `/discover` and transplant the client's story into it.** Somebody already paid a frontier model to take that video apart — take the parts. Output quality goes UP, not down, and overfitting to one template is the correct outcome there.
|
|
53
|
+
|
|
54
|
+
**Unsure resolves to weak, deliberately.** A strong model following the flash harness loses a little freedom and still ships a good video. A weak model skipping it ships something the client rejects.
|
|
55
|
+
|
|
56
|
+
### Does my own subscription already include media generation?
|
|
57
|
+
|
|
58
|
+
| Host agent | Image | Voice / TTS | Transcribe / STT |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| **Google Antigravity** (Gemini) | **Yes** | **Yes** | **Yes** |
|
|
61
|
+
| **Gemini CLI** | **Yes** | **Yes** | **Yes** |
|
|
62
|
+
| **OpenAI Codex / ChatGPT** | **Yes** | **Yes** | **Yes** |
|
|
63
|
+
| **Claude Code** | No — Anthropic ships no image, voice or transcription model | No | No |
|
|
64
|
+
| OpenCode / Cursor / anything else | depends on the configured model — read your own tool list | | |
|
|
65
|
+
|
|
66
|
+
`vidfarm agent-image --check` prints this plus a guess at the host from its env markers. **The guess is a hint; your own tool list is the authority.**
|
|
67
|
+
|
|
68
|
+
> **Generation already inside a flat subscription is FREE, so it is allowed in cost mode `minimize`, with no `--yes` and no permission ask.** Vidfarm bills $0 — no primitive, no job, no wallet debit — and the provider bills $0 extra, because the capability is inside the plan the human already pays for. **So "$0 cost saving mode" on Antigravity may still generate its own images and its own voiceover, and you report it honestly as $0.**
|
|
69
|
+
|
|
70
|
+
**Default to using the subscription.** Prefer it over a free local engine that takes twenty minutes, and long before anything billed. The free rungs (stock, IconScout, the client's own site) still come first on quality grounds — *free* does not mean *first*. Name in your report which assets came from your own subscription; that sentence is the proof `minimize` really cost $0. Full page: `references/agent-included-imagegen.md`.
|
|
71
|
+
|
|
12
72
|
## 1. Setup
|
|
13
73
|
|
|
14
74
|
```bash
|
|
@@ -19,6 +79,7 @@ vidfarm doctor # verifies vidfarm + ffmpeg + hyperframes
|
|
|
19
79
|
vidfarm skills add vidfarm # install the full director skill on disk — do this
|
|
20
80
|
vidfarm serve # free local editor + render loop at localhost:3000
|
|
21
81
|
vidfarm browser setup # optional: drive the user's own Chrome — FREE footage sourcing
|
|
82
|
+
vidfarm update-check --if-stale --quiet # §0a — silent when current, no-op if checked in the last 24h
|
|
22
83
|
```
|
|
23
84
|
|
|
24
85
|
**Image generation you already pay for is free — check before you spend or go without.** If the agent driving this terminal includes image generation in its own subscription (Google Antigravity / Gemini CLI, OpenAI Codex / ChatGPT — **Claude Code does not**, Anthropic ships no image model), generating a graphic bills Vidfarm $0 and the provider $0 extra. That makes it legal in cost mode `minimize`, with no `--yes`. `vidfarm agent-image --check` answers "do I have it here?"; `vidfarm agent-image "<subject>" --items "a,b,c"` prints the prompt, the key-plate rules and the cut + import commands. Free stock and IconScout still come first. Full page: `references/agent-included-imagegen.md`.
|
|
@@ -60,12 +121,14 @@ Every visual on the timeline comes from one of three sources. Try them in this o
|
|
|
60
121
|
|
|
61
122
|
A capable agent can replicate a template entirely on brush 1 and 2, on the free tier, with no wallet.
|
|
62
123
|
|
|
124
|
+
**There is a fourth source, and for a weak model it is the FIRST one: a template somebody already decomposed.** `/discover` holds thousands of viral videos that have been taken apart into their scene-by-scene DNA — hook, retention, payoff, cut rhythm, typography, per-beat paintbrush assignments — and forking one is free. That decomposition costs $1+ in AI spend to reproduce yourself, so always check for an existing one before paying to re-run it. `vidfarm discover --query "<the client's problem, in their words>" --json`; a row carrying `summary`, `keywords` and `promotions` is decomposed. **Weak models: this is not an optimisation, it is the method** — see `vidfarm.cc/flash-harness.md`.
|
|
125
|
+
|
|
63
126
|
## 3. Worked example — one video, end to end
|
|
64
127
|
|
|
65
128
|
```bash
|
|
66
129
|
vidfarm ideas --topic "cold email tool for agencies" # free, offline → content-ideas.md
|
|
67
130
|
vidfarm storyboard ./work --init --frames "Hook,Problem,Reveal,CTA" # plan first — free to redo
|
|
68
|
-
vidfarm
|
|
131
|
+
vidfarm discover --query "ugc testimonial" --json # find a published template
|
|
69
132
|
vidfarm fork <templateId> # → forkId
|
|
70
133
|
vidfarm pull <forkId> --dir ./work # composition.html + .harness/ to disk
|
|
71
134
|
vidfarm public-raws --category greenscreen --limit 20 # source footage, $0
|
|
@@ -86,6 +149,7 @@ vidfarm publish <forkId> # push edits back to th
|
|
|
86
149
|
|
|
87
150
|
| The user asks for | Do | Read |
|
|
88
151
|
|---|---|---|
|
|
152
|
+
| **Anything at all, and §0b said you are a weak model** | do not author a format from prose. Fork a **decomposed** template off `/discover`, derive its DNA, procure every raw, transplant the client's story into it | `vidfarm.cc/flash-harness.md` |
|
|
89
153
|
| "Help me figure out what to make" | `vidfarm ideas --grid --topic "<offer>" --count 30` first — the list beats an interview | `references/onboarding.md` |
|
|
90
154
|
| "Give me a consultation" | the `brainstorm/*` chain: cold-start → awareness stages → angles → hooks. No AI key: `vidfarm consult` prints the prompt for **you** to answer, $0 | `references/onboarding.md` |
|
|
91
155
|
| "Find a template and start" | `vidfarm templates search` → `fork` → `pull` | `references/core-workflows.md`, `recipes/find-and-fork-template.md` |
|
|
@@ -98,6 +162,7 @@ vidfarm publish <forkId> # push edits back to th
|
|
|
98
162
|
| "A UGC ad for my app" / "someone reacting, then the app" / a client's app demo you have to make watchable | three streams cut against each other: `ugc-reaction` raws (ONE actor, via `actor_<uuid>`), a Display Greenscreen device carrying their real demo on its screen, and the demo. Ships a voiceover-only cut to publish + a voiceover+music cut to review | `vidfarm.cc/experimental/ugc-reaction-greenscreen.md` |
|
|
99
163
|
| "A tips slideshow / carousel" / "5 tips for X" / a listicle post | N still slides at exactly 3.0s, die-cut cutouts on a page or a photo, a literal "Tips for…" cover, ONE slide shilled from the middle. **The slides are the deliverable; the MP4 is the preview.** $0–$0.15 | `vidfarm.cc/experimental/sticker-slideshow-tips.md` |
|
|
100
164
|
| "A confession / truth bomb over a POV or ambient scene" / "just text on a video" / a text-story post | ONE unbroken take + ONE static block of unplated type. Nothing animates, nothing is cut. `duration = words / 8` (min 8s) so one play reaches the HALFWAY mark and they loop to finish. Cast the scene DARK so the type needs no plate. $0 | `vidfarm.cc/experimental/wall-text-pov-ugc.md` |
|
|
165
|
+
| "A fake text conversation" / "a chat story" / "a screenshot thread" / funny-or-dramatic DMs | N still chat screenshots at exactly 3.0s, pure type on pure black, **no phone chrome at all**. Two voices that stay separable with the colour removed, `>>>` cards for time jumps, the offer named ONCE mid-thread as a plot fact, and NO ending — it stops mid-action. **The slides are the deliverable; the MP4 is the preview.** $0 | `vidfarm.cc/experimental/engaging-chat-convo.md` |
|
|
101
166
|
| "An animated explainer / a little story that animates" / "like those Vox map animations" / "cutout paper animation" | ONE full-bleed parchment stage, no cuts, a cast of die-cut paper stickers moved by ONE paused GSAP timeline. Narrated, subtitles stay small and kinetic in colour only, the offer named once as a wordmark on the LAST beat. Buy SHEETS, not stickers. Desktop-only (the web editor strips scripts). ~$0.25 hybrid, $0 minimize | `vidfarm.cc/experimental/animated-sticker-story.md` |
|
|
102
167
|
| "Make N videos for N customers / clients" / a batch that must not read as N runs of one template | differentiation is an INPUT, not a hope: assign each variant its own frame before you build any of them, then review frame by frame. $0 | `vidfarm.cc/experimental/unique-product-explainers.md` |
|
|
103
168
|
| "Download this video from `<url>`" | `vidfarm download-video <url>` (paid). Free plan gets a 402 — `vidfarm browse page "<url>"` and save it from their Chrome yourself, else have them download it, then `vidfarm put-file`. Never answer "I can't." | `references/browser-harness.md` |
|
|
@@ -119,7 +184,7 @@ Ask both once, early, then persist them. They are independent axes.
|
|
|
119
184
|
|
|
120
185
|
| Mode | Per video | Method |
|
|
121
186
|
|---|---|---|
|
|
122
|
-
| `minimize` | $0 | free local compute + free stock media
|
|
187
|
+
| `minimize` | $0 | free local compute + free stock media + **any generation your own subscription already includes** (§0b) |
|
|
123
188
|
| `hybrid` *(default)* | ~$0.01–$1 | free where free, pay AI only where it clearly wins |
|
|
124
189
|
| `rich-ai` | $1+ | generate reusable greenscreen assets, key them, animate in HTML, save to the library for later reuse |
|
|
125
190
|
| `pure-videogen` | $5+ | script → storyboard stills → scene-by-scene generation. No reuse |
|
|
@@ -131,7 +196,7 @@ AI charges go to the user's own provider keys (`vidfarm add-provider-key`), not
|
|
|
131
196
|
- `interactive` — at checkpoints you hand over a copy-paste brief and they hand a file back: an image prompt they run in a free web generator, or a keyword list for footage. Mint them with `vidfarm handoff image` / `vidfarm handoff raws`. Costs minutes, usually beats what an API budget buys.
|
|
132
197
|
- `autonomous` — you finish end to end with zero steps from them.
|
|
133
198
|
|
|
134
|
-
Two consequences worth stating out loud: `minimize + interactive` still gets custom art, for $0. `minimize + autonomous` gets none. Voice samples (`vidfarm voices --sample`, `--free --sample`) are free on both tiers — offer them either way.
|
|
199
|
+
Two consequences worth stating out loud: `minimize + interactive` still gets custom art, for $0. `minimize + autonomous` gets none — **unless §0b said your own subscription includes generation, in which case `minimize + autonomous` gets custom art AND a custom voiceover, still at $0.** That is the normal case on Antigravity, which is what most gigworkers run. Voice samples (`vidfarm voices --sample`, `--free --sample`) are free on both tiers — offer them either way.
|
|
135
200
|
|
|
136
201
|
## 5b. Production has phases — plan, collect, build
|
|
137
202
|
|
|
@@ -157,6 +222,7 @@ The standards themselves are not optional. `vidfarm qa <dir>` is — it is a fre
|
|
|
157
222
|
- **The first frame is the thumbnail.** A real visual and the hook words at `start:0`. No fade-up, no entrance transition on the first clip. Check with `vidfarm stills <dir> --at 0`.
|
|
158
223
|
- **Cut ruthlessly.** First assembly is 30–50% too long. Delete every beat that the video survives without, then ripple the hole closed. Length is an output, not a target.
|
|
159
224
|
- **Review the whole video, never one frame.** Tile ~12 stills with `vidfarm stills <dir> --sheet` and read it as an image — one type scale, one palette, deliberate pacing, clean joins. Verify audio by measurement (~12–15 dB speech-over-bed, peak <0 dBFS). Report what you measured separately from what you judged. Your own "looks good" is the least reliable signal here.
|
|
225
|
+
- **Prove the render MOVES before you call it done: `vidfarm motion-check <file.mp4>`.** A frozen render has the right duration, frame count, file size and audio, and a perfect frame 0 — every other check you would run passes. `vidfarm render --target local` runs this itself and fails on a still output (`--allow-static` when that is the point).
|
|
160
226
|
- **Use the real trending song.** The render is a review artifact; the user re-attaches the same track from the platform's in-app music library at post time, where it is licensed. Pull it with `vidfarm download-audio <post-url>`, keep it on its own `<audio>` layer at its own `data-volume`, and name the track in your report. Exception: a paid ad placement is not covered — say so once and offer `vidfarm music "<same vibe, same BPM>"`.
|
|
161
227
|
- **Ask about deduplication before the render, not after.** "Is this going out more than once?" Answering early keeps it at render once → dedupe N.
|
|
162
228
|
|
|
@@ -167,6 +233,7 @@ The standards themselves are not optional. `vidfarm qa <dir>` is — it is a fre
|
|
|
167
233
|
- Render only through `POST /api/v1/compositions/:forkId/render`. Never call the renderer directly.
|
|
168
234
|
- Submissions are **not** idempotent. Every render and primitive POST charges again — check status before retrying.
|
|
169
235
|
- In the web editor, CSS/declarative motion only; JS animation adapters are stripped on save. Locally via `vidfarm serve`, the full adapters work.
|
|
236
|
+
- **`window.__player`, `window.__hf`, `window.__playerReady`, `window.__renderReady` and `window.__hyperframes` belong to the engine — never assign them.** Assigning one destroys frame capture: the render exits 0 and writes a correct-length MP4 in which nothing moves. Motion has one mechanism — register a paused timeline and let the engine seek it: `window.__timelines = window.__timelines || {}; window.__timelines["<data-composition-id>"] = gsap.timeline({ paused: true });` (that key must equal the root's `data-composition-id` exactly). For per-frame drawing, hang your draw call off a paused driver timeline's `onUpdate` instead of inventing a seek API. `vidfarm lint` and `vidfarm qa` both error on this, and a local render refuses to start.
|
|
170
237
|
- Treat `forkId` as an unguessable bearer token. Never put provider secrets in composition HTML or JSON.
|
|
171
238
|
|
|
172
239
|
## 8. Deeper knowledge — fetch on demand
|
|
@@ -197,8 +264,13 @@ Otherwise fetch `https://vidfarm.cc/skill-pack/vidfarm/files/<path>`. Load one f
|
|
|
197
264
|
| `references/rest-api.md` | direct HTTP integration only |
|
|
198
265
|
| `recipes/*.md` | `find-and-fork-template` · `retheme-template` · `local-edit-render-approve` · `onboard-a-new-director` · `bulk-scripting-with-a-harness` · `cutout-graphics-for-explainers` |
|
|
199
266
|
| `harnesses/README.md` | anything harness-shaped — start here. Bases: `short-form` · `hooks` · `ugc-testimonial` · `explainer` · `product-demo` · `product-explainer` |
|
|
200
|
-
| `vidfarm
|
|
267
|
+
| `vidfarm.cc/flash-harness.md` | **you are a weak model (§0a), or you are unsure.** The process harness that sits above every format harness: match a decomposed `/discover` template → decompose if nobody has → derive the viral + visual DNA → procure every raw in one pass → transplant. Also at `vidfarm harness show flash-harness`, offline |
|
|
268
|
+
| `vidfarm harness list` | both shelves: the bases above, **plus every format harness from `vidfarm.cc/experimental`** — `flash-harness` (the weak-model process, not a format) · `meme-recaption` · `wall-text-pov-ugc` · `ugc-reaction-greenscreen` · `sticker-slideshow-tips` · `engaging-chat-convo` · `animated-sticker-story` · `google-news-to-video` · `unique-product-explainers`. They ship in the package, so `vidfarm harness show <name>` reads one in full with no fetch, and `vidfarm qa ./work --harness <name>` grades against it. The name is the URL slug |
|
|
201
269
|
|
|
202
|
-
Also served at `vidfarm.cc`: `/experiments.md` (ad testing), `/marketplace.md` (the marketplace manual — routes you to one of the two side harnesses below), `/marketplace-buyer.md` (**buyer side**: commission videos from the crowd), `/agentic-clipper.md` (**worker side**: "Agentic Clipper" mode — one orchestrator on a long-horizon earning mission, one subagent per task), `/update.md` (upgrade runbook), `/experimental` (format harnesses under live testing — the index a clipper routes tasks against), `/skill/vidfarm-platform` (architecture), `/skill/hyperframes` (composition-authoring craft — route broad "make me a video" asks here first).
|
|
270
|
+
Also served at `vidfarm.cc`: `/flash-harness.md` (**the weak-model process harness — read it first if §0a called you weak or unsure**), `/experiments.md` (ad testing), `/marketplace.md` (the marketplace manual — routes you to one of the two side harnesses below), `/marketplace-buyer.md` (**buyer side**: commission videos from the crowd), `/agentic-clipper.md` (**worker side**: "Agentic Clipper" mode — one orchestrator on a long-horizon earning mission, one subagent per task), `/update.md` (upgrade runbook), `/experimental` (format harnesses under live testing — the index a clipper routes tasks against), `/skill/vidfarm-platform` (architecture), `/skill/hyperframes` (composition-authoring craft — route broad "make me a video" asks here first).
|
|
203
271
|
|
|
204
272
|
**The two marketplace harnesses never mix.** A buyer's agent must never load the clipper loop and a clipper must never load the buyer loop. If the user's side is unclear, ask: *"Do you want videos made for you, or do you want to get paid to make videos for other people?"*
|
|
273
|
+
|
|
274
|
+
**But an ask about EARNING has already answered it — do not ask.** *"help me earn on vidfarm marketplace"*, *"help me make money on vidfarm"*, *"find me video gigs"*, *"any work today?"*, *"check the vending machines"*, *"run in agentic clipper mode"* all mean the **clipper** side: load `/agentic-clipper.md`, then run the four moves — read the feed's recent notifications for who is asking right now, pull the `GIG_…` id out of each notification's join link and check `available_funds` before committing, join the funded ones with a Base L2 `wallet_address` in the same call, then `GET /work/available` and poll a queue for a task. The copy-paste version is in `/marketplace.md` → *"help me earn on vidfarm" — the whole first session*.
|
|
275
|
+
|
|
276
|
+
**Then ask the gigworker one more question: the RUN MODE.** It decides which stages of every task stop for them — `full-interactive` (shortlist · plan · raws · cut), `quick-interactive` (shortlist · cut), `auto-batch` (cut only — build the batch unattended, review it in one sitting), `auto-submit` (nothing; opt-in, in their own words). The middle two are the common ones. `vidfarm clipper-run mode` explains all four and saves the answer, `vidfarm clipper-run next` says what to do right now, and `vidfarm clipper-run gate <task>` exits non-zero while a human gate is open — so `gate && gigs submit …` cannot send an unreviewed cut. Full section: `/marketplace.md` → *The four run modes*.
|
package/clipper.md
CHANGED
|
@@ -80,6 +80,22 @@ Ask these, in one message, with the defaults stated:
|
|
|
80
80
|
5. **How often — default every 30 minutes** while their computer is on. Respect the gig's rate
|
|
81
81
|
limit above anything they ask for.
|
|
82
82
|
6. **Where to keep the project — default `~/clipper/<gig-id>/`.**
|
|
83
|
+
7. **Auto-submit — default OFF, and say why.** You build the video, then they watch it and approve
|
|
84
|
+
it before it goes to the client. Explain the reason in one line: rejections score against their
|
|
85
|
+
wallet and clients read that as a trust score on their public profile, so one bad submit lowers
|
|
86
|
+
future earnings. Turn it on only if they ask for it in their own words, and record those words.
|
|
87
|
+
8. **Their hardware — measure it, don't ask.** Read cores, RAM and free disk
|
|
88
|
+
(`sysctl -n hw.ncpu hw.memsize` on macOS, `nproc` + `free -g` on Linux,
|
|
89
|
+
`Get-CimInstance Win32_ComputerSystem` on Windows), then time the first render. **Capable**
|
|
90
|
+
(≥ 8 GB RAM, ≥ 4 cores) uses the free local engines: `vidfarm tts --engine local` (Kokoro),
|
|
91
|
+
`vidfarm stt --engine whisper`, local keying. **Weak** hardware never starts those — they do not
|
|
92
|
+
fail, they just take twenty minutes and lose the deadline. There, ask the AI agent subscription
|
|
93
|
+
they already pay for to make the voice line, the audio or the image, and bring the file in with
|
|
94
|
+
`vidfarm put-file`. Spend stays $0 on either tier. Write the tier into `config.json`.
|
|
95
|
+
9. **Which AI agent runs the loop — recommend Google Antigravity CLI on a budget**, and **OpenAI
|
|
96
|
+
Codex** if they can afford the more expensive subscription. Either runs this whole file. Do not
|
|
97
|
+
make a subscription the price of entry — start on Antigravity, get the first proof paid, and let
|
|
98
|
+
them upgrade out of earnings.
|
|
83
99
|
|
|
84
100
|
Write their answers into `config.json` (Phase 3) so the loop is reproducible and they can edit it
|
|
85
101
|
later without you.
|
|
@@ -334,10 +350,23 @@ vidfarm put-file ./sheet.png
|
|
|
334
350
|
vidfarm mask ./sheet.png --crop x,y,w,h --flat "#00FF00" --out prop-a.png # once per element
|
|
335
351
|
```
|
|
336
352
|
|
|
337
|
-
**
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
and
|
|
353
|
+
**The gigworker watches the finished video and approves it before you submit. Auto-submit is OFF by
|
|
354
|
+
default.** Show them the file — `vidfarm serve` plays it, `vidfarm stills` gives frames — say what
|
|
355
|
+
you want checked (brief satisfied, hook alive in 3 seconds, captions readable and in the safe zone,
|
|
356
|
+
audio clean, brand right), and **wait**. No answer is not a yes. If they say fix it, fix it and come
|
|
357
|
+
back. If they say drop it, do not submit.
|
|
358
|
+
|
|
359
|
+
**This is a money rail, not politeness.** A rejected proof carries a reputation penalty
|
|
360
|
+
(`low_quality` 1x, `incomplete` 2x, `fake_proof` 5x), it follows their wallet across every gig on
|
|
361
|
+
the network, and **clients read it as a trust score on their public profile** before choosing whose
|
|
362
|
+
pitch to take. One weak video lowers every future payday; a run of approved ones raises the score
|
|
363
|
+
and wins better-funded machines at higher prices. Holding a cut back costs one task. Submitting it
|
|
364
|
+
costs earnings they will never see arrive.
|
|
365
|
+
|
|
366
|
+
**Only submit unattended when the gigworker explicitly asks for it** — "auto-submit", "you don't
|
|
367
|
+
need to check with me". Write the exact words and the date into `config.json`, keep the permission
|
|
368
|
+
as narrow as they gave it (one machine is not all machines), and turn it back off after any
|
|
369
|
+
rejection. Never enable it yourself to hit a deadline or clear a queue.
|
|
341
370
|
|
|
342
371
|
---
|
|
343
372
|
|
|
@@ -407,6 +436,9 @@ what they earned (or are owed, for off-chain), and what it cost — AI spend sho
|
|
|
407
436
|
|
|
408
437
|
These exist because the loop touches real customers, real reputation, and real money.
|
|
409
438
|
|
|
439
|
+
- **Never submit a video the gigworker hasn't approved**, unless they explicitly asked for
|
|
440
|
+
auto-submit. Build, QA and queue unattended all you like; the last step waits for a human.
|
|
441
|
+
Rejections are permanent, wallet-anchored, and visible to clients as a public trust score.
|
|
410
442
|
- **Never fake a proof.** Every submitted URL must point at a video you actually rendered for that
|
|
411
443
|
task. `fake_proof` is a 5x reputation penalty and it is permanent and wallet-anchored.
|
|
412
444
|
- **Never promise earnings.** Pay is per approved proof and demand-driven — quiet days pay nothing.
|
package/dist/src/cli.js
CHANGED
|
@@ -868,6 +868,10 @@ Fine timeline control (trackpad-level verbs on a pulled/served composition — l
|
|
|
868
868
|
--target cloud: billed Lambda render (~$0.01-$0.10)
|
|
869
869
|
→ POST .../compositions/:forkId/render (--wait to
|
|
870
870
|
poll; --dir pushes composition.html + .json first)
|
|
871
|
+
A local render REFUSES compositions that assign the
|
|
872
|
+
engine-owned globals (window.__player / window.__hf)
|
|
873
|
+
and FAILS if the finished MP4 never moves — pass
|
|
874
|
+
--allow-static for a deliberately still card
|
|
871
875
|
render-status <forkId> <renderId> Poll one render job → GET .../compositions/:forkId/renders/:renderId
|
|
872
876
|
template run <template_id> <operation_name>
|
|
873
877
|
Run one template operation via REST → POST /api/v1/templates/:templateId/operations/:operationName
|
|
@@ -934,6 +938,7 @@ Local media engines & toolchain (all local, free, no account — no cloud key ne
|
|
|
934
938
|
meme-recaption, wall-text-pov-ugc,
|
|
935
939
|
ugc-reaction-greenscreen,
|
|
936
940
|
sticker-slideshow-tips,
|
|
941
|
+
engaging-chat-convo,
|
|
937
942
|
animated-sticker-story,
|
|
938
943
|
google-news-to-video,
|
|
939
944
|
unique-product-explainers
|
|
@@ -961,6 +966,13 @@ Local media engines & toolchain (all local, free, no account — no cloud key ne
|
|
|
961
966
|
sheet shows it
|
|
962
967
|
--sheet-out <file> Sheet path (default <out>/contact-sheet.png)
|
|
963
968
|
--sheet-width <px> Per-tile width in the sheet (default 320)
|
|
969
|
+
motion-check <video.mp4> DOES IT MOVE? Proves a finished video is not (local ffmpeg)
|
|
970
|
+
a still image. A frozen render has the right
|
|
971
|
+
duration, frame count, audio and frame 0, so
|
|
972
|
+
every other check you would run passes. Exits 1
|
|
973
|
+
when nothing in the video ever changes. Runs
|
|
974
|
+
automatically after vidfarm render --target local
|
|
975
|
+
[--json] [--fps <n>]
|
|
964
976
|
doctor Health-check the local toolchain: node, (local)
|
|
965
977
|
ffmpeg, hyperframes engines, Chrome, API key
|
|
966
978
|
(whoami), provider keys, agent CLI, poisoned
|
|
@@ -969,6 +981,20 @@ Local media engines & toolchain (all local, free, no account — no cloud key ne
|
|
|
969
981
|
--kill-orphans Reap orphaned local serve/preview servers
|
|
970
982
|
(running from a deleted package path) so their
|
|
971
983
|
squatted ports are reclaimed
|
|
984
|
+
--version (-v) This devcli's version + the installed skill's (local)
|
|
985
|
+
update-check IS THIS INSTALL CURRENT? The skill and the (npm registry + vidfarm.cc)
|
|
986
|
+
devcli are ONE semver and must move together —
|
|
987
|
+
updating one alone is the usual cause of "that
|
|
988
|
+
command doesn't exist". Agents: run the --if-stale
|
|
989
|
+
form at the top of a session; it is a no-op unless
|
|
990
|
+
the last check is over 24h old, and --quiet prints
|
|
991
|
+
NOTHING when you are already current.
|
|
992
|
+
--if-stale [--hours 24] Only actually check if the last one is older
|
|
993
|
+
--quiet One line, silent when current
|
|
994
|
+
--skip / --unskip Record "not now" for this version / undo it
|
|
995
|
+
--json {devcli, skill, update_available, offline, …}
|
|
996
|
+
Offline is reported as offline, never as outdated.
|
|
997
|
+
Breaking-change notes: https://vidfarm.cc/update.md
|
|
972
998
|
skills <list|add <names…>|update> Install/refresh agent skills into → GET /skill-pack/* (or the
|
|
973
999
|
<root>/.agents/skills/<name> + a relative copies bundled in this repo)
|
|
974
1000
|
.claude/skills symlink; skills-lock.json is
|
|
@@ -1168,10 +1194,35 @@ Marketplace (paid, cloud-only — the bazaar never renders locally):
|
|
|
1168
1194
|
(alias: dir · save-url|from-url|import-url — CLI twin of the "Save to Files" picker)
|
|
1169
1195
|
gigs <sub> Your vending machines, BOTH sides (client + gigworker)
|
|
1170
1196
|
machines · tasks · add-task · proofs · approve · reject · ring-bell (client, paid)
|
|
1171
|
-
|
|
1172
|
-
|
|
1197
|
+
earn · feed · check · join · work · claim · submit · proof · earnings (gigworker, free — bring DOLLARPLATOON_API_KEY)
|
|
1198
|
+
upload · mine · join-feed
|
|
1199
|
+
"HELP ME EARN ON VIDFARM" IS ONE COMMAND: vidfarm gigs earn. It joins the
|
|
1200
|
+
feed, reads who is asking for videos right now, checks every one of those
|
|
1201
|
+
machines for MONEY, and ranks them best-payer-first with the join command.
|
|
1202
|
+
'gigs check <gig|join-url>' is the funds check on its own — run it BEFORE you
|
|
1203
|
+
spend an hour, because approval does not create money. 'gigs submit
|
|
1204
|
+
--clean-master <file>' seals the unwatermarked cut in the proof's private
|
|
1205
|
+
note, which the buyer cannot open until a rollup has actually paid you.
|
|
1206
|
+
e.g. vidfarm gigs earn
|
|
1207
|
+
vidfarm gigs add-task --task "60s explainer for acme.com" --price 0.50 --upload-link <share url>
|
|
1173
1208
|
vidfarm gigs proofs --status pending · vidfarm gigs approve PRF_01H…
|
|
1174
|
-
(see: vidfarm gigs help)
|
|
1209
|
+
(see: vidfarm gigs help · the loop: vidfarm.cc/agentic-clipper.md)
|
|
1210
|
+
clipper-run <sub> HOW the clipper loop runs, and WHERE each task is up to
|
|
1211
|
+
mode · init · start · shortlist · pick · plan · raws · cut · gate · submitted
|
|
1212
|
+
approve · changes · drop (the gigworker's answers)
|
|
1213
|
+
next · status · review (where am I / what is waiting on the human)
|
|
1214
|
+
Four run modes, differing only in which stages STOP for the gigworker:
|
|
1215
|
+
full-interactive shortlist · plan · raws · cut (best quality on a weak model)
|
|
1216
|
+
quick-interactive shortlist · cut (approve ONE template, then watch the cut)
|
|
1217
|
+
auto-batch cut (build everything, submit nothing until reviewed)
|
|
1218
|
+
auto-submit nothing (unattended; opt-in, in the gigworker's own words)
|
|
1219
|
+
The stage lives in files under ./CLIPPER, not in the context — that is what
|
|
1220
|
+
lets a weak model run a long task without forgetting where it was. 'gate'
|
|
1221
|
+
exits non-zero while a human gate is open, so a loop written as
|
|
1222
|
+
'clipper-run gate <task> && gigs submit …' cannot send an unreviewed cut.
|
|
1223
|
+
e.g. vidfarm clipper-run mode quick-interactive
|
|
1224
|
+
vidfarm clipper-run next
|
|
1225
|
+
(see: vidfarm clipper-run help)
|
|
1175
1226
|
shared <sub> <link> USE a link someone shared with you — NO account, NO API key
|
|
1176
1227
|
info · ls --tree · search · grab · note · mkdir · put · get
|
|
1177
1228
|
grab is the one to reach for: it finds footage by MEANING and downloads it in one step.
|
|
@@ -1311,6 +1362,19 @@ async function main() {
|
|
|
1311
1362
|
process.stdout.write(HELP);
|
|
1312
1363
|
return;
|
|
1313
1364
|
}
|
|
1365
|
+
// `vidfarm --version` is the first line of the update runbook and of every
|
|
1366
|
+
// "what are you running?" support exchange, and it used to error out with
|
|
1367
|
+
// "Unknown option before command". Handled before dispatch so no subcommand
|
|
1368
|
+
// can shadow it.
|
|
1369
|
+
if (argv[0] === "--version" || argv[0] === "-v" || argv[0] === "version") {
|
|
1370
|
+
const { installedDevcliVersion, installedSkillPath, readSkillVersion } = await import("./devcli/update-check.js");
|
|
1371
|
+
const skillPath = installedSkillPath();
|
|
1372
|
+
const skillVersion = skillPath ? readSkillVersion(readFileSync(skillPath, "utf8")) : null;
|
|
1373
|
+
console.log(installedDevcliVersion() ?? "unknown");
|
|
1374
|
+
console.log(`skill ${skillVersion ?? "not installed"}${skillPath ? ` (${skillPath})` : ""}`);
|
|
1375
|
+
console.log("check for updates: vidfarm update-check");
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1314
1378
|
const command = argv[0];
|
|
1315
1379
|
// Opt-out crash telemetry (also covers the long-lived `serve` runtime, whose
|
|
1316
1380
|
// in-process job/render captures share this client). Guarded + no-op by default.
|
|
@@ -1630,6 +1694,16 @@ async function main() {
|
|
|
1630
1694
|
await runGigsCommand(rest);
|
|
1631
1695
|
return;
|
|
1632
1696
|
}
|
|
1697
|
+
// The RUN MODE of an agentic clipper loop — how much of the gigworker's
|
|
1698
|
+
// attention each task costs — plus the per-task stage that survives a
|
|
1699
|
+
// context reset. Local files only; no key, no network.
|
|
1700
|
+
case "clipper-run":
|
|
1701
|
+
case "clipper-mode":
|
|
1702
|
+
case "run-mode": {
|
|
1703
|
+
const { runClipperRunCommand } = await import("./devcli/clipper-run.js");
|
|
1704
|
+
await runClipperRunCommand(rest);
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1633
1707
|
case "get-file":
|
|
1634
1708
|
await runGetFileCommand(rest);
|
|
1635
1709
|
return;
|
|
@@ -1755,6 +1829,13 @@ async function main() {
|
|
|
1755
1829
|
case "stills":
|
|
1756
1830
|
await runStillsCommand(rest);
|
|
1757
1831
|
return;
|
|
1832
|
+
// "does this finished video actually move?" — the check that catches a
|
|
1833
|
+
// frozen render, which passes every other verification.
|
|
1834
|
+
case "motion-check":
|
|
1835
|
+
case "motioncheck":
|
|
1836
|
+
case "frozen-check":
|
|
1837
|
+
await runMotionCheckCommand(rest);
|
|
1838
|
+
return;
|
|
1758
1839
|
case "doctor":
|
|
1759
1840
|
await runDoctorCommand(rest);
|
|
1760
1841
|
return;
|
|
@@ -1770,6 +1851,13 @@ async function main() {
|
|
|
1770
1851
|
case "hf":
|
|
1771
1852
|
await runHyperframesPassthroughCommand(rest);
|
|
1772
1853
|
return;
|
|
1854
|
+
case "update-check":
|
|
1855
|
+
case "check-update":
|
|
1856
|
+
case "outdated": {
|
|
1857
|
+
const { runUpdateCheckCommand } = await import("./devcli/update-check.js");
|
|
1858
|
+
await runUpdateCheckCommand(rest);
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1773
1861
|
default:
|
|
1774
1862
|
if (!command.startsWith("-")) {
|
|
1775
1863
|
// Positional template id → default to booting the local editor.
|
|
@@ -4119,6 +4207,10 @@ async function runRenderCommand(argv) {
|
|
|
4119
4207
|
out: { type: "string" },
|
|
4120
4208
|
quality: { type: "string" },
|
|
4121
4209
|
"keep-project": { type: "boolean", default: false },
|
|
4210
|
+
// A deliberately static render (a title card, a still loop) is a real
|
|
4211
|
+
// output — this is how you say so. Everything else that never moves is a
|
|
4212
|
+
// bug, so the default is to fail loudly.
|
|
4213
|
+
"allow-static": { type: "boolean", default: false },
|
|
4122
4214
|
wait: { type: "boolean", default: false }
|
|
4123
4215
|
}
|
|
4124
4216
|
});
|
|
@@ -4177,8 +4269,20 @@ async function runRenderCommand(argv) {
|
|
|
4177
4269
|
keepProjectDir: Boolean(parsed.values["keep-project"])
|
|
4178
4270
|
});
|
|
4179
4271
|
const prepCuts = result.prepNotes.filter((note) => note.action !== "skipped");
|
|
4272
|
+
// A frozen render is a FAILED render wearing a successful render's exit
|
|
4273
|
+
// code. Treat it as a failure unless the director said the video is static
|
|
4274
|
+
// on purpose.
|
|
4275
|
+
const frozen = Boolean(result.motion?.frozen) && !parsed.values["allow-static"];
|
|
4180
4276
|
if (ctx.json) {
|
|
4181
|
-
printJson({
|
|
4277
|
+
printJson({
|
|
4278
|
+
ok: !frozen,
|
|
4279
|
+
render_target: "local",
|
|
4280
|
+
output_path: result.outputPath,
|
|
4281
|
+
duration_ms: result.durationMs,
|
|
4282
|
+
media_prep: result.prepNotes,
|
|
4283
|
+
motion: result.motion ?? null,
|
|
4284
|
+
...(frozen ? { error: "frozen_render", message: result.motion?.reason } : {})
|
|
4285
|
+
});
|
|
4182
4286
|
}
|
|
4183
4287
|
else {
|
|
4184
4288
|
if (prepCuts.length > 0) {
|
|
@@ -4186,10 +4290,22 @@ async function runRenderCommand(argv) {
|
|
|
4186
4290
|
}
|
|
4187
4291
|
console.log(`${GREEN}Rendered locally in ${(result.durationMs / 1000).toFixed(1)}s → ${result.outputPath}${RESET}`);
|
|
4188
4292
|
console.log(`${DIM}$0.00 — local Vidfarm render, no cloud charge.${RESET}`);
|
|
4293
|
+
if (frozen && result.motion) {
|
|
4294
|
+
const { formatFrozenRenderFailure } = await import("./lib/frozen-render.js");
|
|
4295
|
+
console.log(formatFrozenRenderFailure(result.motion, { red: RED, bold: BOLD, dim: DIM, reset: RESET }));
|
|
4296
|
+
}
|
|
4297
|
+
else if (result.motion?.skipped) {
|
|
4298
|
+
console.log(`${DIM}Motion not verified (${result.motion.reason}) — check it by eye before publishing.${RESET}`);
|
|
4299
|
+
}
|
|
4300
|
+
else if (result.motion) {
|
|
4301
|
+
console.log(`${DIM}Motion verified: ${result.motion.moving_pairs}/${result.motion.compared_pairs} sampled frame pairs differ — the video moves.${RESET}`);
|
|
4302
|
+
}
|
|
4189
4303
|
// Nudge, not a gate: QA never runs automatically, but every production
|
|
4190
4304
|
// should get a look before it's published.
|
|
4191
4305
|
console.log(`${DIM}Recommended: ${RESET}vidfarm qa ${sourceDir ?? "<dir>"}${DIM} — social-native QA (HTML slop + font regime). Feedback only, free, instant.${RESET}`);
|
|
4192
4306
|
}
|
|
4307
|
+
if (frozen)
|
|
4308
|
+
process.exitCode = 1;
|
|
4193
4309
|
return;
|
|
4194
4310
|
}
|
|
4195
4311
|
if (sourceDir) {
|
|
@@ -12715,6 +12831,54 @@ async function runStillsCommand(argv) {
|
|
|
12715
12831
|
console.log(`${DIM}READ the contact sheet as ONE image — that is how you catch what per-scene checks miss: uneven margins, a wandering type scale or accent colour, N identically-long beats, a jarring join, dead space under top-anchored content. Fix drift by defining the system, not by patching the one odd scene.${RESET}`);
|
|
12716
12832
|
}
|
|
12717
12833
|
}
|
|
12834
|
+
// `vidfarm motion-check <video.mp4>` — prove a finished video actually MOVES.
|
|
12835
|
+
//
|
|
12836
|
+
// The one check that catches a whole class of silent render failure: a frozen
|
|
12837
|
+
// render has the right duration, the right frame count, clean audio, and a
|
|
12838
|
+
// perfect frame 0, so every other verification an agent would think to run
|
|
12839
|
+
// passes. This one samples the video and compares consecutive frames. Runs on
|
|
12840
|
+
// any MP4, not just a vidfarm render — including the output of a watermark or
|
|
12841
|
+
// overlay pass, which is where the other known cause lives.
|
|
12842
|
+
async function runMotionCheckCommand(argv) {
|
|
12843
|
+
const parsed = parseArgs({
|
|
12844
|
+
args: argv,
|
|
12845
|
+
allowPositionals: true,
|
|
12846
|
+
options: {
|
|
12847
|
+
json: { type: "boolean", default: false },
|
|
12848
|
+
fps: { type: "string" }
|
|
12849
|
+
}
|
|
12850
|
+
});
|
|
12851
|
+
const target = parsed.positionals[0];
|
|
12852
|
+
if (!target)
|
|
12853
|
+
throw new Error("motion-check requires a video path: `vidfarm motion-check <video.mp4> [--json]`.");
|
|
12854
|
+
const videoPath = path.resolve(process.cwd(), target);
|
|
12855
|
+
if (!existsSync(videoPath))
|
|
12856
|
+
throw new Error(`No such file: ${videoPath}`);
|
|
12857
|
+
const fpsRaw = parsed.values.fps;
|
|
12858
|
+
const fpsGrid = fpsRaw !== undefined ? Number(fpsRaw) : undefined;
|
|
12859
|
+
if (fpsRaw !== undefined && (!Number.isFinite(fpsGrid) || fpsGrid <= 0)) {
|
|
12860
|
+
throw new Error(`--fps must be a positive number, got "${fpsRaw}".`);
|
|
12861
|
+
}
|
|
12862
|
+
const { checkRenderMotion, formatFrozenRenderFailure } = await import("./lib/frozen-render.js");
|
|
12863
|
+
const result = await checkRenderMotion(videoPath, fpsGrid ? { fpsGrid } : {});
|
|
12864
|
+
if (parsed.values.json) {
|
|
12865
|
+
printJson(result);
|
|
12866
|
+
if (result.frozen)
|
|
12867
|
+
process.exitCode = 1;
|
|
12868
|
+
return;
|
|
12869
|
+
}
|
|
12870
|
+
if (result.frozen) {
|
|
12871
|
+
console.log(formatFrozenRenderFailure(result, { red: RED, bold: BOLD, dim: DIM, reset: RESET }));
|
|
12872
|
+
process.exitCode = 1;
|
|
12873
|
+
return;
|
|
12874
|
+
}
|
|
12875
|
+
if (result.skipped) {
|
|
12876
|
+
console.log(`${YELLOW}? Motion not verified${RESET} ${DIM}— ${result.reason}${RESET}`);
|
|
12877
|
+
return;
|
|
12878
|
+
}
|
|
12879
|
+
console.log(`${GREEN}✓ The video moves${RESET} ${DIM}— ${result.moving_pairs}/${result.compared_pairs} sampled frame pairs differ (max change ${result.motion_score.toFixed(1)}%).${RESET}`);
|
|
12880
|
+
console.log(`${DIM}This proves motion exists, not that the video is good. Still watch it: vidfarm stills <dir> --sheet.${RESET}`);
|
|
12881
|
+
}
|
|
12718
12882
|
// ── Agent skill ───────────────────────────────────────────────────────────────
|
|
12719
12883
|
// Install the latest director skill onto disk as a Claude Code / agent skill so
|
|
12720
12884
|
// the user's AI agent can read SKILL.director.md and act. We pull the freshest
|