@officexapp/vidfarm-devcli 0.21.27 → 0.21.28
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 +26 -0
- package/.agents/skills/vidfarm/SKILL.md +6 -4
- package/.agents/skills/vidfarm/recipes/local-edit-render-approve.md +3 -2
- package/.agents/skills/vidfarm/references/assets-and-sourcing.md +3 -3
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +32 -0
- package/.agents/skills/vidfarm/references/editor-workflows.md +41 -5
- package/.agents/skills/vidfarm/references/onboarding.md +4 -4
- package/.agents/skills/vidfarm/references/primitives.md +5 -1
- package/SKILL.director.md +94 -19
- package/SKILL.md +5 -2
- package/dist/src/cli.js +69 -0
- package/dist/src/devcli/qa-check.js +420 -0
- package/package.json +3 -1
- package/public/serve-shells/tools-image.html +378 -265
- package/public/serve-shells/tools-video.html +760 -167
package/SKILL.director.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vidfarm
|
|
3
|
-
description: Use Vidfarm as a director. Browse/add inspiration videos, browse the free public raws catalog BY CATEGORY (curated shelves like scroll-stoppers/greenscreen/reaction — the cheapest way to source footage for one video, and a ready-made clip pool for bulk scripting N variants), fork a template into a composition, edit it in the Trackpad Editor (timeline-based like Premiere/DaVinci), auto-decompose source video into scenes, render to MP4, approve into a shareable post, and schedule it. Includes login, provider keys, discovery, versioning, uploads/downloads, and billing. Every step is available as raw REST; `vidfarm-devcli` wraps those routes and composes the file-backed scripting flows.
|
|
3
|
+
description: Use Vidfarm as a director. Run a strategy **consultation** (the `brainstorm/*` chain — cold-start interview, awareness stages, persuasive angles, hooks, product placement). Browse/add inspiration videos, browse the free public raws catalog BY CATEGORY (curated shelves like scroll-stoppers/greenscreen/reaction — the cheapest way to source footage for one video, and a ready-made clip pool for bulk scripting N variants), fork a template into a composition, edit it in the Trackpad Editor (timeline-based like Premiere/DaVinci), auto-decompose source video into scenes, render to MP4, approve into a shareable post, and schedule it. Includes login, provider keys, discovery, versioning, uploads/downloads, and billing. Every step is available as raw REST; `vidfarm-devcli` wraps those routes and composes the file-backed scripting flows.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Vidfarm Director
|
|
@@ -223,7 +223,9 @@ You may be running as the **in-web AI chat** (the /editor copilot, the chat dock
|
|
|
223
223
|
|
|
224
224
|
- **Determine the surface before claiming capabilities.** The web chat has only its declared tools and REST routes. It cannot execute arbitrary JavaScript/Python, open a shell, create a local repository script, or use the user's filesystem. Never tell a web-chat user that you ran code or wrote a script unless a dedicated declared tool actually did so. A desktop coding agent has a real shell and filesystem and MAY write/run scripts, perform arbitrary local computations over paginated API results, create reports/CSVs/JSON, edit composition files, and orchestrate long devcli workflows within the user's authorization.
|
|
225
225
|
- **The web AI chat can do all three paintbrushes** — clip raws, author HTML/hyperframe motion, and generate AI media — and it drives edits directly on the live timeline. Keep small-to-medium jobs here: text/caption swaps, a scene or two replaced, single generations, captions, approve/schedule. Just do them.
|
|
226
|
-
- **
|
|
226
|
+
- **No HTML slop — a video is not a web page.** Compositions are authored in HTML, so the #1 tell of an AI-edited video is landing-page furniture: gradient CTA "buttons" ("Sign Up for a Free Trial →"), rows of benefit chips/badges ("✓ No Credit Card Needed"), frosted/bordered cards holding a gradient headline + a URL, feature grids, bullet lists, web-default type (Inter/Roboto/Arial at weight 400-600). None of that exists in a real TikTok/Reel — **nothing in a video is clickable**. If you are typing `btn` / `badge` / `chip` / `card` / `rounded-full` / `shadow-lg` / `backdrop-blur` / `bg-gradient-to-r`, stop and rewrite it as timed text on the footage. Arrows, scribble/underline marks, italics, ALL-CAPS, single-word color pops, emoji, transparent cut-out stickers, and mock social UI (iMessage bubbles, comment cards) are all *fine* — they're native to the platform.
|
|
227
|
+
- **On devcli, QA every video you produce: `vidfarm qa ./work`.** Free, instant, local-only — it blocklists exactly the slop above plus font-regime/safe-zone drift, and prints a concrete fix per finding. **Feedback, not a gate**: it exits 0 even on findings, never runs automatically, and is a blocklist (unusual/stylized compositions pass untouched), so there's no reason not to run it before every publish. `--json` for scripted batches, `--strict` only if you want a CI failure. **Web-chat copilot: this command does not exist for you** (devcli-only, no REST twin) — apply the standard by hand, and when handing a heavy job to a local coding agent, tell them to run `vidfarm qa`.
|
|
228
|
+
- **Every production adheres to the TikTok-native caption standard.** On-screen text lives inside the readable safe zone (**~8%–85%** of a 9:16 frame — never pinned to the top/bottom edges the phone UI clips), uses the composition's bold font regime (**Montserrat** default / TikTok Sans, weight **700–900**, ~36–64px on a 1080-wide frame), and uses **exactly one of four valid backgrounds**: outline/stroke (`background_style:"outline"`, the default), plain + shadow (`"plain"`), an active-word highlight pill (`set_captions` `spotlight`/`karaoke` — the only legitimate pill), or a tight-hugging solid band (`"highlight-solid"`, radius ≤8px, no border/shadow/gradient/blur). Decomposed forks often inherit the source's edge-pinned caption in an off-regime font — fix it, don't inherit it. Local devcli renders auto-normalize position + font family only (never the slop), so author it correctly. Full rules in `references/editor-workflows.md` ("Social-native visual standard" + "TikTok-native caption standard").
|
|
227
229
|
- **Where the web chat struggles: complex, long, multi-step transformations.** A full multi-scene re-theme, an iterative render-critique-iterate loop, heavy scripted or batch work, or anything needing a real filesystem and many sequential tool calls will hit context limits, turn/timeout ceilings, and the web editor's constraints (CSS/declarative motion only — JS animation adapters are stripped on save). Don't grind a big transformation one layer at a time in a chat turn and stall.
|
|
228
230
|
- **Practical workaround — hand the heavy job to local devcli.** When a task is genuinely large or long-running, **proactively recommend the director run it locally with an AI coding agent** (Claude Code / OpenAI Codex / any capable agent): `vidfarm pull <forkId>` writes the composition + the `.harness/` grounding bundle to disk, the agent edits with the full devcli verb set and JS animation adapters, renders free with `vidfarm serve`, and `vidfarm publish` pushes it back. This is the **best-quality (B) harness's** natural home (adversarial grading with a coding agent). Frame it as "this is a big rebuild — you'll get a better, faster result running it locally with a coding agent; here's how," not as a dead end.
|
|
229
231
|
- **Offer a handoff, do not impersonate the desktop agent.** When web chat reaches that boundary, offer to save a Markdown handoff in My Files containing the objective, selected template/fork IDs, asset paths, grounding, constraints, completed work, and suggested devcli commands. Create it only after the user agrees. The desktop agent should read that document, pull the referenced fork, and then use its actual code/shell capabilities.
|
|
@@ -237,7 +239,7 @@ Read only the relevant reference file for the current task.
|
|
|
237
239
|
- Timeline editing, decompose, captions, motion, AI placement: `references/editor-workflows.md`
|
|
238
240
|
- Raws hunts, My Files, recurring characters, asset retrieval: `references/assets-and-sourcing.md`
|
|
239
241
|
- REST automation, `vidfarm` command surface, local serve loop, skill packs: `references/automation-and-local-dev.md`
|
|
240
|
-
- Getting-started interviews, strategy docs, onboarding flow: `references/onboarding.md`
|
|
242
|
+
- Getting-started interviews, **consultations**, strategy docs, onboarding flow: `references/onboarding.md`
|
|
241
243
|
- Primitive routes such as TTS, STT, music, overlays, background removal, product placement: `references/primitives.md`
|
|
242
244
|
- Complete REST API map and raw-HTTP conventions: `references/rest-api.md`. Load it only when the user asks for REST, an endpoint/schema, direct HTTP integration, or exhaustive API coverage. For the entire specification, follow its domain links and load every listed reference; do not preload them into ordinary director conversations.
|
|
243
245
|
|
|
@@ -259,7 +261,7 @@ HyperFrames authoring and rendering in this package are Vidfarm-native: local wo
|
|
|
259
261
|
|
|
260
262
|
Choose the narrowest path that satisfies the request.
|
|
261
263
|
|
|
262
|
-
1. If the user needs help figuring out what to make, read `references/onboarding.md` first.
|
|
264
|
+
1. If the user needs help figuring out what to make, **or asks for a "consultation"** (the `brainstorm/*` chain: cold-start interview → awareness stages → angles → hooks), read `references/onboarding.md` first.
|
|
263
265
|
2. If the user already knows the goal and needs a suitable template, read `references/core-workflows.md` and use the template discovery flow.
|
|
264
266
|
3. If the task is “change this video,” read `references/editor-workflows.md`.
|
|
265
267
|
4. If the task is “find footage” or “use our existing assets,” read `references/assets-and-sourcing.md`.
|
|
@@ -1004,16 +1006,52 @@ Three surfaces:
|
|
|
1004
1006
|
- **Editor UI (human)** — the Inspector's **Animate (Ken Burns)** dropdown on any selected image.
|
|
1005
1007
|
- **Desktop agents (devcli)** — `vidfarm place ./work --src <img> --kind image --ken-burns zoom-in [--ken-burns-intensity 0.3]`, or fused with generation: `vidfarm generate image --prompt "…" --place ./work --ken-burns pan-left`.
|
|
1006
1008
|
|
|
1007
|
-
###
|
|
1009
|
+
### Social-native visual standard — no HTML slop, ever
|
|
1008
1010
|
|
|
1009
|
-
|
|
1011
|
+
Compositions are authored in HTML, so the single most common way an AI-edited video betrays itself is by **looking like a web page**. A landing-page button, a row of feature chips, a frosted card with a gradient headline — these are everywhere on the web and **nowhere in a real social post**. A director scrolling TikTok/Reels/Shorts never sees them, so the moment one appears the video reads as machine-made and the retention dies. HTML and hyperframes are the right tool; **Bootstrap/Tailwind furniture is not.**
|
|
1012
|
+
|
|
1013
|
+
**The one test:** *would this element exist if the video were shot on a phone and captioned in CapCut?* If it only makes sense inside a browser — if its whole job is to look **clickable** — cut it. **Nothing in a video is clickable.**
|
|
1014
|
+
|
|
1015
|
+
**BANNED — never author, and strip on sight when a fork or a paste brings one in:**
|
|
1016
|
+
|
|
1017
|
+
- **CTA / "button" shapes.** Any filled capsule or rounded rect containing action copy — "Sign Up for a Free Trial →", "Learn More", "Get Started", "Book a Call" — especially gradient-filled with a glow or drop shadow. A social CTA is *spoken*, or a plain caption line, or an arrow pointing at the real UI. Not a button.
|
|
1018
|
+
- **Badge / chip / pill rows.** The "✓ ID-Verified Tutors · ✓ No Credit Card Needed · ✓ 30-Min Trial" strip of small rounded tags. Nothing on TikTok is a `<span class="badge">`. Say the three benefits as three timed caption lines instead — one at a time, on the footage.
|
|
1019
|
+
- **Cards, panels, containers.** A rounded-rect box (dark, bordered, shadowed, or `backdrop-filter` frosted) holding a heading plus a subheading/URL. Text belongs **on the footage**, not inside a floating panel with padding around it.
|
|
1020
|
+
- **Gradient text fills, neon border glows, elevation shadows, glassmorphism, hover-implying strokes.**
|
|
1021
|
+
- **Web page furniture of any kind:** navbars, hero sections, feature grids, two-column layouts, `<ul>` bullet lists with disc markers, tables, alerts, progress bars as decoration, logo-in-a-circle avatars, "as seen in" strips.
|
|
1022
|
+
- **Corporate web type:** Inter, Roboto, system-ui, Arial, Helvetica, Georgia, Times — at web weights (400–600) and web sizes (16–24px). Instantly reads as "a screenshot of a website."
|
|
1023
|
+
|
|
1024
|
+
**Greppable smell test.** If you are typing `class="btn…"`, `badge`, `chip`, `tag`, `card`, `panel`, `container`, `row`/`col-`, `hero`, `cta`, `rounded-full`, `shadow-lg`, `backdrop-blur`, `bg-gradient-to-r`, `border border-…` — **stop.** You are building a web page, not a video. Rewrite as timed text on footage.
|
|
1025
|
+
|
|
1026
|
+
**ALLOWED and encouraged — these ARE social-native:**
|
|
1027
|
+
|
|
1028
|
+
- **Arrows** (drawn, animated, hand-style), circles/scribbles/underline strokes highlighting part of the frame, hand-drawn marks, checkmarks *as glyphs inside a caption line* (not as chips).
|
|
1029
|
+
- **Italic / oblique** for emphasis, ALL-CAPS, a single word in a pop color, mixed weights across a stacked line.
|
|
1030
|
+
- **Emoji inline in text** (sparingly), **sticker/cut-out overlays** on transparent PNG (`create-overlay`), mock social UI when the format calls for it (iMessage bubbles, a TikTok comment card, a fake DM, a countdown/progress bar) — these are native artifacts of the platform, not web furniture.
|
|
1031
|
+
- **Full-bleed footage** with text sitting directly on it.
|
|
1032
|
+
|
|
1033
|
+
**On devcli there's a checker: `vidfarm qa <dir|composition.html>`.** Free, instant, local-only — a blocklist pass for everything above plus the font regime and safe zone, with a concrete fix per finding. **Run it on every video you produce.** It is feedback, not a gate (exit 0 even on findings, never runs automatically, `--strict` only if you want a CI failure) and a blocklist, not an allowlist (stylized/hand-made compositions pass untouched — it will not homogenize your videos). No cloud/REST twin: the web copilot enforces this standard by hand. Details in `references/automation-and-local-dev.md` ("`vidfarm qa`").
|
|
1034
|
+
|
|
1035
|
+
### TikTok-native caption standard (position + font + background) — always adhere
|
|
1036
|
+
|
|
1037
|
+
Short-form is watched on a phone, and the phone's UI eats the frame's edges. **Never pin on-screen text to the extreme top or bottom** — the top ~8% sits under the status bar / "Following · For You" tabs and the bottom ~15% under the username, caption text, music marquee, and action rail. Text there is literally clipped and reads as amateur. Three rules, applied to **every** caption/title/overlay you place or inherit:
|
|
1010
1038
|
|
|
1011
1039
|
- **Position → readable safe zone.** Keep the text box's vertical extent inside **~8%–85%** of canvas height (9:16). A lower-third subtitle lives around **y≈70%** (the `captions generate` default); a "me when…" / "POV:" meme line sits near the **top of the safe zone (y≈8%), not y:0**. Preserve the original top-vs-bottom anchoring — just pull it off the dead-zone edge, don't recenter everything. Also keep wide captions clear of the **right ~12%** action rail (a centered box at `x:10 width:80` is safe).
|
|
1012
|
-
- **Font → the composition regime.** Use the bundled display fonts only — **Montserrat** (bold default, weight
|
|
1040
|
+
- **Font → the composition regime.** Use the bundled display fonts only — **Montserrat** (bold default, weight **700–900**), **TikTok Sans**, Abel, Source Code Pro, Yesteryear. Don't request a font the composition doesn't import (it silently falls back to a web-default sans, which is exactly the slop look). Sizes are in PIXELS of a 1080-wide frame: **~36–64px** reads well, never below ~28px and **never 0** (invisible). Keep captions to ~2 lines / ~5 words per line; `line_height` 0.95–1.15 for stacked display lines.
|
|
1041
|
+
- **Background → one of exactly four valid treatments.** Any text you place uses one of these and nothing else:
|
|
1013
1042
|
|
|
1014
|
-
|
|
1043
|
+
| # | Treatment | How to set it | When |
|
|
1044
|
+
|---|---|---|---|
|
|
1045
|
+
| 1 | **Bare text + heavy outline/stroke** (black stroke on white or a bright fill) | `background_style:"outline"` | The default TikTok look |
|
|
1046
|
+
| 2 | **Bare text + soft drop shadow only**, no box | `background_style:"plain"` | Clean/cinematic over busy footage |
|
|
1047
|
+
| 3 | **Highlight pill behind the ACTIVE word only** | `set_captions caption_style:"spotlight"` / `"karaoke"` (+ `caption_highlight_color`) | Hormozi/CapCut word-by-word. **The only legitimate "pill" in a video** — it tracks the spoken word, so it isn't a badge |
|
|
1048
|
+
| 4 | **Solid band that tightly hugs the text lines** (CapCut "text box") | `background_style:"highlight-solid"` (or `"highlight-translucent"`) + a `background` color | Guaranteed legibility over noisy footage |
|
|
1015
1049
|
|
|
1016
|
-
|
|
1050
|
+
Treatment 4 is a **band, not a card**: it hugs the glyphs with minimal padding, corner radius ≤ ~8px, **no border, no drop shadow, no gradient, no blur**, and it wraps *one* text run — never a heading + subheading + URL stacked inside one rounded box. The moment it grows padding, a stroke, or a second element inside it, it has become a web card. Fix it.
|
|
1051
|
+
|
|
1052
|
+
**A common trap: decomposed templates mirror the source's caption placement**, so a forked meme can arrive with its caption pinned at `top:0` in a non-regime font — and a re-theme prompt ("make this for my tutoring service") is exactly where an agent starts inventing landing-page CTAs and benefit chips because the *subject* is a SaaS product. **Fix to the standard, don't inherit it, and don't import the website's design language into the video.** When placing text yourself (`set_captions`, `set_layer_text`, `set_layer_style`, `add_layer`, devcli `place`/`captions`), set `y` / `font_family` / `font_weight` / `background_style` to the standard from the start.
|
|
1053
|
+
|
|
1054
|
+
> Local devcli renders enforce part of this automatically: `renderCompositionLocally` runs `normalizeTikTokCaptionLayout` (src/devcli/composition-edit.ts) on every production, clamping caption/text layers into the 8%–85% safe zone and coercing off-regime primary fonts to Montserrat. It only fixes position and font family — it will happily render your Bootstrap card. Get it right in the composition so the editor preview, the local render, and any cloud render match.
|
|
1017
1055
|
|
|
1018
1056
|
### Animated captions — word-by-word caption styles (TikTok/CapCut)
|
|
1019
1057
|
|
|
@@ -1201,11 +1239,11 @@ Local scans persist to a SQLite library under `~/.vidfarm` (free compute, subscr
|
|
|
1201
1239
|
|
|
1202
1240
|
## My Files (the user's asset library)
|
|
1203
1241
|
|
|
1204
|
-
Each user has a persistent **My Files** filesystem — their own uploaded videos (mp4/mov/webm), images (png/jpg/jpeg/gif/webp/svg), audio (mp3/wav/m4a/aac), and documents (pdf/md/txt/csv) — organized into **virtual folders**. This is where a user keeps footage, brand assets, logos, music, briefs, scripts, and the durable strategy docs from Getting Started (
|
|
1242
|
+
Each user has a persistent **My Files** filesystem — their own uploaded videos (mp4/mov/webm), images (png/jpg/jpeg/gif/webp/svg), audio (mp3/wav/m4a/aac), and documents (pdf/md/txt/csv) — organized into **virtual folders**. This is where a user keeps footage, brand assets, logos, music, briefs, scripts, and the durable strategy docs from Getting Started / a consultation (OFFER.md, awareness-levels.md, etc.). Both the editor AI copilot and an agent CLI can **read and write** it to find or save assets instead of asking the user to re-upload or paste URLs.
|
|
1205
1243
|
|
|
1206
1244
|
- **List** — `GET /api/v1/user/me/attachments` → `{ attachments: [{ id, fileName, contentType, sizeBytes, folderPath, viewUrl, createdAt }], folders: [string] }`. The `viewUrl` is a durable URL you can drop into a composition layer or pass into a primitive route. devcli: `vidfarm files [--folder <path>]`.
|
|
1207
1245
|
- **Read one** — resolve it from the list and stream/read its `viewUrl`. devcli: `vidfarm get-file <id> [dest]` (add `--print` to print text contents of md/txt/csv/json inline). For images/video/audio/pdf, reference the `viewUrl` as media — you can't read their bytes as text.
|
|
1208
|
-
- **Write into My Files** — `POST /api/v1/user/me/attachments/upload` (multipart `file` + optional `folder_path`) persists a file into the library. devcli: `vidfarm put-file <localFile> --folder <path>`, or pipe/inline text without a temp file: `echo "…" | vidfarm put-file --stdin --as
|
|
1246
|
+
- **Write into My Files** — `POST /api/v1/user/me/attachments/upload` (multipart `file` + optional `folder_path`) persists a file into the library. devcli: `vidfarm put-file <localFile> --folder <path>`, or pipe/inline text without a temp file: `echo "…" | vidfarm put-file --stdin --as OFFER.md --folder acme` / `vidfarm put-file --content "…" --as OFFER.md`. This is how an agent saves Getting Started / consultation context (OFFER.md — or OFFER_<NAME>.md for a multi-offer director — plus awareness-levels.md, persuasive-angles.md, ad-hooks.md) or drops a logo/product demo into a namescoped folder. In the web editor copilot the same capability is the **`browse_files` write action** (`action=write` with `file_name` + `content` + optional `folder_path`), which accepts text files (md/txt/csv/json/srt/vtt).
|
|
1209
1247
|
- **`vidfarm upload` is different** — it targets the **ephemeral** temporary-files store (`POST /api/v1/user/me/temporary-files/upload`) for a throwaway durable URL to drop straight into a composition or approved post. Use `put-file` when you want the asset to persist in the user's My Files library; use `upload` for one-shot media you just need a URL for. Namescope scratch uploads under a **`temp/` folder** (`vidfarm upload clip.mp4 --folder temp`) so they stay in one purgeable place. Better still, if you're on a `vidfarm serve` box, skip the upload entirely and reference the file off disk with `place --src ./clip.mp4` (see "Local file paths as media").
|
|
1210
1248
|
|
|
1211
1249
|
### Metadata notes + vector search (find assets by meaning)
|
|
@@ -1270,7 +1308,7 @@ The web copilot exposes the same three as `browse_files action=rename|move|copy`
|
|
|
1270
1308
|
|
|
1271
1309
|
**Assume My Files is multi-offer.** A user often runs more than one product, offer, brand, or region, and namescopes assets into folders accordingly — by product (`acme-skincare/`, `zensleep/`), by offer/campaign (`summer-sale/`), by region (`us/`, `eu/`), by asset type (`logos/`, `ugc-clips/`), or any arbitrary scheme. There is **no fixed layout** — read the `folders` tree first and reason about how this user organized things. Before pulling assets for a task, infer which folder(s) match the product/offer/region the current composition is about (match folder names to the composition title, video context, and what the user said), then scope reads to that folder so you never mix one brand's logo/product-shot/music into another brand's video. If the target offer is ambiguous, ask which product/offer/region (or which folder) this work is for rather than guessing across offers.
|
|
1272
1310
|
|
|
1273
|
-
In the editor web copilot the same filesystem is exposed via the **`browse_files` tool** (`action=list` / `action=search` / `action=read` / `action=write` / `action=annotate` / `action=move` / `action=copy` / `action=rename`), so the copilot follows the identical reasoning: search or list to find the right offer's folder, then read an asset — or `write` a text doc (
|
|
1311
|
+
In the editor web copilot the same filesystem is exposed via the **`browse_files` tool** (`action=list` / `action=search` / `action=read` / `action=write` / `action=annotate` / `action=move` / `action=copy` / `action=rename`), so the copilot follows the identical reasoning: search or list to find the right offer's folder, then read an asset — or `write` a text doc (OFFER.md, awareness-levels.md, …) or import a media URL (`source_url`) into that folder, annotating anything worth finding again. `action=rename`/`move`/`copy` reorganize the tree (see the three routes above) — use them to keep character folders and asset names tidy. `browse_files list` defaults to `path='/'` when `path` is omitted, so the `/raws` (hunted raws) and `/temp` (scratch) roots surface alongside the My Files folders instead of being hidden; a `/raws` listing also accepts a `content_type` filter (exact shot-kind — `talking_head`, `b_roll`, `product_shot`, `screen_recording`, …), and every listing paginates via `offset` / `limit`. The devcli equivalents are `vidfarm files [--search]` / `get-file` / `put-file [--notes]` / `annotate-file` / `directory rename|move|copy`.
|
|
1274
1312
|
|
|
1275
1313
|
## Free-plan API keys — valid everywhere, gated per route
|
|
1276
1314
|
|
|
@@ -1493,6 +1531,7 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
1493
1531
|
| `vidfarm raws preset list\|run\|save` / `raws export <ids…> --to <dir>` | (local library) | saved queries; copy raw MP4s out |
|
|
1494
1532
|
| `vidfarm lint <dir\|composition.html>` | (local static validation) | pre-publish composition check: timing, overlaps, preset names, media src |
|
|
1495
1533
|
| `vidfarm stills <dir> [--at 0,2.5,…]` | (local in-process render of PNG frames) | visually verify an edit without a full render |
|
|
1534
|
+
| `vidfarm qa <dir\|composition.html> [--json] [--strict]` | (local static QA — **devcli-only**, no cloud/REST twin) | **social-native QA: HTML slop + font regime. Run it on EVERY video you produce.** Free, instant, feedback-only |
|
|
1496
1535
|
| `vidfarm doctor` | (local environment triage) | check ffmpeg/node/keys/agent CLI/poisoned env + list local serve/preview processes before debugging anything else; `--kill-orphans` reaps dead servers squatting ports (fixes the "Waiting for preview server…" hang) |
|
|
1497
1536
|
| `vidfarm skills list\|add <name>\|update` | `GET /skill-pack/index.json` · `/skill-pack/:name/*` | install/refresh skill packs (see "Skill packs — import on demand") |
|
|
1498
1537
|
| `vidfarm tts "…" --engine local` / `vidfarm stt <file> --engine whisper` | (keyless LOCAL engines: Kokoro-82M TTS, whisper.cpp STT) | narration + word-timestamp transcripts with zero keys and zero accounts |
|
|
@@ -1506,6 +1545,37 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
1506
1545
|
|
|
1507
1546
|
**Approving a locally rendered MP4 (the URL-first rule + the durability rule).** The approve route (`POST /api/v1/approved/posts`) and every media-taking route accept a `url`, never raw file bytes — so you never "upload to approve" in one shot. An approved post is a **permanent** share page, so the media must live in **durable My Files**, not the 30-day temp store (a temp-hosted video would 404 the share page after 30 days). The correct sequence for a big local file is: (1) `POST /api/v1/user/me/attachments/presign` with `{ file_name, content_type, size_bytes }` → (2) PUT the raw bytes to the returned presigned S3 URL → (3) `POST /api/v1/user/me/attachments` (finalize) → use the returned durable `viewUrl` as the approve media `url`. `vidfarm approve --video ./final.mp4` does all of this automatically (durable by default; up to **200 MB**). Never POST a large file as multipart to `.../attachments/upload` against the cloud host: that path proxies through Lambda and caps near 6 MB (it exists only as a fallback for local-storage `vidfarm serve` boxes). Only use the temp-store route (`.../temporary-files/*`, or `vidfarm approve --temp`) for a **throwaway** preview you don't mind losing in 30 days.
|
|
1508
1547
|
|
|
1548
|
+
## `vidfarm qa` — the social-native QA pass (devcli-only, heavily recommended)
|
|
1549
|
+
|
|
1550
|
+
```bash
|
|
1551
|
+
vidfarm qa ./work # human-readable findings + verdict
|
|
1552
|
+
vidfarm qa ./work --json # machine-readable: rule / severity / where / fix
|
|
1553
|
+
vidfarm qa ./work --strict # ALSO exit 1 on slop (only if you want a CI gate)
|
|
1554
|
+
```
|
|
1555
|
+
|
|
1556
|
+
**Run this on every video you produce.** It is free, instant (pure DOM, no ffmpeg/Chrome/network), and it is the only automated check for the thing that most often ruins an agent-made video: **HTML slop**. Compositions are authored in HTML, so an agent's web-page instincts leak straight onto the frame as landing-page furniture that appears on every website and in **zero** real TikToks.
|
|
1557
|
+
|
|
1558
|
+
**It is feedback, not a gate.** Default exit code is **0** even when it finds slop; nothing in the render or publish path calls it; it never runs automatically. `--strict` exists only if you deliberately want a CI failure. A finding you disagree with is fine to ignore and say so — it is a lint, not a verdict on the work.
|
|
1559
|
+
|
|
1560
|
+
**It is a BLOCKLIST, not an allowlist.** It names specific known-bad web patterns. Everything it doesn't name is legal, so a weird, ugly, hand-made, or wildly stylized composition passes untouched. It will never push your videos toward one house style — if it fires on a genuine creative choice, that's a bug in the rule, not in your video.
|
|
1561
|
+
|
|
1562
|
+
What it flags:
|
|
1563
|
+
|
|
1564
|
+
| Rule | Severity | What trips it |
|
|
1565
|
+
|---|---|---|
|
|
1566
|
+
| `cta-button` | error | Action copy ("Sign Up for a Free Trial →") **inside** a filled/gradient rounded capsule. Bare CTA copy in a caption is fine — "BUY NOW" is real social copy |
|
|
1567
|
+
| `badge-chip-row` | error | 2+ sibling small rounded filled tags — the "✓ No Credit Card Needed · ✓ 30-Min Trial" strip |
|
|
1568
|
+
| `card-panel` / `glass-card` | error | A rounded box with a border/shadow/`backdrop-filter` wrapping 2+ elements. A tight legibility **band** (radius ≤8px, one text run, no border/shadow) stays legal |
|
|
1569
|
+
| `gradient-text` | error | `background-clip:text` gradient headline fills |
|
|
1570
|
+
| `clickable-element` | error/warn | `<button>`, `<form>`, `<input>`; `<a href>` warns |
|
|
1571
|
+
| `web-framework-classes` | error/warn | Bootstrap/Tailwind class tokens (`btn`, `badge`, `card`, `hero`, `col-*`, `rounded-full`, `shadow-lg`, `backdrop-blur`, `bg-gradient-to-*`) or a linked CSS framework. A `<script>` CDN for GSAP/anime.js is fine |
|
|
1572
|
+
| `page-structure` / `bullet-list` | error/warn | `<nav>`/`<header>`/`<footer>`/`<table>`; a `<ul>` with visible bullet markers |
|
|
1573
|
+
| `font-regime` | error/warn | A text layer in a website body font (Inter/Roboto/Arial/system-ui → **error**) or any family outside the imported regime (Montserrat, TikTok Sans, Abel, Source Code Pro, Yesteryear → warn, it silently falls back at render) |
|
|
1574
|
+
| `font-size` / `font-weight` | error/warn | `font-size:0` (invisible) is an error; sub-2.6%-of-canvas-width text and weight <600 warn |
|
|
1575
|
+
| `caption-safe-zone` | warn | Text outside the 8%–85% band on a **portrait** canvas (landscape/square are exempt) |
|
|
1576
|
+
|
|
1577
|
+
Every finding carries a concrete `fix` line — the answer is always "say it as timed text on the footage", never just "delete it". Fold `--json` into scripted batch runs to QA N variants at once.
|
|
1578
|
+
|
|
1509
1579
|
## Cost mode — the devcli's money-saving guardrail
|
|
1510
1580
|
|
|
1511
1581
|
The four modes, quoted as **cost per finished video**. The first two are spend postures; the last two are also *production methods* — `rich-ai` buys reusable greenscreen raws and animates them in HTML, `pure-videogen` generates every frame:
|
|
@@ -1646,13 +1716,13 @@ Vidfarm is in active development. Endpoints, response shapes, and the editor UI
|
|
|
1646
1716
|
- treat `latest_version` as the source of truth for what the editor loaded
|
|
1647
1717
|
- if a publish fails, check `renders/:renderId` for the error phase and stderr before retrying
|
|
1648
1718
|
|
|
1649
|
-
## Getting started / onboarding
|
|
1719
|
+
## Getting started / onboarding / consultation
|
|
1650
1720
|
|
|
1651
|
-
|
|
1721
|
+
Run this flow when the director signals they **don't know where to start** ("getting started", "help me set up", "I don't know where to begin") **or asks for a "consultation"** ("give me a consultation", "consult me on my offer", "I want a strategy consultation"). **"Consultation" is a first-class trigger for the brainstorm chain** — treat it as a request for the full run: the cold-start interview → awareness stages → angles → hooks (steps 1-4 below), driven by the `brainstorm/*` primitives rather than brainstormed in chat from memory. If they already know what they want, skip straight to it — never force onboarding. Directors can also jump to any single step (e.g. "just find me a good template for X" → go straight to template discovery, or "consult me on hooks" → run `brainstorm/hooks` alone).
|
|
1652
1722
|
|
|
1653
1723
|
The point of onboarding is to build **durable, reusable context** in My Files, namescoped under the director's product/offer folder, so future chats and agents can read it back. Save each artifact as Markdown with `browse_files` write (web copilot) or `vidfarm put-file` (devcli):
|
|
1654
1724
|
|
|
1655
|
-
1. **Product context** →
|
|
1725
|
+
1. **Product context** → **`OFFER.md`** (or `OFFER_<NAME>.md` — e.g. `OFFER_ACME_SKINCARE.md` — when the director runs more than one offer). Drive the interview questions with `brainstorm/coldstart` and **recommend the director save their answers into that file**, either locally next to their project or in cloud My Files. This is the durable artifact every later step reads back; `About.md` / `Interview.md` are legacy names — prefer `OFFER*.md` for anything new.
|
|
1656
1726
|
2. **Awareness level** (Eugene Schwartz — problem-aware, solution-unaware, …) → `awareness-levels.md`. If it's genuinely unknown after thinking it through, note that ads for **every** level should be made and tested. Use `brainstorm/awareness_stages`.
|
|
1657
1727
|
3. **Persuasive angles** → `persuasive-angles.md`, via `brainstorm/angles`.
|
|
1658
1728
|
4. **Hooks** → `ad-hooks.md`, via `brainstorm/hooks`.
|
|
@@ -1660,7 +1730,7 @@ The point of onboarding is to build **durable, reusable context** in My Files, n
|
|
|
1660
1730
|
6. **Budget** — ask roughly what they want to spend per video, and map it to the Cost spectrum (free reuse+local render → pennies for cloud render → ~$1 for some AI scenes → $10+ for heavy AI gen). This sets which approach you default to and whether AI **video** generation is on the table (ask permission before using it; image gen is cheap and fine). Budget can also be revisited per editor project.
|
|
1661
1731
|
7. **Recommend & adapt a template** — pair what you now know about the offer against the decomposed template catalog (`GET /discover/feed?q=<offer>`, read each result's `promotions`/`keywords`/`summary`), recommend the best 3-6, then fork and **modify** the winner to fit their offer. Prefer already-decomposed templates so the director skips the ~$0.10 decompose cost.
|
|
1662
1732
|
|
|
1663
|
-
**Assume multiple offers.** My Files is multi-offer (see the My Files section) — namescope every onboarding artifact under the right product/offer/region folder (`acme-skincare/
|
|
1733
|
+
**Assume multiple offers.** My Files is multi-offer (see the My Files section) — namescope every onboarding artifact under the right product/offer/region folder (`acme-skincare/OFFER.md`, not a bare `OFFER.md`) so one brand's context never bleeds into another's. When a director keeps several offers in one flat folder, name the files `OFFER_ACME_SKINCARE.md` / `OFFER_ACME_SUPPLEMENTS.md` instead.
|
|
1664
1734
|
|
|
1665
1735
|
## Default assistance pattern
|
|
1666
1736
|
|
|
@@ -1991,7 +2061,11 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/audio/transcribe" \
|
|
|
1991
2061
|
|
|
1992
2062
|
## Brainstorm primitives
|
|
1993
2063
|
|
|
1994
|
-
The `brainstorm/*` primitives are the strategy toolkit. They are reusable, billable AI reasoning steps — the same family the AI Copilot exposes as chip suggestions. Treat **product placement** as a first-class member of this family, right alongside angles and hooks
|
|
2064
|
+
The `brainstorm/*` primitives are the strategy toolkit. They are reusable, billable AI reasoning steps — the same family the AI Copilot exposes as chip suggestions. Treat **product placement** as a first-class member of this family, right alongside angles and hooks.
|
|
2065
|
+
|
|
2066
|
+
When a director asks for a **"consultation"**, that is this toolkit: run the chain `coldstart` (the interview) → `awareness_stages` → `angles` → `hooks`, saving each output to My Files as you go (see `references/onboarding.md`). Never improvise a consultation from memory when these routes exist. "Consult me on hooks/angles" → run just that primitive.
|
|
2067
|
+
|
|
2068
|
+
`coldstart` returns *questions*, not answers — always recommend the director write their answers into an **`OFFER.md`** (or `OFFER_<NAME>.md` when they run several offers), locally or in cloud My Files. That file becomes the `offer_description` you feed into `awareness_stages`, `angles`, `hooks`, and `product_placement`.
|
|
1995
2069
|
|
|
1996
2070
|
- `POST /api/v1/primitives/brainstorm/coldstart` — `{ payload: { user_message } }` → foundational questionnaire for a customer starting from zero.
|
|
1997
2071
|
- `POST /api/v1/primitives/brainstorm/awareness_stages` — `{ payload: { offer_description } }` → which Eugene-Schwartz awareness stages to target first.
|
|
@@ -2069,8 +2143,9 @@ Use this when a coding agent is doing the work locally or the user wants a repro
|
|
|
2069
2143
|
3. Read `./work/.harness/agent-guide.md` and `./work/.harness/context.json` before editing.
|
|
2070
2144
|
4. Make deterministic edits to `composition.html` and optionally `composition.json`.
|
|
2071
2145
|
5. Validate with `vidfarm lint` or `vidfarm stills` when useful.
|
|
2072
|
-
6.
|
|
2073
|
-
7.
|
|
2146
|
+
6. **QA before you render: `vidfarm qa ./work`.** Free, instant, devcli-only. It blocklists HTML slop (CTA buttons, benefit chip rows, frosted cards, gradient text, web-page classes/fonts) and checks the caption font regime + safe zone. 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.
|
|
2147
|
+
7. Render with `vidfarm render <forkId> --dir ./work --wait`.
|
|
2148
|
+
8. Approve the finished MP4 with `vidfarm approve --video <url|./final.mp4> --caption "..."`. This prints the shareable `share_url`.
|
|
2074
2149
|
|
|
2075
2150
|
**Approving a locally rendered file → cloud preview link.** Approve takes media by **URL**, not bytes, and an approved post is **permanent** — so the local MP4 must land in **durable My Files**, not the 30-day temp store (a temp video would 404 the share page after 30 days). The devcli presigns, PUTs the bytes **direct to S3**, finalizes, then approves with that durable URL — so `vidfarm approve --video ./final.mp4` handles files up to **200 MB**, bypasses the ~6 MB Lambda request-body limit, and the share link never breaks. By raw REST: `POST /api/v1/user/me/attachments/presign` → PUT to the returned S3 URL → `POST /api/v1/user/me/attachments` (finalize) → pass the returned `viewUrl` in the approve `media` array. Do not multipart-POST a big file to `.../attachments/upload` against the cloud host (Lambda-bound, ~6 MB cap). Add `vidfarm approve --temp` only when you want a disposable 30-day preview.
|
|
2076
2151
|
|
package/SKILL.md
CHANGED
|
@@ -72,7 +72,7 @@ This page does not contain the workflows. Pull the full director skill and load
|
|
|
72
72
|
- `editor-workflows` — timeline editing, decompose, captions, the three paintbrushes & two replication harnesses
|
|
73
73
|
- `assets-and-sourcing` — raws hunts, My Files, recurring characters
|
|
74
74
|
- `automation-and-local-dev` — REST + `vidfarm` devcli + local serve loop
|
|
75
|
-
- `onboarding` — "help me figure out what to make" interview
|
|
75
|
+
- `onboarding` — "help me figure out what to make" interview, and the **consultation** flow
|
|
76
76
|
- `primitives` — TTS, STT, music, overlays, background removal, product placement
|
|
77
77
|
- `rest-api` — complete REST map (load only for direct HTTP integration)
|
|
78
78
|
- **Updating Vidfarm:** `https://vidfarm.cc/update.md` — **fetch this file whenever the user asks to update/upgrade Vidfarm**, says their skill or devcli is out of date, or hits a command/route that "used to work". It's the clean uninstall→reinstall runbook for BOTH halves (the `vidfarm` skill pack and `@officexapp/vidfarm-devcli`), and it carries the current breaking-change notes — read it live rather than improvising an `npm i -g`, because the notes are exactly what a blind reinstall misses.
|
|
@@ -83,7 +83,7 @@ For composition *authoring* craft (motion, keyframes, scene design), Vidfarm shi
|
|
|
83
83
|
|
|
84
84
|
## Router — where to go from the user's ask
|
|
85
85
|
|
|
86
|
-
1. "Help me figure out what to make" → `references/onboarding.md`
|
|
86
|
+
1. "Help me figure out what to make" **or "give me a consultation"** → `references/onboarding.md`. A consultation *is* the `brainstorm/*` chain: cold-start interview → awareness stages → angles → hooks. Run the primitives; don't improvise the strategy in chat.
|
|
87
87
|
2. "Find a template and start" → `references/core-workflows.md`
|
|
88
88
|
3. "Change / re-theme this video" → `references/editor-workflows.md`
|
|
89
89
|
4. "Find footage / use our assets" → `references/assets-and-sourcing.md`
|
|
@@ -99,6 +99,9 @@ For composition *authoring* craft (motion, keyframes, scene design), Vidfarm shi
|
|
|
99
99
|
- Never build composition HTML by string concatenation — parse, edit, re-serialize the DOM.
|
|
100
100
|
- Render only through `POST /api/v1/compositions/:forkId/render`; never call the renderer directly.
|
|
101
101
|
- Submissions are **not idempotent** — every render/primitive POST charges again. Check status before retrying.
|
|
102
|
+
- **No HTML slop.** Compositions are HTML, but a video is not a web page: never author CTA "buttons", benefit chip/badge rows, frosted or bordered cards holding a headline + URL, gradient text, feature grids, or bullet lists — none of that exists in a real TikTok, and nothing in a video is clickable. Say it as timed text on the footage. Arrows, scribble/underline marks, italics, ALL-CAPS, color pops, emoji, cut-out stickers, and mock social UI are fine.
|
|
103
|
+
- **On devcli, `vidfarm qa <dir>` before every render.** Free, instant, local-only blocklist for the slop above + the font regime. Feedback, not a gate (exits 0, never automatic). No REST/web equivalent.
|
|
104
|
+
- **Caption regime is mandatory**: an imported display font (Montserrat default / TikTok Sans), weight 700–900, ~36–64px on a 1080-wide frame, inside the 8%–85% safe zone, and exactly one of four backgrounds — `outline`, `plain`, an active-word `spotlight`/`karaoke` pill, or a tight-hugging `highlight-solid` band (radius ≤8px, no border/shadow/gradient/blur).
|
|
102
105
|
- In the web editor, CSS/declarative motion only (JS animation adapters are stripped on save); locally via `vidfarm serve` the full JS adapters work.
|
|
103
106
|
- Treat `forkId` as an unguessable bearer token. Never put provider secrets in composition HTML/JSON.
|
|
104
107
|
|
package/dist/src/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ import { formatCompositionLintIssues, lintCompositionHtml } from "./services/com
|
|
|
19
19
|
import { resolveFfmpeg } from "./services/clip-curation/ffmpeg.js";
|
|
20
20
|
import { parseHyperframesJson, runHyperframesCommand } from "./devcli/hyperframes-cli.js";
|
|
21
21
|
import { renderCompositionStills } from "./devcli/stills.js";
|
|
22
|
+
import { formatQaReport, qaCompositionHtml } from "./devcli/qa-check.js";
|
|
22
23
|
import { removeGreenscreenLocal, localGreenscreenAvailable, defaultGreenscreenOutPath, GREENSCREEN_PRESETS, trimTransparentBorders, cropImageRegion } from "./devcli/greenscreen-local.js";
|
|
23
24
|
import { runDoctorCommand } from "./devcli/doctor.js";
|
|
24
25
|
import { findFreePort } from "./devcli/port-utils.js";
|
|
@@ -595,6 +596,16 @@ Local media engines & toolchain (all local, free, no account — no cloud key ne
|
|
|
595
596
|
lint <dir|composition.html> Validate a composition on disk (same (local)
|
|
596
597
|
checks the /editor chat preflights); prints
|
|
597
598
|
errors/warnings, exit 1 on errors [--json]
|
|
599
|
+
qa <dir|composition.html> Social-native QA pass — HIGHLY RECOMMENDED (local, devcli-only)
|
|
600
|
+
on every video you produce. Blocklists "HTML
|
|
601
|
+
slop" (CTA buttons, benefit chip rows, frosted
|
|
602
|
+
cards, gradient text, web-page classes/fonts)
|
|
603
|
+
and checks the caption font regime + safe zone.
|
|
604
|
+
Feedback only: exits 0 even when it finds slop,
|
|
605
|
+
and it never runs automatically. Blocklist, not
|
|
606
|
+
allowlist — unusual styles pass untouched
|
|
607
|
+
--json Machine-readable findings (rule/severity/fix)
|
|
608
|
+
--strict Also exit 1 when slop is found (for CI)
|
|
598
609
|
stills <dir|composition.html> Render PNG stills of the CURRENT (local in-process render)
|
|
599
610
|
composition — the visual self-check loop
|
|
600
611
|
("did my edit look right") without a full render
|
|
@@ -1177,6 +1188,11 @@ async function main() {
|
|
|
1177
1188
|
case "lint":
|
|
1178
1189
|
await runLintCommand(rest);
|
|
1179
1190
|
return;
|
|
1191
|
+
case "qa":
|
|
1192
|
+
case "qa-check":
|
|
1193
|
+
case "slop-check":
|
|
1194
|
+
await runQaCommand(rest);
|
|
1195
|
+
return;
|
|
1180
1196
|
case "stills":
|
|
1181
1197
|
await runStillsCommand(rest);
|
|
1182
1198
|
return;
|
|
@@ -2156,6 +2172,8 @@ Rules:
|
|
|
2156
2172
|
- When swapping visuals, match both the literal scene DNA and the narrative purpose of the beat.
|
|
2157
2173
|
- For replacement graphics, screenshots, or still-like scenes, prefer AI image generation plus Ken Burns before paying for AI video unless static_vs_pivot says motion footage is load-bearing.
|
|
2158
2174
|
- If narration must be customized, default to premium ElevenLabs first, then the user's own ElevenLabs path, then BYOK OpenAI/Gemini/OpenRouter. If captions or scenes were timed to the old VO, retime them to the new narration.
|
|
2175
|
+
- NO HTML SLOP. You are editing HTML, but the output is a social video, not a web page. Never author landing-page furniture: CTA "buttons" (a filled/gradient rounded capsule with action copy like "Sign Up for a Free Trial →"), benefit chip/badge rows ("✓ No Credit Card Needed"), bordered/shadowed/frosted cards holding a headline + URL, gradient text fills, feature grids, bulleted lists, or web-default fonts (Inter/Roboto/Arial/system-ui). None of that appears in a real TikTok, and nothing in a video is clickable — say it as timed text on the footage instead. Arrows, scribble/underline marks, italics, ALL-CAPS, single-word color pops, emoji, transparent cut-out stickers, and mock social UI (iMessage bubbles, comment cards) are all fine. Captions use an imported family (Montserrat default / TikTok Sans / Abel / Source Code Pro / Yesteryear) at weight 700-900, ~36-64px on a 1080-wide frame, inside the 8%-85% safe zone, with exactly one of four backgrounds: outline, plain, an active-word spotlight/karaoke pill, or a tight-hugging solid band (radius <=8px, no border/shadow/gradient/blur).
|
|
2176
|
+
- QA EVERY VIDEO BEFORE YOU RENDER: run \`vidfarm qa .\` in this directory. It's free, instant, and local — a blocklist for the slop above plus the font regime and safe zone, with a concrete fix per finding. It's feedback, not a gate (exits 0 even on findings, never runs automatically) and a blocklist, not an allowlist, so stylized or hand-made work passes untouched. Fix what's real, ignore what's a deliberate style call. \`--json\` for scripted batches.
|
|
2159
2177
|
|
|
2160
2178
|
The three paintbrushes (Vidfarm is thrift-first — do NOT spend AI credits on every scene):
|
|
2161
2179
|
- Paint each REPLACE beat with one of three brushes, cheapest first: (1) RAW CLIPS remixed from existing footage (\`vidfarm raws search\` the local/library first, then \`vidfarm raws scan <url|file>\` to hunt new ones — free local compute); (2) HTML/JS HYPERFRAMES — animate text/images/logos/stickers/charts with CSS or JS adapters (anime.js/GSAP/Lottie/Three) for titles, kinetic captions, data beats, brand cards; (3) PURE AI GENERATION (\`vidfarm generate image|video\`) — most expensive, AI video especially, last resort for beats no clip or hyperframe can cover.
|
|
@@ -3575,6 +3593,9 @@ async function runRenderCommand(argv) {
|
|
|
3575
3593
|
}
|
|
3576
3594
|
console.log(`${GREEN}Rendered locally in ${(result.durationMs / 1000).toFixed(1)}s → ${result.outputPath}${RESET}`);
|
|
3577
3595
|
console.log(`${DIM}$0.00 — local Vidfarm render, no cloud charge.${RESET}`);
|
|
3596
|
+
// Nudge, not a gate: QA never runs automatically, but every production
|
|
3597
|
+
// should get a look before it's published.
|
|
3598
|
+
console.log(`${DIM}Recommended: ${RESET}vidfarm qa ${sourceDir ?? "<dir>"}${DIM} — social-native QA (HTML slop + font regime). Feedback only, free, instant.${RESET}`);
|
|
3578
3599
|
}
|
|
3579
3600
|
return;
|
|
3580
3601
|
}
|
|
@@ -8615,6 +8636,54 @@ async function runLintCommand(argv) {
|
|
|
8615
8636
|
if (!result.ok)
|
|
8616
8637
|
process.exitCode = 1;
|
|
8617
8638
|
}
|
|
8639
|
+
// `vidfarm qa <dir|composition.html>` — the social-native QA pass. Compositions
|
|
8640
|
+
// are authored in HTML, so an agent's web-page instincts leak in as landing-page
|
|
8641
|
+
// furniture (gradient CTA capsules, benefit chip rows, frosted cards) that never
|
|
8642
|
+
// appears in a real TikTok. This is a BLOCKLIST of those specific patterns plus
|
|
8643
|
+
// the font regime — deliberately NOT an allowlist, so weird/ugly/hand-made
|
|
8644
|
+
// compositions pass untouched and nothing pushes every video toward one style.
|
|
8645
|
+
//
|
|
8646
|
+
// FEEDBACK, NOT A GATE: exit code stays 0 even when slop is found, unless the
|
|
8647
|
+
// caller opts into --strict. Nothing in the render path calls this. It is a
|
|
8648
|
+
// devcli-only capability (no cloud/REST twin) — heavily recommended after every
|
|
8649
|
+
// production, never mandatory and never automatic.
|
|
8650
|
+
async function runQaCommand(argv) {
|
|
8651
|
+
const parsed = parseArgs({
|
|
8652
|
+
args: argv,
|
|
8653
|
+
allowPositionals: true,
|
|
8654
|
+
options: {
|
|
8655
|
+
json: { type: "boolean", default: false },
|
|
8656
|
+
strict: { type: "boolean", default: false }
|
|
8657
|
+
}
|
|
8658
|
+
});
|
|
8659
|
+
const target = parsed.positionals[0];
|
|
8660
|
+
if (!target)
|
|
8661
|
+
throw new Error("qa requires a composition path: `vidfarm qa <dir-or-composition.html> [--json] [--strict]`.");
|
|
8662
|
+
const htmlPath = resolveCompositionHtmlPath(target);
|
|
8663
|
+
const report = qaCompositionHtml(readFileSync(htmlPath, "utf8"));
|
|
8664
|
+
if (parsed.values.json) {
|
|
8665
|
+
printJson(report);
|
|
8666
|
+
if (!report.ok && parsed.values.strict)
|
|
8667
|
+
process.exitCode = 1;
|
|
8668
|
+
return;
|
|
8669
|
+
}
|
|
8670
|
+
const body = formatQaReport(report, { red: RED, yellow: YELLOW, green: GREEN, dim: DIM, reset: RESET });
|
|
8671
|
+
if (body)
|
|
8672
|
+
console.log(body);
|
|
8673
|
+
const scope = `${report.checked.layers} layer(s), ${report.checked.text_layers} text layer(s)${report.checked.canvas ? `, ${report.checked.canvas}` : ""}`;
|
|
8674
|
+
if (report.verdict === "clean") {
|
|
8675
|
+
console.log(`${GREEN}✓ no HTML slop found${RESET} ${DIM}— ${scope} — ${htmlPath}${RESET}`);
|
|
8676
|
+
}
|
|
8677
|
+
else if (report.verdict === "warnings") {
|
|
8678
|
+
console.log(`${YELLOW}! ${report.warnings.length} warning(s), no slop${RESET} ${DIM}— ${scope} — ${htmlPath}${RESET}`);
|
|
8679
|
+
}
|
|
8680
|
+
else {
|
|
8681
|
+
console.log(`${RED}✗ ${report.errors.length} slop issue(s)${RESET}, ${report.warnings.length} warning(s) ${DIM}— ${scope} — ${htmlPath}${RESET}`);
|
|
8682
|
+
console.log(`${DIM}This is feedback, not a gate — fix what's real and re-run. A deliberate stylistic choice that trips a rule is fine to ignore.${RESET}`);
|
|
8683
|
+
}
|
|
8684
|
+
if (!report.ok && parsed.values.strict)
|
|
8685
|
+
process.exitCode = 1;
|
|
8686
|
+
}
|
|
8618
8687
|
// `vidfarm stills <dir|composition.html>` — render PNG stills of the CURRENT
|
|
8619
8688
|
// composition in-process (the agent's visual self-verification loop).
|
|
8620
8689
|
async function runStillsCommand(argv) {
|