@officexapp/vidfarm-devcli 0.21.34 → 0.21.36
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/editor-capabilities/SKILL.md +14 -3
- package/.agents/skills/vidfarm/SKILL.md +66 -33
- package/.agents/skills/vidfarm/harnesses/README.md +112 -0
- package/.agents/skills/vidfarm/{regimes/explainer.QA_REGIME.md → harnesses/explainer.HARNESS.md} +3 -2
- package/.agents/skills/vidfarm/{regimes/hooks.QA_REGIME.md → harnesses/hooks.HARNESS.md} +3 -3
- package/.agents/skills/vidfarm/{regimes/product-demo.QA_REGIME.md → harnesses/product-demo.HARNESS.md} +1 -1
- package/.agents/skills/vidfarm/{regimes/short-form.QA_REGIME.md → harnesses/short-form.HARNESS.md} +39 -10
- package/.agents/skills/vidfarm/{regimes/ugc-testimonial.QA_REGIME.md → harnesses/ugc-testimonial.HARNESS.md} +3 -3
- package/.agents/skills/vidfarm/recipes/{bulk-scripting-with-a-regime.md → bulk-scripting-with-a-harness.md} +20 -12
- package/.agents/skills/vidfarm/recipes/cutout-graphics-for-explainers.md +43 -13
- package/.agents/skills/vidfarm/recipes/local-edit-render-approve.md +1 -1
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +77 -26
- package/.agents/skills/vidfarm/references/editor-workflows.md +18 -5
- package/.agents/skills/vidfarm/references/hooks-and-virality.md +65 -7
- package/.agents/skills/vidfarm/references/reviewing-renders.md +2 -1
- package/.agents/skills/vidfarm-media/SKILL.md +2 -2
- package/.agents/skills/vidfarm-media/references/tts.md +26 -4
- package/SKILL.director.md +292 -98
- package/SKILL.md +33 -15
- package/dist/src/cli.js +1200 -141
- package/dist/src/devcli/handoff.js +54 -33
- package/dist/src/devcli/{qa-regime.js → harness.js} +132 -55
- package/dist/src/devcli/plate-key.js +698 -0
- package/dist/src/devcli/qa-check.js +209 -4
- package/dist/src/devcli/skill-docs.js +136 -0
- package/dist/src/devcli/sticker-pack.js +48 -0
- package/package.json +6 -4
- package/.agents/skills/vidfarm/regimes/README.md +0 -79
package/.agents/skills/vidfarm/{regimes/short-form.QA_REGIME.md → harnesses/short-form.HARNESS.md}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: short-form
|
|
3
|
-
video_type: general short-form social video (TikTok / Reels / Shorts) — the default base
|
|
3
|
+
video_type: general short-form social video (TikTok / Reels / Shorts) — the default base harness
|
|
4
4
|
checks:
|
|
5
5
|
duration_sec: 8-90
|
|
6
6
|
aspect: 9:16
|
|
@@ -11,16 +11,19 @@ checks:
|
|
|
11
11
|
font_regime: required
|
|
12
12
|
max_text_cards: 3
|
|
13
13
|
max_simultaneous_text: 2
|
|
14
|
+
max_words_per_cue: 12
|
|
15
|
+
max_dead_air_sec: 2.5
|
|
16
|
+
max_tail_sec: 1.5
|
|
14
17
|
max_scene_sec: 8
|
|
15
18
|
---
|
|
16
19
|
|
|
17
|
-
# Short-Form
|
|
20
|
+
# Short-Form Harness
|
|
18
21
|
|
|
19
|
-
The default base. Start here, copy it next to your work, then **delete what doesn't apply and add what makes your format yours** — a
|
|
22
|
+
The default base. Start here, copy it next to your work, then **delete what doesn't apply and add what makes your format yours** — a harness you didn't edit is a harness that isn't about your videos.
|
|
20
23
|
|
|
21
24
|
> **Part I** is the anatomy: the four charges that decide whether a video travels.
|
|
22
25
|
> **Part II** is the rules that keep it credible.
|
|
23
|
-
> Run the pre-flight checklist before you build, and `vidfarm qa <dir> --
|
|
26
|
+
> Run the pre-flight checklist before you build, and `vidfarm qa <dir> --harness ./HARNESS.md` before you publish. An unchecked box is a rewrite, not a fix in the edit.
|
|
24
27
|
|
|
25
28
|
## Part 0 — who this is for (fill this in yourself)
|
|
26
29
|
|
|
@@ -57,7 +60,7 @@ Three seconds, not five. The decision is made before you finish the first senten
|
|
|
57
60
|
|
|
58
61
|
**Banned openings:** throat-clearing ("Hey guys", "So I wanted to talk about…") · a logo, title card, fade from black, or a beat of silence · any sentence whose subject arrives in the second half · context before the claim (context is beat 2).
|
|
59
62
|
|
|
60
|
-
⚠️ **Frame 0 is the hook AND the thumbnail.** No black open, no fade, subject in frame, caption already legible. See the `hooks`
|
|
63
|
+
⚠️ **Frame 0 is the hook AND the thumbnail.** No black open, no fade, subject in frame, caption already legible. See the `hooks` harness for the full chunk-1 craft.
|
|
61
64
|
|
|
62
65
|
### 🔄 Curiosity loop — retention
|
|
63
66
|
|
|
@@ -84,9 +87,9 @@ The moment the video pays what the hook promised, landing on **what they actuall
|
|
|
84
87
|
|
|
85
88
|
A video with replies gets shown again; a video with none dies at its first audience regardless of watch time. **Bait is a deliberate beat, not something you hope for.** One ask, in the final beat, **and in the post caption** (many people reply from the caption without finishing).
|
|
86
89
|
|
|
87
|
-
Works: the confession invite ("tell me which one you already quit") · the guess ("guess how long it took, I say it at the end" — pairs perfectly with a withheld-number loop) · the named omission ("I left one thing out on purpose, ask me") · the soft disagree (a true concession people want to argue with) · a specific question, never "thoughts?".
|
|
90
|
+
Works: the confession invite ("tell me which one you already quit") · the guess ("guess how long it took, I say it at the end" — pairs perfectly with a withheld-number loop) · the named omission ("I left one thing out on purpose, ask me") · the soft disagree (a true concession people want to argue with) · a specific question, never "thoughts?" · the keyword comment ("comment CLIPPER and I'll send the breakdown") — standard and allowed; keep the keyword topical and actually hand over what you promised.
|
|
88
91
|
|
|
89
|
-
Doesn't: ragebait or a deliberate error to farm corrections (works once, costs the positioning permanently) · "follow for part two" (breaks Rule 1) ·
|
|
92
|
+
Doesn't: ragebait or a deliberate error to farm corrections (works once, costs the positioning permanently) · "follow for part two" (breaks Rule 1) · an earnings/health claim attached to the ask ("comment MONEY and I'll show you how to make $10k/mo" — the keyword is fine, the claim isn't) · anything that makes bait the price of the payoff. **Pay off first, then ask.**
|
|
90
93
|
|
|
91
94
|
### 📝 Captions — the delivery system for three of the four charges
|
|
92
95
|
|
|
@@ -94,10 +97,20 @@ Not an accessibility afterthought: captions are how the hook, the loop, and the
|
|
|
94
97
|
|
|
95
98
|
- **Verbatim, every word.** Paraphrased captions desync from the voice and read as fake.
|
|
96
99
|
- **Weight 700–900, inside the 8–85% safe zone.** Below 700 disappears against footage.
|
|
97
|
-
- **One to three words per line, one line at a time.** A block of full sentences doesn't get read.
|
|
100
|
+
- **One to three words per line, one line at a time.** A block of full sentences doesn't get read. Long narration is paged into 3–5-word kinetic cues (`captions generate --style word-pop|spotlight`), never held as one static block.
|
|
101
|
+
- **Placed in the quietest region of the frame**, measured off a still — not dropped on the default lower third.
|
|
98
102
|
- **Cards are timed text over footage** — never a card UI, table, chip row, or frosted panel (`vidfarm qa` flags those as slop).
|
|
99
103
|
- **Max ~3 standalone cards per video:** one for the loop, one for the payoff, one for the bait.
|
|
100
104
|
|
|
105
|
+
#### Caption PLACEMENT is measured off the frame, before styling is decided
|
|
106
|
+
|
|
107
|
+
The safe zone (8–85%) says where text is *allowed*; it does not say where text *belongs*. Inside that band, the caption goes in the region of the frame with the least competing for attention — and that region is found by looking at a frame, not by defaulting to `y:70`.
|
|
108
|
+
|
|
109
|
+
- **Grab the frame and read it.** `vidfarm stills <dir> --at <t>` is free. Split the safe band into thirds and ask which one is quietest: open sky, a blank wall, a defocused background, an empty tabletop, dead space above or below the subject. That third gets the words.
|
|
110
|
+
- **A caption over the busy third is a self-inflicted wound.** It collides with the subject, so it needs a plate to survive, so the plate becomes a bright slab, so the frame now has two things fighting instead of one. Moving the text 40% up the frame solves all three at once. Nothing else on screen competing → the placement is purely a legibility choice, so make it the *readable* one.
|
|
111
|
+
- **Placement decides the plate, not the other way round.** Choose the position first, then measure the band you actually chose (below). Empty sky measures calm → no plate. Reaching for treatment 4 before you've tried moving the text is the mistake.
|
|
112
|
+
- **Size is part of placement.** A line that runs frame-edge to frame-edge has no placement left to choose. If the words don't fit in the quiet region at ~36–64px, cut words or shrink the type — don't widen the box.
|
|
113
|
+
|
|
101
114
|
#### Caption styling is MEASURED off the background, never hardcoded
|
|
102
115
|
|
|
103
116
|
A white rounded caption plate copied from another video onto a near-black stage is a bright slab the design never asked for — it dominates the frame and reads as a UI element pasted over the video. So measure what is actually behind the caption band, then pick one of three treatments:
|
|
@@ -121,6 +134,18 @@ The mechanical fix: compare each caption phrase against the words on screen in t
|
|
|
121
134
|
|
|
122
135
|
## Part II — the rules
|
|
123
136
|
|
|
137
|
+
### Rule 0 — every second earns its place, or it gets cut
|
|
138
|
+
|
|
139
|
+
The thumb re-decides continuously; a second carrying nothing is a free exit. Assume the first assembly is **30–50% too long** and go find the seconds — agents write videos like prose (wind-up, restatement, tidy conclusion) and every one of those habits is a hole in the retention curve.
|
|
140
|
+
|
|
141
|
+
- **The deletion test, on every beat:** delete it — does the video still make sense and does the payoff still land? Then it stays deleted. Anything that survives must serve one of the four charges; "it gives context" is not a charge.
|
|
142
|
+
- **Cut on sight:** intros/logo stings, the wind-up sentence before the claim, restatement, inter-sentence silence over ~0.35s, real-time process, establishing shots, reading what's already on screen, the outro tail, and filler pans that exist because a clip was short.
|
|
143
|
+
- **Always ripple the hole closed** (`vidfarm ripple <dir> --at <sec> --delta -<sec>`) — a cut that leaves a gap converts fluff into dead air, which is worse.
|
|
144
|
+
- **Density is not speed.** The held comedic beat, the payoff playing out, and a cue's readability keep their seconds. Cut *words*, never the time text needs to be read.
|
|
145
|
+
- **Length is an output.** Build the charges, cut, and ship whatever's left. A brief that dictates a duration ordered fluff.
|
|
146
|
+
|
|
147
|
+
Machine half: `max_dead_air_sec` / `max_tail_sec` / `max_scene_sec` above, plus `vidfarm qa`'s `dead-air`, `dead-tail`, `slow-scene`. Craft: `references/hooks-and-virality.md` → "Density".
|
|
148
|
+
|
|
124
149
|
### Rule 1 — every video is standalone. There is no part two.
|
|
125
150
|
|
|
126
151
|
A viewer arriving mid-scroll with zero context must get a complete, useful video. **You do not control the order** — your video 12 is most people's video 1, and if one breaks out it breaks out *alone*. A cross-video cliffhanger converts your one winner into a dead end. Sharing an angle, a look, or a set of beliefs across the catalog is the strategy; *dependency* is what's banned. Test: hand it to someone who knows nothing — "wait, what is this?" and "where's the rest?" are both failures.
|
|
@@ -155,7 +180,7 @@ Verbatim captions in the font regime and the safe zone · frame 0 works as hook
|
|
|
155
180
|
- [ ] 🔄 The loop closes in this video, and the payoff is the thing that was promised
|
|
156
181
|
- [ ] 😍 The payoff is shown, not summarized, and lands before the final beat
|
|
157
182
|
- [ ] 🎣 One bait ask, in the final beat and in the post caption
|
|
158
|
-
- [ ] 🎣 The bait is not ragebait, a
|
|
183
|
+
- [ ] 🎣 The bait is not ragebait, a follow-for-part-two, or an earnings/health claim traded for the reply (a keyword comment ask is fine)
|
|
159
184
|
|
|
160
185
|
**Standalone**
|
|
161
186
|
- [ ] A stranger seeing only this video understands what it's about
|
|
@@ -168,9 +193,13 @@ Verbatim captions in the font regime and the safe zone · frame 0 works as hook
|
|
|
168
193
|
|
|
169
194
|
**Production**
|
|
170
195
|
- [ ] Verbatim captions, weight 700–900, safe zone, one line at a time
|
|
196
|
+
- [ ] Caption position was chosen off an actual still — it sits in the quietest region of the frame, not on top of the subject, and no line runs edge-to-edge
|
|
197
|
+
- [ ] Anything longer than a phrase is paged into kinetic cues rather than held as a static block
|
|
171
198
|
- [ ] Caption colour / active colour / plate were chosen by measuring the composited background behind the band, not copied from another video
|
|
172
199
|
- [ ] One caption treatment for the whole video, and the active-word colour is readable on it
|
|
173
200
|
- [ ] No caption repeats the words already on screen in that scene (≥60% overlap → drop the caption)
|
|
201
|
+
- [ ] The deletion test was actually run: name the beat you cut, or why nothing could go
|
|
202
|
+
- [ ] No stretch of the video is there to fill time — no intro, no wind-up, no restatement, no tail after the last word
|
|
174
203
|
- [ ] ≤3 standalone cards, no slop furniture, `vidfarm qa` otherwise clean
|
|
175
204
|
- [ ] In a batch: this variant differs from its siblings by more than one noun
|
|
176
205
|
|
|
@@ -191,4 +220,4 @@ Verbatim captions in the font regime and the safe zone · frame 0 works as hook
|
|
|
191
220
|
| Good retention, no comments | 🎣 Bait | No ask, or the ask was "thoughts?" |
|
|
192
221
|
| Comments, but hostile | 🎣 Bait | Ragebait or an over-claim |
|
|
193
222
|
|
|
194
|
-
**Vary one charge at a time.** A batch where everything changed at once teaches you nothing — which is the entire point of running scripting mode against a
|
|
223
|
+
**Vary one charge at a time.** A batch where everything changed at once teaches you nothing — which is the entire point of running scripting mode against a harness instead of just generating volume.
|
|
@@ -13,9 +13,9 @@ checks:
|
|
|
13
13
|
max_simultaneous_text: 1
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
# UGC / Testimonial
|
|
16
|
+
# UGC / Testimonial Harness
|
|
17
17
|
|
|
18
|
-
For the format where a person talks to camera about a product they use. The entire value of the format is that it **doesn't look produced** — so most of this
|
|
18
|
+
For the format where a person talks to camera about a product they use. The entire value of the format is that it **doesn't look produced** — so most of this harness is about what NOT to add.
|
|
19
19
|
|
|
20
20
|
## The one test
|
|
21
21
|
|
|
@@ -33,7 +33,7 @@ If the answer needs the brand's permission, budget, or logo, it isn't UGC — it
|
|
|
33
33
|
| **Honest limit** | before the close | What it doesn't do / who it isn't for. **The credibility beat** |
|
|
34
34
|
| **Close** | final beat | What they'd tell a friend. Not a CTA read |
|
|
35
35
|
|
|
36
|
-
**The product enters second, never first.** A testimonial that opens on the product is a commercial. It opens on the *situation* — and situations are also what survives a cold algorithm (see the `hooks`
|
|
36
|
+
**The product enters second, never first.** A testimonial that opens on the product is a commercial. It opens on the *situation* — and situations are also what survives a cold algorithm (see the `hooks` harness).
|
|
37
37
|
|
|
38
38
|
## Rules
|
|
39
39
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
## Recipe: Bulk Video Generation (Scripting Mode) with a
|
|
1
|
+
## Recipe: Bulk Video Generation (Scripting Mode) with a HARNESS.md
|
|
2
2
|
|
|
3
3
|
Use this when the director wants **volume** — daily posting, hook tests, one video per clip in a pool, N variants of a template. Ask first if you're not sure: *"One video, or should we set this up as a repeatable batch?"* If they want volume, this is the shape.
|
|
4
4
|
|
|
5
|
-
The thing that makes bulk work is not the loop — loops are easy. It's that **nobody is going to watch variant #37 as carefully as variant #1**, so the standard has to be written down before the loop runs. That's the `
|
|
5
|
+
The thing that makes bulk work is not the loop — loops are easy. It's that **nobody is going to watch variant #37 as carefully as variant #1**, so the standard has to be written down before the loop runs. That's the `HARNESS.md`.
|
|
6
6
|
|
|
7
7
|
### 0. Read the craft harness first
|
|
8
8
|
|
|
9
|
-
`references/hooks-and-virality.md` — the four charges (hook / loop / payoff / bait), the three gates, and the anti-patterns that only bite at volume. Two of them decide whether this batch is worth running at all: **a different noun is not a different hook** (twenty variants of one sentence with the nouns swapped is one video), and **never point a generator at your grader** (a model writing hooks scored by the same model converges on the rubric, not on what works — scores climb, nothing improves). The
|
|
9
|
+
`references/hooks-and-virality.md` — the four charges (hook / loop / payoff / bait), the three gates, and the anti-patterns that only bite at volume. Two of them decide whether this batch is worth running at all: **a different noun is not a different hook** (twenty variants of one sentence with the nouns swapped is one video), and **never point a generator at your grader** (a model writing hooks scored by the same model converges on the rubric, not on what works — scores climb, nothing improves). The harness catches defects; it does not rank winners.
|
|
10
10
|
|
|
11
11
|
### 1. Agree the variant axis — before any code
|
|
12
12
|
|
|
@@ -20,14 +20,22 @@ vidfarm pull <forkId> --dir ./work # one canonical base fork per batch
|
|
|
20
20
|
|
|
21
21
|
Read `./work/.harness/agent-guide.md` first, as always.
|
|
22
22
|
|
|
23
|
-
### 3. Install and EDIT the
|
|
23
|
+
### 3. Install and EDIT the harness
|
|
24
|
+
|
|
25
|
+
Two ways in, depending on where the format came from:
|
|
24
26
|
|
|
25
27
|
```bash
|
|
26
|
-
|
|
27
|
-
vidfarm
|
|
28
|
+
# (a) From a bundled base — when the format is one you're defining
|
|
29
|
+
vidfarm harness list # short-form | hooks | ugc-testimonial | explainer | product-demo
|
|
30
|
+
vidfarm harness init hooks --out ./work/HARNESS.md
|
|
31
|
+
|
|
32
|
+
# (b) From the template you're batching — when the format is one you're REPLICATING
|
|
33
|
+
vidfarm harness derive <forkId> --out ./work/HARNESS.md # the decomposition, as a harness
|
|
28
34
|
```
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
(b) is what a director means by *"give me the harness for this template_id"*: the decompose pass already extracted the template's viral / visual / structural / audio / build DNA, and `derive` folds those strands into the same editable Markdown a bundled base produces. If the fork was never decomposed, run `vidfarm decompose` first.
|
|
37
|
+
|
|
38
|
+
Either way, **edit it with the director**. The generated file is a starting point; the parts that matter are the ones they add — who the viewer is, their banned vocabulary, the compliance line, the pacing this account actually uses. A harness nobody edited isn't about their videos. A *derived* harness has the extra failure mode of sounding authoritative: it was written by a model that watched one video, so its "unknown" lines and its confident-but-wrong lines both need a human pass. Existing harness somewhere else on disk? Just point at it: `--harness ./brand/HOUSE_RULES.md`. They stack.
|
|
31
39
|
|
|
32
40
|
### 4. Source the N cheaply
|
|
33
41
|
|
|
@@ -38,13 +46,13 @@ vidfarm public-raws --category scroll-stoppers --limit 20 --json > pool.json
|
|
|
38
46
|
|
|
39
47
|
A curated shelf is a pre-tagged, free, already-hosted clip pool — the cheapest way to get N distinct variants without N downloads or N generation calls.
|
|
40
48
|
|
|
41
|
-
### 5. Loop: edit → QA against the
|
|
49
|
+
### 5. Loop: edit → QA against the harness → render
|
|
42
50
|
|
|
43
51
|
```bash
|
|
44
52
|
for VARIANT in "${VARIANTS[@]}"; do
|
|
45
53
|
SLUG="$(echo "$VARIANT" | tr ' ' '-' | cut -c1-40)"
|
|
46
54
|
vidfarm set-text ./work --layer hook --text "$VARIANT"
|
|
47
|
-
vidfarm qa ./work --json > "qa/$SLUG.json" # ./work/
|
|
55
|
+
vidfarm qa ./work --json > "qa/$SLUG.json" # ./work/HARNESS.md auto-discovered
|
|
48
56
|
jq -e '.ok' "qa/$SLUG.json" >/dev/null || { echo "skipped $SLUG"; continue; }
|
|
49
57
|
vidfarm render "$FORK_ID" --dir ./work --out "renders/$SLUG.mp4" --tracer "batch-$SLUG"
|
|
50
58
|
done
|
|
@@ -81,11 +89,11 @@ Read the sheets. In a batch you're looking for two different things: **per-video
|
|
|
81
89
|
|
|
82
90
|
### 6. Answer the review items — don't skip this
|
|
83
91
|
|
|
84
|
-
The
|
|
92
|
+
The harness's `- [ ]` checklist comes back on every run because the CLI *can't* settle it. Machine checks catch a 13-word hook or a black first frame; only you can answer "is this variant genuinely different from its siblings?" or "can the viewer guess the withheld answer?" **Report both halves honestly**: what the machine checked, and what you judged. A batch report claiming a clean pass on the judgment half is worse than no report.
|
|
85
93
|
|
|
86
|
-
### 7. Feed what you learn back into the
|
|
94
|
+
### 7. Feed what you learn back into the harness
|
|
87
95
|
|
|
88
|
-
When the director says "the label-framed hooks all died" or "anything over 30s tanked", write it into `
|
|
96
|
+
When the director says "the label-framed hooks all died" or "anything over 30s tanked", write it into `HARNESS.md` as a rule or a checklist line — with the reason attached, so the next agent doesn't argue it away. The compositions are disposable; **the harness is the artifact that compounds across batches.**
|
|
89
97
|
|
|
90
98
|
### Cost note
|
|
91
99
|
|
|
@@ -16,7 +16,7 @@ The mechanical trio — **generate on a chroma plate → key it out → trim to
|
|
|
16
16
|
```
|
|
17
17
|
One accent color for the active word, everything else near-black. No outline/stroke, no drop shadow, no pill — those exist to survive busy footage and just add noise on white.
|
|
18
18
|
|
|
19
|
-
**Those hexes are the answer for a white stage, not the answer.** They are one instance of a general rule: **caption color, active-word color and plate are chosen by MEASURING the background behind the caption band, never by taste or habit.** On a near-black stage the same flags ship a bright plate the design never asked for and an active word nobody can read. The measurement procedure and the three treatments live in `
|
|
19
|
+
**Those hexes are the answer for a white stage, not the answer.** They are one instance of a general rule: **caption color, active-word color and plate are chosen by MEASURING the background behind the caption band, never by taste or habit.** On a near-black stage the same flags ship a bright plate the design never asked for and an active word nobody can read. The measurement procedure and the three treatments live in `harnesses/short-form.HARNESS.md` → "Caption styling is measured off the background" — read it before you copy the line above onto anything that isn't white.
|
|
20
20
|
- **Female TTS narration.** Default to a warm, friendly **female** voice and say which one you picked: local-first `vidfarm tts "<script>" --voice coral` (OpenAI — `nova` if the script wants more energy, `sage` for calmer), `--voice Kore` or `Leda` on Gemini, or `vidfarm voices` → `vidfarm tts --cloud --voice <voice_id>` on ElevenLabs. Tell the director they can swap it in one flag.
|
|
21
21
|
- **Clean and simple wins.** One idea on screen at a time. Two or three cutouts per beat, not eight. Generous white space, one accent color, one font. When in doubt, remove an element rather than add one.
|
|
22
22
|
|
|
@@ -56,28 +56,58 @@ Out comes `./stickers/sticker-01-red-barn.png`, `sticker-02-tractor.png`, … ea
|
|
|
56
56
|
- `--gap <pct>` (default 1.2) — how far apart two islands must be to count as separate items. **Two items came out as one sticker → lower it** (or ask for wider spacing on the sheet). **One item came out split in two → raise it** (its parts, e.g. a floating antenna or a dotted arrow, weren't bridged).
|
|
57
57
|
- `--min-area <pct>` (default 0.15) — drops key speckle. There is **no maximum** — see the size note below.
|
|
58
58
|
- `--preset`/`--key-color` — match the plate (default `#00FF00`). `--pad`, `--alpha-threshold`, `--output-format png|webp|gif`, `--prefix`, `--max-items`, `--keep-plate`/`--keep-sheet` behave like `cutout`'s.
|
|
59
|
+
- `--sheet-mode auto|zoned|flat`, `--zones auto|off|RxC`, `--zone-cols <n>`, `--key-mode smart|flat`, `--refine` — how the plate(s) are laid out and removed. All four are explained below ("How the key actually works now").
|
|
59
60
|
- Stubborn item? Fall back to one hand-measured `vidfarm mask ./sheet.png --crop x,y,w,h --flat "#00FF00"` for that one; the rest of the pack still comes from `sticker-pack`.
|
|
60
61
|
|
|
61
|
-
**
|
|
62
|
+
**How the key actually works now — CONNECTIVITY, not color matching.** This is the thing to internalize, because it removes the constraint that used to force sticker art to stay flat and simple. `sticker-pack` and `cutout` do **not** delete every pixel that looks like the plate. They score each pixel's distance to its plate color, then **flood-fill inward from the edge of the sheet** through plate-ish pixels, and delete only what the fill **reaches**. Consequences, all of them useful:
|
|
62
63
|
|
|
63
|
-
- **
|
|
64
|
-
- **
|
|
64
|
+
- **Plate-colored art survives if it's inside the item.** A green leaf on a green sheet, a `#00FF00` eye, a highlight in the plate hue — unreachable from the sheet edge, therefore not background, therefore kept. The console tells you when it mattered: *"kept 10,000 plate-colored pixels INSIDE the art that a flat key would have punched out."*
|
|
65
|
+
- **Outline / line-art shapes keep their middles.** The old "rim around a see-through hole" failure is a property of the flat chromakey, not of keying. An enclosed interior can't be reached by the fill.
|
|
66
|
+
- **Edges come out clean.** Boundary pixels get a real coverage estimate and the plate is **un-mixed out of each one individually** (`art = (C − (1−α)·plate)/α`, stored as straight alpha), so there's no green fringe. That's strictly better than ffmpeg's global `despill`, which rebalances every pixel and discolors plate-hued art in the interior.
|
|
67
|
+
- **`--key-mode flat` brings the old behaviour back.** It's the cloud primitive's exact filter chain — use it to reproduce a cloud render bit-for-bit, or as a simple fallback.
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
What the key still can't do for you: an item whose **outer edge** is the plate color dissolves into it, and anything that **fades** into the plate (soft glow, blur, drop shadow) has no crisp silhouette to find.
|
|
67
70
|
|
|
68
|
-
**
|
|
71
|
+
**ONE PLATE COLOR PER STICKER — `--sheet-mode zoned`.** The silhouette constraint is what "one plate per sheet" makes painful: the more items on the sheet, the more of the palette is off-limits to all of them. So stop giving the sheet one background. A **zoned** sheet is a grid of solid color **panels**, one item per panel, each panel's plate chosen against **that item**:
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
```
|
|
74
|
+
# 6 items → a 3×2 grid of color panels, each item's plate picked against its own art
|
|
75
|
+
vidfarm sticker-pack --generate "pond life, flat vector" \
|
|
76
|
+
--items "green frog,pink lotus,blue heron,white pebble,yellow reed,orange koi" \
|
|
77
|
+
--sheet-mode zoned --out-dir ./stickers
|
|
78
|
+
|
|
79
|
+
# A zoned sheet from a web tool: recover the panels from the sheet's own edges
|
|
80
|
+
vidfarm sticker-pack ./sheet.png --zones auto # (this is the default)
|
|
81
|
+
vidfarm sticker-pack ./sheet.png --zones 3x2 # or declare the grid you asked for
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- The green frog sits on magenta while the pink lotus sits on green — **in the same image job**. Item art may use **any** palette, including the colors of the *other* panels.
|
|
85
|
+
- Each panel is keyed independently, with its color read back off **that panel's own corners** (image models drift the exact hue they were told, so the sheet is the source of truth, not the prompt).
|
|
86
|
+
- **Names stop being guessed.** On a flat sheet, `--items` is matched to items in reading order, which goes off-by-one the moment two items merge. On a zoned sheet, panel *N* holds the item you asked for in panel *N* — so the mapping is exact, and `stickers.json` records each sticker's `panel` and `plate`.
|
|
87
|
+
- `--sheet-mode auto` (the default) zones a generation of 2+ named items and stays flat otherwise. `--zone-cols <n>` forces the grid width.
|
|
88
|
+
|
|
89
|
+
**The FLAT one-color sheet is still first-class — reach for it with a weaker image model.** A cheap or small model will paint one background no matter how the grid is described. That's fine and fully supported:
|
|
90
|
+
|
|
91
|
+
- `--sheet-mode flat` asks for the classic single-plate sheet (and `--key-mode smart` still applies, so hollow art and plate-colored interiors are still safe).
|
|
92
|
+
- If you asked for zones and the model ignored them, **keying detects it and re-keys the sheet as one plate automatically** — either because a "panel" had no plate to remove, or because its item filled the panel corner to corner (so the item's own color read as the plate). It says so on the console and in `stickers.json` (`key_note`). Zoning can't strand you.
|
|
93
|
+
|
|
94
|
+
**Plate color is still chosen for you on a flat sheet.** `--generate` reads the subject and moves the plate off any hue it mentions — green (`#00FF00`) → magenta (`#FF00FF`) → blue (`#0047BB`) → black → white — and prints which it chose and why; say it back to the director when it moves (*"your items are mostly green, so I generated them on a magenta plate"*). Splitting a sheet you already have, the plate is read off the sheet itself, so a red/purple/blue sheet from a free web generator just works. Pin it with `--key-color "#FF00FF"` / `--preset magenta`, or `--no-auto-key` for plain green.
|
|
95
|
+
|
|
96
|
+
**Soft, painterly, furry or glassy art → `--refine`.** Every chroma key needs a crisp silhouette, so watercolor edges, fur, glow, glass and cast shadows are out of scope for the keyer no matter how clever the fill is. `--refine` handles them: the keyer's job shrinks to *locating* each item, and the item is then re-cut from the **un-keyed** sheet with the local ONNX matting model (free, ~1–2s each), which ignores color entirely and produces a genuine soft matte.
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
vidfarm sticker-pack ./painterly-sheet.png --refine --out-dir ./stickers
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
It's checked, not trusted: the matte is measured, and if it came back **empty** or **fully opaque** (the model didn't see a subject) that item keeps its keyed cut and the console says so. Flat vector art is exactly where matting fails and the chroma cut is better — so **don't use `--refine` by default**, only when the art is genuinely soft.
|
|
71
103
|
|
|
72
|
-
|
|
104
|
+
**What the prompt still has to ask for.** Three rules survive, and they're all about geometry rather than color:
|
|
73
105
|
|
|
74
|
-
|
|
106
|
+
> Every object's OUTER EDGE must be a clearly different color from its own panel's background, and crisp — no glow, blur, mist, drop shadow or reflection fading from the object into the background. Every enclosed area must be sealed by the artwork itself, not open to the background. Keep a clear margin of plain background between objects and around the sheet edge; nothing touching, overlapping or connected.
|
|
75
107
|
|
|
76
|
-
|
|
108
|
+
`cutout --generate`, `sticker-pack --generate`, `vidfarm handoff image` and the `create-overlay` REST primitive append the right clause for you — the relaxed one above for the smart keyer, and the strict "closed, solidly filled shapes, nothing in a near-plate shade, fully opaque" clause when `--key-mode flat` is in play. **Write it yourself only when you prompt a generator directly** (a free web tool by hand, or your own `POST /api/v1/primitives/images/generate` call).
|
|
77
109
|
|
|
78
|
-
- **
|
|
79
|
-
- If the fill was merely *close* to the plate rather than absent, a lower `--tolerance` can rescue the sheet you already have. Nothing rescues a genuinely empty interior.
|
|
80
|
-
- Last resort for one stubborn item: `vidfarm mask ./sheet.png --crop x,y,w,h` — ONNX matting doesn't care what color the fill is, so it lifts art the chroma key can't.
|
|
110
|
+
**The hollow check still runs, and it means something different now.** `sticker-pack` and `cutout` measure the transparent area **fully enclosed by an item's own art** and report `hole_pct` (plus `holes`, `hollow: true` at ≥20%) in `--json`, in `stickers.json`, and as a `⚠ N% hollow` console flag. Under the smart keyer an enclosed hole **can't** have been keyed away, so a flag is usually *real* art — a ring, donut, picture frame, letter "O", or an item drawn as separate pieces with background showing between them. It warns, never blocks. If a flagged item genuinely looks wrong, the shape's fill is **open** to the background through a gap in its outline; ask the prompt for sealed shapes. Last resort for one stubborn item: `vidfarm mask ./sheet.png --crop x,y,w,h` — ONNX matting doesn't care what color anything is.
|
|
81
111
|
|
|
82
112
|
**Generation is the failure point, not the cutting.** The sheet prompt is auto-appended with the important instruction — *every item fully separated by clear plate-colored background, nothing touching or overlapping, wide margins, no text, no shadows, one consistent style* — because **touching items segment as one sticker**. If a pack comes back merged, re-run the generation asking for more spacing before you fight the `--gap` knob.
|
|
83
113
|
|
|
@@ -7,7 +7,7 @@ Use this when a coding agent is doing the work locally or the user wants a repro
|
|
|
7
7
|
3. Read `./work/.harness/agent-guide.md` and `./work/.harness/context.json` before editing.
|
|
8
8
|
4. Make deterministic edits to `composition.html` and optionally `composition.json`.
|
|
9
9
|
5. Validate with `vidfarm lint` or `vidfarm stills` when useful. **Always look at `vidfarm stills ./work --at 0`** — that frame becomes the thumbnail, so it must not be black, empty, or mid-fade.
|
|
10
|
-
6. **QA before you render: `vidfarm qa ./work`.** Free, instant, devcli-only. It blocklists HTML slop (CTA buttons, benefit chip rows, a lone pill around a static stat/label, frosted cards, gradient text, web-page classes/fonts), checks the caption font regime + safe zone, and flags a blank/fading first frame (the thumbnail). Feedback only — exit 0 even on findings, never automatic — but it catches the #1 tell of an agent-made video, so run it on every production. Fix what's real, ignore what's a deliberate style call, then render.
|
|
10
|
+
6. **QA before you render: `vidfarm qa ./work`.** Free, instant, devcli-only. It blocklists HTML slop (CTA buttons, benefit chip rows, a lone pill around a static stat/label, frosted cards, gradient text, web-page classes/fonts), checks the caption font regime + safe zone, flags oversized captions and static walls of text, and flags a blank/fading first frame (the thumbnail). It cannot see pixels, so *where in the frame* the caption sits is still on you — which is why every run ends with a **`▶ NOW WATCH THE VIDEO`** block: render, `vidfarm stills ./work --sheet`, open the contact sheet, and judge each caption against its actual picture. Do that before you report the video as done. Feedback only — exit 0 even on findings, never automatic — but it catches the #1 tell of an agent-made video, so run it on every production. Fix what's real, ignore what's a deliberate style call, then render.
|
|
11
11
|
7. Render with `vidfarm render <forkId> --dir ./work --wait`.
|
|
12
12
|
7b. **Review the render as a whole before you approve — this is the step that most changes quality.** `vidfarm qa` and `lint` are static checks on the DOM; neither can see the video. Tile ~12 stills into one contact sheet and read it as an image — `vidfarm stills ./work --sheet` does both in one command (add `--at 0,2,4,…` to pick the timestamps): consistent margins, one type scale, one accent colour, deliberate pacing, no jarring join, no dead band under top-anchored content, end card settled ≥2s before the last frame. Compare frames from two different scenes — a frozen render (overlay pass without `-loop 1`, assets outside the composition root) passes duration, frame-count and audio-hash checks while every frame is identical. Check the mix by measurement, not by ear. Full method + the six most common defects: `references/reviewing-renders.md`.
|
|
13
13
|
8. **Ask about deduplication before you approve** — "is this going out more than once (several accounts, another platform, a re-post later)?" If yes, run `vidfarm dedupe ./final.mp4 [--variants N]` on the **exported** MP4 (free, local ffmpeg, no re-render) and approve each variant separately. Asking here rather than after publication is what avoids paying for a second render. See `references/core-workflows.md` → *Deduplicate before you publish*.
|