@mevdragon/vidfarm-devcli 0.20.6 → 0.20.9

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.
Files changed (157) hide show
  1. package/.agents/skills/editor-capabilities/SKILL.md +15 -1
  2. package/.agents/skills/vidfarm-director/SKILL.md +106 -0
  3. package/.agents/skills/vidfarm-director/recipes/find-and-fork-template.md +16 -0
  4. package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +13 -0
  5. package/.agents/skills/vidfarm-director/recipes/onboard-a-new-director.md +13 -0
  6. package/.agents/skills/vidfarm-director/recipes/retheme-template.md +17 -0
  7. package/.agents/skills/vidfarm-director/references/assets-and-sourcing.md +117 -0
  8. package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +245 -0
  9. package/.agents/skills/vidfarm-director/references/core-workflows.md +273 -0
  10. package/.agents/skills/vidfarm-director/references/editor-workflows.md +365 -0
  11. package/.agents/skills/vidfarm-director/references/onboarding.md +28 -0
  12. package/.agents/skills/vidfarm-director/references/primitives.md +308 -0
  13. package/SKILL.director.md +517 -250
  14. package/dist/src/cli.js +563 -99
  15. package/dist/src/devcli/clips.js +29 -12
  16. package/dist/src/devcli/doctor.js +2 -2
  17. package/dist/src/devcli/local-backend.js +46 -2
  18. package/dist/src/services/clip-curation/cost.js +5 -1
  19. package/dist/src/services/clip-curation/gemini.js +14 -2
  20. package/dist/src/services/clip-curation/hunt.js +56 -0
  21. package/dist/src/services/clip-curation/index.js +1 -1
  22. package/dist/src/services/clip-curation/scan.js +5 -3
  23. package/package.json +30 -10
  24. package/SKILL.platform.md +0 -432
  25. package/demo/README.md +0 -28
  26. package/demo/dist/app.css +0 -1
  27. package/demo/dist/app.js +0 -1850
  28. package/demo/dist/chunks/chunk-DXB73IDG.js +0 -1
  29. package/demo/dist/chunks/chunk-S7OWAJDS.js +0 -36
  30. package/demo/dist/chunks/chunk-VTIBZ6AN.js +0 -1
  31. package/demo/dist/chunks/dist-ADSJKBVE.js +0 -332
  32. package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +0 -1
  33. package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +0 -425
  34. package/demo/dist/chunks/lib-XAQ37YOE.js +0 -1
  35. package/demo/dist/chunks/src-TJ2QYA4U.js +0 -207
  36. package/demo/dist/favicon.ico +0 -0
  37. package/demo/dist/icons/timeline/audio.svg +0 -7
  38. package/demo/dist/icons/timeline/captions.svg +0 -5
  39. package/demo/dist/icons/timeline/composition.svg +0 -12
  40. package/demo/dist/icons/timeline/image.svg +0 -18
  41. package/demo/dist/icons/timeline/music.svg +0 -10
  42. package/demo/dist/icons/timeline/text.svg +0 -3
  43. package/demo/dist/index.html +0 -15
  44. package/dist/src/account-pages-legacy.js +0 -9396
  45. package/dist/src/account-pages.js +0 -61
  46. package/dist/src/app.js +0 -21603
  47. package/dist/src/composition-runtime.js +0 -1053
  48. package/dist/src/config.js +0 -217
  49. package/dist/src/context.js +0 -447
  50. package/dist/src/dev-app-legacy.js +0 -739
  51. package/dist/src/dev-app.js +0 -6
  52. package/dist/src/devcli/migrate-local.js +0 -140
  53. package/dist/src/devcli/sync.js +0 -368
  54. package/dist/src/domain.js +0 -5
  55. package/dist/src/editor-chat-history.js +0 -82
  56. package/dist/src/editor-chat.js +0 -828
  57. package/dist/src/editor-dark-theme.js +0 -1128
  58. package/dist/src/frontend/debug.js +0 -71
  59. package/dist/src/frontend/discover-client.js +0 -130
  60. package/dist/src/frontend/discover-store.js +0 -23
  61. package/dist/src/frontend/file-directory.js +0 -1018
  62. package/dist/src/frontend/flockposter-cache-store.js +0 -124
  63. package/dist/src/frontend/homepage-client.js +0 -446
  64. package/dist/src/frontend/homepage-shared.js +0 -201
  65. package/dist/src/frontend/homepage-store.js +0 -66
  66. package/dist/src/frontend/homepage-view.js +0 -705
  67. package/dist/src/frontend/page-runtime-client.js +0 -132
  68. package/dist/src/frontend/page-runtime-store.js +0 -9
  69. package/dist/src/frontend/sentry.js +0 -42
  70. package/dist/src/frontend/template-editor-chat.js +0 -4181
  71. package/dist/src/help-page.js +0 -346
  72. package/dist/src/homepage.js +0 -1458
  73. package/dist/src/index.js +0 -16
  74. package/dist/src/instrument.js +0 -30
  75. package/dist/src/landing-page.js +0 -384
  76. package/dist/src/page-runtime.js +0 -2
  77. package/dist/src/page-shell.js +0 -1452
  78. package/dist/src/primitive-context.js +0 -416
  79. package/dist/src/primitive-registry.js +0 -3940
  80. package/dist/src/primitive-sdk.js +0 -4
  81. package/dist/src/primitives/hyperframes-media.js +0 -108
  82. package/dist/src/react-page-shell.js +0 -35
  83. package/dist/src/ready-post-schedule-component.js +0 -1540
  84. package/dist/src/registry.js +0 -296
  85. package/dist/src/reskin/agency-page.js +0 -299
  86. package/dist/src/reskin/calendar-page.js +0 -568
  87. package/dist/src/reskin/chat-page.js +0 -942
  88. package/dist/src/reskin/discover-page.js +0 -1788
  89. package/dist/src/reskin/document.js +0 -1587
  90. package/dist/src/reskin/help-page.js +0 -357
  91. package/dist/src/reskin/index-page.js +0 -62
  92. package/dist/src/reskin/inpaint-clipper-page.js +0 -890
  93. package/dist/src/reskin/inpaint-page.js +0 -2554
  94. package/dist/src/reskin/inpaint-video-page.js +0 -1339
  95. package/dist/src/reskin/job-runs-page.js +0 -477
  96. package/dist/src/reskin/library-page.js +0 -1634
  97. package/dist/src/reskin/login-page.js +0 -262
  98. package/dist/src/reskin/portfolio-page.js +0 -687
  99. package/dist/src/reskin/pricing-page.js +0 -390
  100. package/dist/src/reskin/settings-page.js +0 -732
  101. package/dist/src/reskin/theme.js +0 -711
  102. package/dist/src/runtime.js +0 -35
  103. package/dist/src/services/api-call-history.js +0 -249
  104. package/dist/src/services/auth.js +0 -152
  105. package/dist/src/services/billing-pricing.js +0 -39
  106. package/dist/src/services/billing.js +0 -241
  107. package/dist/src/services/cast.js +0 -127
  108. package/dist/src/services/chat-threads.js +0 -92
  109. package/dist/src/services/clip-records.js +0 -250
  110. package/dist/src/services/clip-search.js +0 -77
  111. package/dist/src/services/clip-vectors.js +0 -125
  112. package/dist/src/services/composition-sanitize.js +0 -124
  113. package/dist/src/services/composition-watch.js +0 -79
  114. package/dist/src/services/elevenlabs.js +0 -222
  115. package/dist/src/services/file-directory.js +0 -117
  116. package/dist/src/services/fork-access.js +0 -93
  117. package/dist/src/services/fork-manifest.js +0 -42
  118. package/dist/src/services/ghostcut.js +0 -179
  119. package/dist/src/services/hyperframes.js +0 -3654
  120. package/dist/src/services/job-capacity.js +0 -14
  121. package/dist/src/services/job-logs.js +0 -197
  122. package/dist/src/services/jobs.js +0 -136
  123. package/dist/src/services/local-dynamo.js +0 -0
  124. package/dist/src/services/media-processing.js +0 -766
  125. package/dist/src/services/primitive-media-lambda.js +0 -280
  126. package/dist/src/services/providers.js +0 -2748
  127. package/dist/src/services/rate-limits.js +0 -262
  128. package/dist/src/services/scene-annotations.js +0 -32
  129. package/dist/src/services/serverless-auth.js +0 -382
  130. package/dist/src/services/serverless-jobs.js +0 -1084
  131. package/dist/src/services/serverless-provider-keys.js +0 -409
  132. package/dist/src/services/serverless-records.js +0 -1515
  133. package/dist/src/services/serverless-template-configs.js +0 -75
  134. package/dist/src/services/storage.js +0 -461
  135. package/dist/src/services/swipe-customize.js +0 -437
  136. package/dist/src/services/template-certification.js +0 -413
  137. package/dist/src/services/template-loader.js +0 -99
  138. package/dist/src/services/template-runtime-bundles.js +0 -217
  139. package/dist/src/services/template-sources.js +0 -1017
  140. package/dist/src/services/upstream.js +0 -248
  141. package/dist/src/services/video-normalization.js +0 -2
  142. package/dist/src/services/webhooks.js +0 -62
  143. package/dist/src/template-editor-pages.js +0 -2576
  144. package/dist/src/template-editor-shell.js +0 -2893
  145. package/dist/src/template-sdk.js +0 -4
  146. package/dist/src/worker.js +0 -17
  147. package/public/assets/discover-client-app.js +0 -1
  148. package/public/assets/file-directory-app.js +0 -3
  149. package/public/assets/homepage-app.js +0 -54
  150. package/public/assets/homepage-client-app.js +0 -80
  151. package/public/assets/page-runtime-client-app.js +0 -94
  152. package/public/assets/placeholders/scene-placeholder.png +0 -0
  153. package/src/assets/SELLING_AWARENESS_STAGES.md +0 -579
  154. package/src/assets/SELLING_WITH_HOOKS.md +0 -377
  155. package/src/assets/SELLING_WITH_VSLS.md +0 -606
  156. package/src/assets/favicon.ico +0 -0
  157. package/src/assets/logo-vidfarm.png +0 -0
package/SKILL.director.md CHANGED
@@ -1,24 +1,109 @@
1
1
  ---
2
2
  name: vidfarm-director
3
- description: Use Vidfarm as a director. Browse/add inspiration videos, 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. Browse/add inspiration videos, browse/save public raws, 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
7
7
 
8
- Vidfarm is a **video composition studio**. Directors fork a published template, edit it on a timeline in the **Trackpad Editor**, publish to MP4, and share.
8
+ Vidfarm is a video composition studio. Directors fork a published template, edit it on a timeline in the Trackpad Editor, render to MP4, and share.
9
9
 
10
10
  Use this skill when the user wants to:
11
11
 
12
- - log in with OTP and save their AI provider keys
13
- - discover published composition templates
14
- - fork a template into an editable composition
15
- - edit the timeline (add/remove/reorder layers, retime clips, swap media)
16
- - auto-decompose a raw source video into scenes with AI
17
- - publish a fork to MP4 and share the result
18
- - browse version history and revert
19
- - manage sharing/visibility
12
+ - log in and save provider keys
13
+ - discover templates or add a new inspiration
14
+ - fork a template and edit a composition
15
+ - re-theme or rebuild a video while preserving the viral DNA
16
+ - render, approve, and schedule a post
17
+ - automate Vidfarm through REST, `vidfarm`, or a local `vidfarm serve` loop
18
+ - manage files, raws, recurring characters, versions, or sharing
20
19
 
21
- Do **not** use this skill to author new templates from scratch, deploy platform infrastructure, or discuss internal architecture those belong to `SKILL.platform.md` (if it exists) or a separate developer workflow.
20
+ Do not use this skill to author new templates from scratch, deploy platform infrastructure, or discuss internal platform architecture. Those belong in a separate developer or platform workflow.
21
+
22
+ ## Default stance
23
+
24
+ - Treat the Trackpad Editor as the primary surface. Reach for templates and forks before primitives.
25
+ - Default to the cheapest approach that works. Reuse footage first, use image generation freely, and ask before AI video generation.
26
+ - Prefer already-decomposed templates when they match the user’s goal.
27
+ - For heavy edits, read the grounding artifacts before acting: `video-context.json`, `editor-harness.json`, and local `.harness/*` bundles when present.
28
+ - For agentic rewrites, think in the three axes: scenes, audio, text. Decide whether each axis is a SWAP or a REPLACE.
29
+
30
+ ## The three paintbrushes (Vidfarm's operating philosophy)
31
+
32
+ Vidfarm is founder-friendly and pragmatic: **we do not burn expensive AI credits on everything.** Every visual on the timeline is painted with one of three "paintbrushes," and for bulk creation it is often combinatorially cheaper to reach for the first two before the third:
33
+
34
+ 1. **Raw clips** — cut and remix footage from existing long-form or short-form video (the director's own library, or freshly hunted out of a URL/VOD). Cheapest, and the workhorse for scene REPLACE.
35
+ 2. **HTML/JS hyperframes** — video-from-HTML: CSS/declarative animation, anime.js/GSAP motion, animated image + text elements, data-viz, modeling. Cheap, deterministic, infinitely re-themeable.
36
+ 3. **Pure AI generation** — AI image/video/voice/music. The most expensive brush; AI **video** especially. Use last, only where the other two genuinely cannot cover the beat.
37
+
38
+ Directors also accumulate a **reusable media asset library** — logos, stickers, reactions, b-roll, a-roll, a brand media kit. Recreation should have an opinion on **when and where** to reuse these. But respect the format's viral DNA: a director can accidentally reskin away the very thing that makes the template land, so tie every asset/paintbrush recommendation back to the harness (`viral_dna`, `editor-harness.json`) rather than swapping freely.
39
+
40
+ **A technical replication decomposition names, per template, the actual method for each beat — when/where/what is raw clips vs HTML hyperframes vs pure AI gen — and offers two harnesses:**
41
+
42
+ - **(A) Cheap & efficient** *(default)* — recaption text; background-video + foreground-video memes; animate HTML/image elements with hyperframes; reuse library media; AI-generate a reusable element **once** then reuse it; greenscreen; raw-clip long-form and remix; lean on the memes/reactions/b-roll/a-roll library and brand media kit; only if genuinely needed, reach for AI image/video/voice/music.
43
+ - **(B) Best quality** — AI video generation by default; storyboard with AI **image** first; then adversarially grade the result with a coding agent (Claude Code / Codex / any capable AI agent) and iterate.
44
+
45
+ Present both harnesses to the director, recommend (A) unless they've asked for premium or budget covers it, and explain the tradeoff in these terms. Full methodology: `references/editor-workflows.md` (“The three paintbrushes & two replication harnesses”); cost bands: `references/core-workflows.md` (Cost spectrum).
46
+
47
+ **Be generous to free-tier / no-account users.** Vidfarm's harness is open source; an everyday capable AI agent should be able to follow this decomposition and recreate a template on its own, without a Vidfarm wallet — so keep the recommendation self-contained and pragmatic, not gated behind paid primitives.
48
+
49
+ **Free tier vs. paid — who does the decomposition.** On the free tier (local devcli, no Vidfarm account) the harness gives the *method*, not the pre-computed answer: **the user (and their AI agent) is responsible for watching the reference video, decomposing it into its elements, and deciding the three paintbrushes themselves** — there is no `video-context.json` / `editor-harness.json` / `scene-annotations.json` handed to them. Be honest about that and coach them through doing it. **Paid Vidfarm accounts** get the leverage: a massive library of **pre-decomposed viral videos** (the decompose passes already run — viral DNA, emotional punch, editor harness, per-scene recreation annotations) plus **prompt-harness best practices distilled from platform-scale learnings**. When a free-tier user is grinding the decomposition by hand, it's fair to mention that a Vidfarm account would hand them the decomposition and the proven harness instead.
50
+
51
+ ## Web AI chat vs. local devcli — know your surface's limits
52
+
53
+ You may be running as the **in-web AI chat** (the /editor copilot, the chat dock, or the /chat and /library assistants) or as a **local coding agent** driving `vidfarm-devcli` (Claude Code / Codex on the user's machine). Same three paintbrushes, different reach — be honest about which surface you are and route heavy work accordingly:
54
+
55
+ - **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.
56
+ - **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.
57
+ - **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.
58
+ - **Never send the user away just to read knowledge.** Deeper skill knowledge is always a **tool call** away in-place: call `load_skill` (e.g. `load_skill('vidfarm-director', file='references/editor-workflows.md')`, or a craft pack like `editor-capabilities` / `hyperframes-animation`) to pull the exact reference you need mid-conversation. Only recommend switching surfaces for the WORK (a heavy transformation), never for the information.
59
+
60
+ ## Read Only What You Need
61
+
62
+ Read only the relevant reference file for the current task.
63
+
64
+ - Template discovery, auth, fork/publish/share/cost flow: `references/core-workflows.md`
65
+ - Timeline editing, decompose, captions, motion, AI placement: `references/editor-workflows.md`
66
+ - Raws hunts, My Files, recurring characters, asset retrieval: `references/assets-and-sourcing.md`
67
+ - REST automation, `vidfarm` command surface, local serve loop, skill packs: `references/automation-and-local-dev.md`
68
+ - Getting-started interviews, strategy docs, onboarding flow: `references/onboarding.md`
69
+ - Primitive routes such as TTS, STT, music, overlays, background removal, product placement: `references/primitives.md`
70
+
71
+ ## Quick Router
72
+
73
+ Choose the narrowest path that satisfies the request.
74
+
75
+ 1. If the user needs help figuring out what to make, read `references/onboarding.md` first.
76
+ 2. If the user already knows the goal and needs a suitable template, read `references/core-workflows.md` and use the template discovery flow.
77
+ 3. If the task is “change this video,” read `references/editor-workflows.md`.
78
+ 4. If the task is “find footage” or “use our existing assets,” read `references/assets-and-sourcing.md`.
79
+ 5. If the task is scripted, local, CI-driven, or `vidfarm serve`-based, read `references/automation-and-local-dev.md`.
80
+ 6. If the task explicitly asks for a primitive or needs specialized generation/transcription work, read `references/primitives.md`.
81
+
82
+ ## Non-Negotiables
83
+
84
+ - API-key auth is the `vidfarm-api-key` header. Do not use `Authorization: Bearer`.
85
+ - Do not manipulate composition HTML by string concatenation. Parse, edit, and re-serialize the DOM.
86
+ - Do not call the renderer directly. Rendering goes through `POST /api/v1/compositions/:forkId/render`.
87
+ - Do not store provider secrets in composition HTML or JSON.
88
+ - Treat `forkId` as an unguessable bearer token for read access.
89
+ - Submission routes are generally not idempotent. Especially for renders and expensive primitives, check status before retrying.
90
+ - In the web editor, use CSS/declarative motion only. Script-bearing HTML is stripped or rejected there.
91
+
92
+ ## Recommended Recipes
93
+
94
+ Use these when the user’s task matches the pattern closely.
95
+
96
+ - Template selection and first fork: `recipes/find-and-fork-template.md`
97
+ - Full re-theme while preserving the format’s feel: `recipes/retheme-template.md`
98
+ - Local pull/edit/render/approve loop: `recipes/local-edit-render-approve.md`
99
+ - New-director onboarding and durable context capture: `recipes/onboard-a-new-director.md`
100
+
101
+ ## Output Posture
102
+
103
+ - Prefer concrete actions over abstract discussion.
104
+ - Name the chosen path explicitly: template reuse, raws hunt, local serve, cloud render, etc.
105
+ - Surface cost tradeoffs before expensive generation.
106
+ - When in doubt between a broad reference and a recipe, start with the recipe.
22
107
 
23
108
  ## Mental model
24
109
 
@@ -95,7 +180,7 @@ To bring a new viral video into the catalog as a **private** template you own, i
95
180
  - `DELETE /discover/templates/:entryId` — remove a private inspiration/template you own (accepts either the `inspiration_...` or minted `template_...` id).
96
181
  - `POST /api/v1/inspirations/:inspirationId/decompose { user_prompt? }` — AI-decompose an inspiration's downloaded video into scenes (requires a saved provider key; same 120s source cap as auto-decompose).
97
182
 
98
- devcli: `vidfarm inspiration-add <url|file.mp4>` (a local file path runs the presign→PUT→finalize flow for you), `vidfarm inspiration-rm <id>`, `vidfarm inspiration-decompose <id>`, `vidfarm discover [query]` to browse/search templates, and `vidfarm videos [query] [--mine]` to browse/search source inspirations. On the web, the Discover page's **Add Template** modal accepts either a URL or a file upload.
183
+ devcli: `vidfarm inspiration-add <url|file.mp4>` (a local file path runs the presign→PUT→finalize flow for you), `vidfarm inspiration-rm <id>`, `vidfarm inspiration-decompose <id>`, `vidfarm discover [query]` to browse/search templates, `vidfarm public-raws [query] [--category <key>] [--type <raw_type>] [--bookmark <raw_id>]` to browse/save the public raws catalog, and `vidfarm videos [query] [--mine]` to browse/search source inspirations. On the web, the Discover page's **Add Template** modal accepts either a URL or a file upload.
99
184
 
100
185
  ## Fork a template
101
186
 
@@ -136,18 +221,163 @@ Content-Type: application/json
136
221
 
137
222
  Returns the same fork metadata shape as `POST /api/v1/compositions` (201). devcli: `vidfarm clone <forkId> [--from current|default] [--version N] [--title …]`. In the in-editor AI chat this is the `editor_action` `fork_composition` action (`fork_from: current|default`).
138
223
 
139
- ## Automate a template via REST
224
+ ## Render (publish to MP4)
140
225
 
141
- Templates now have the same job-backed REST pattern as primitives, so a script can run them repeatably without the editor UI:
226
+ Rendering publishes the fork's current working state to an MP4 using HyperFrames (cloud Lambda fan-out on the deployed host, or the free in-process renderer on a local `vidfarm serve` box). In the Trackpad Editor this is the **Render** button. The REST route is `/render`; the older `/export` path is kept as a deprecated alias for already-published devcli clients (treat "Render", "publish", `/render`, and `/export` as the same operation). The devcli exposes it as `vidfarm render <forkId> [--wait]`; scripted local dirs can use `vidfarm render <forkId> --dir ./work --wait`.
142
227
 
143
228
  ```
144
- POST /api/v1/templates/:templateId/operations/:operationName
229
+ POST /api/v1/compositions/:forkId/render
145
230
  Content-Type: application/json
146
231
 
147
- { "tracer": "my-run", "payload": { ... }, "webhook_url": "https://..." }
232
+ { "title": "optional", "version": null, "tracer": "optional-trace-id", "html": "optional HTML to save to working state before rendering" }
148
233
  ```
149
234
 
150
- The route returns `202` with a `job_id`, and you can poll the job with `GET /api/v1/user/me/jobs/:jobId` or the template-scoped `GET /api/v1/templates/:templateId/jobs/:jobId`. devcli wraps the same flow as `vidfarm template run <templateId> <operationName> --payload-file payload.json --wait`.
235
+ Returns 202 with `{ ok, renderId, status, progress, expectedOutputPublicUrl, outputUrl, cost, title, version, ... }` where `expectedOutputPublicUrl` is the deterministic public MP4 URL and `version` is the snapshot version created for this publish. Failure modes: `402` if the fork owner is not on a paid plan, `409` while GhostCut subtitle removal is still `pending` (retry after `/remove-video-captions-poll` reports done/failed), `412` if the provider-key preflight fails. Poll:
236
+
237
+ ```
238
+ GET /api/v1/compositions/:forkId/renders/:renderId
239
+ ```
240
+
241
+ Response includes `{ ok, renderId, status: "RUNNING" | "SUCCEEDED" | "FAILED", progress, framesRendered, totalFrames, cost, expectedOutputPublicUrl, outputUrl, outputS3Uri, errors }` (`progress` is 0..1). On success, `expectedOutputPublicUrl` is the durable public MP4 URL and `outputUrl` remains the completion-time field.
242
+
243
+ Every publish creates an immutable version snapshot at `versions/<N>/composition.html` and `versions/<N>/composition.json`.
244
+
245
+ The Web UI **Render** button and devcli render both use this same endpoint. The fast `202` response includes the deterministic `expectedOutputPublicUrl` so a caller can store or pass along the final public S3 URL before the render has completed, then poll by `renderId` until `status` settles.
246
+
247
+ ## Approve a finished post
248
+
249
+ A render produces a bare MP4 URL. **Approving** wraps that MP4 (plus caption, title, pinned comment, and any carousel slides) into a shareable preview page — the phone-mockup page a human opens to review and copy the post.
250
+
251
+ ```
252
+ POST /api/v1/approved/posts
253
+ Content-Type: application/json
254
+
255
+ { "caption": "required", "title": "optional", "pinned_comment": "optional", "tracer": "optional",
256
+ "media": [ { "url": "https://.../output.mp4", "kind": "video", "role": "primary" } ] }
257
+ ```
258
+
259
+ `caption` is required. `media[]` items take `{ url, kind?: "image"|"video"|..., role?: "primary"|"slide"|... }`. Response (`201`) is the approved post including **`share_url`** — the prod frontend page for previewing/sharing. Surface that URL to the user; it is the headline output of this step.
260
+
261
+ - `GET /api/v1/approved/posts` — list your approved posts
262
+ - `GET /api/v1/approved/posts/:postId` — read one (returns `share_url`, `download_zip_url`)
263
+
264
+ devcli: `vidfarm approve --video <mp4-url> --caption "..."` prints the `share_url` as a first-class openable link; `vidfarm posts` lists, `vidfarm post <id>` reads one.
265
+
266
+ ## Schedule a post
267
+
268
+ Schedule an approved post to a connected destination channel (FlockPoster social account or email) at one ISO timestamp:
269
+
270
+ ```
271
+ POST /api/v1/approved/posts/:postId/schedules
272
+ Content-Type: application/json
273
+
274
+ { "destination_type": "flockposter" | "email", "destination_id": "<channel or email>",
275
+ "scheduled_at": "2026-07-10T14:00:00Z", "timezone": "America/New_York", "additional_notes": "optional" }
276
+ ```
277
+
278
+ Minimum 10-minute lead time. Response (`201`) is the schedule record. Browse existing schedules with `GET /api/v1/approved/posts/:postId/schedules`.
279
+
280
+ devcli: `vidfarm schedule <postId> --at <iso> --to <destinationId> [--type flockposter|email]`, and `vidfarm schedules <postId>` to browse.
281
+
282
+ ## Version history
283
+
284
+ ```
285
+ GET /api/v1/compositions/:forkId/versions?limit=100
286
+ ```
287
+
288
+ Returns `{ versions: [{ version, reason, message, created_by, created_at, render_job_id, composition_url, composition_data_url }], next_cursor }`. Reason is `publish`, `clone`, `manual`, or `migration`. To snapshot the working state manually, `POST /api/v1/compositions/:forkId/versions { "message": "optional" }`.
289
+
290
+ To view a past cut without reverting:
291
+
292
+ ```
293
+ GET /api/v1/compositions/:forkId/versions/:version/composition.html
294
+ ```
295
+
296
+ To revert the working state to a version, `PUT` the historical HTML/JSON back to the working keys.
297
+
298
+ ## Sharing and visibility
299
+
300
+ Every fork has a visibility: `private` (default), `unlisted` (legacy), or `public` (view-only).
301
+
302
+ ```
303
+ PATCH /api/v1/compositions/:forkId/visibility { "visibility": "public" }
304
+ ```
305
+
306
+ Public forks are accessible at `/editor/<templateId>?fork=<forkId>` (view-only for non-owners). Public visibility is the simplest sharing surface.
307
+
308
+ Fine-grained sharing uses per-user permissions and share links (all body fields are snake_case):
309
+
310
+ - `POST /api/v1/compositions/:forkId/permissions { grantee_email | grantee_customer_id, role, expires_at? }` — grant a specific registered user access. Exactly one of `grantee_email` or `grantee_customer_id` is required. Roles: `viewer`, `editor`, `publisher`. Returns `{ permission_id, fork_id, grantee_type, grantee_id, role, granted_by, expires_at, created_at }`.
311
+ - `POST /api/v1/compositions/:forkId/share-links { role, expires_at? }` — create a token URL. Anyone with the token gets the role. Returns `{ token, share_url, role, expires_at, ... }`.
312
+ - `GET /api/v1/compositions/shared/:token` — access via share link
313
+ - `DELETE /api/v1/compositions/:forkId/permissions/:permissionId` — revoke a grant
314
+ - `DELETE /api/v1/compositions/:forkId/share-links/:token` — revoke a link
315
+
316
+ ## Cloning a fork
317
+
318
+ Directors can clone another fork (their own or a shared one) as a new starting point:
319
+
320
+ ```
321
+ POST /api/v1/compositions/:forkId/clone
322
+ Content-Type: application/json
323
+
324
+ { "parent_version": "<N or omit for latest>", "title": "optional" }
325
+ ```
326
+
327
+ The `:forkId` in the path is the fork being cloned. The new fork inherits the parent's HTML/JSON at the specified version and creates a version-1 snapshot with `reason: "clone"`.
328
+
329
+ ## Delete a fork
330
+
331
+ ```
332
+ DELETE /api/v1/compositions/:forkId
333
+ ```
334
+
335
+ Soft-delete only. The fork's `deletedAt` is set. Versions and share links are preserved for audit.
336
+
337
+ ## Cost spectrum (free → $10+/video) — default to saving the director money
338
+
339
+ There is no single price for a video. The **approach** the director picks sets the cost, across a wide spectrum. Always steer toward the cheapest approach that still meets their goal, and make the tradeoff explicit rather than silently choosing an expensive path.
340
+
341
+ | Approach | Typical cost | How |
342
+ |---|---|---|
343
+ | **Reuse + render locally** | **free** | Fork an already-decomposed template, swap captions / images / video with existing MP4s (from **My Files**, the director's **local computer** — reference files straight off disk with `place --src ./file`, no upload — or a **web search**), and render **locally** via `vidfarm serve` (native in-process HyperFrames render — free and unguarded, no cloud). |
344
+ | **Reuse + cloud render** | **~$0.001 – $0.03** | Same reuse, but render on the cloud renderer (`POST /compositions/:forkId/render`, ~$0.01–$0.10 depending on length/res). Cheap **image** generation/edits fit in this band too. |
345
+ | **AI-generate some scenes** | **~$1** | Replace a few scenes with AI-generated video clips for high specificity/customization (see the "Generate AI media" section). |
346
+ | **Heavy AI generation** | **$10+** | Many/long AI video clips, custom characters, fully bespoke scenes. |
347
+
348
+ Rules of thumb:
349
+
350
+ - **Reusing existing footage — or footage the director films themselves — is the cheapest.** AI-generating characters/scenes is the most expensive. Editing captions/images on a reused template is nearly free.
351
+ - **Local render is free; cloud render costs pennies.** If the director is iterating with a coding agent on a `vidfarm serve` box, they can render locally at no charge until they want the durable cloud MP4.
352
+ - **Decompose is a one-time ~$0.10** (smart decompose ≈ provider passthrough + GhostCut ~$0.10/30s) per **new** source video — but the director can **skip it entirely** by forking a template that's **already decomposed** in the catalog.
353
+ - **Image generation is cheap** — use it freely, no need to ask permission.
354
+ - **AI video generation is expensive — ask the director's permission before using it.** Default to reuse/local/image approaches unless they've okayed video gen or told you the budget covers it.
355
+ - **Ask about budget.** During Getting Started (and whenever it's relevant per editor project, or when the director asks about cost), ask roughly what they want to spend per video, and pick the approach band that fits. If they haven't said, assume the cheapest approach that works.
356
+
357
+ The director always chooses the method that works for them — the point is to surface the tradeoff and default to thrift, not to force the cheapest path.
358
+
359
+ **How the approach is painted — the three paintbrushes.** The cost of a video is set by *which brush* recreates each scene: (1) **raw clips** remixed from existing footage, (2) **HTML/JS hyperframes** (animated text/images/graphics), (3) **pure AI generation** (image/video/voice/music, most expensive). A technical replication decomposition names, per beat, when/where/what each brush should be, and offers two harnesses — **(A) cheap & efficient** (recaption, bg+fg video memes, hyperframe animation, reuse/greenscreen the asset library, AI only if needed) and **(B) best quality** (AI video by default, AI-image storyboarding first, adversarial grading with a coding agent). Default to (A); reserve (B) for premium/budgeted work. Full methodology in `references/editor-workflows.md` (“The three paintbrushes & two replication harnesses”).
360
+
361
+ ## Billing
362
+
363
+ Vidfarm charges directly in USD from the caller's wallet. There are no credits.
364
+
365
+ - **Wallet top-up** — Stripe checkout via the Settings UI, backed by `POST /settings/wallet/funding-link` (browser session; returns `{ checkout_url, client_reference_id }`)
366
+ - **Balance & history** — shown on the Settings page; billing events via `GET /u/:customerId/settings/wallet/events` (browser session). There is no `/api/v1/user/me/wallet` REST endpoint.
367
+ - **Cost anchors** (see the Cost spectrum section above for how these combine per approach):
368
+ - Local render on a `vidfarm serve` box (in-process HyperFrames): **free**
369
+ - Cloud render (HyperFrames Lambda fan-out): typically $0.01 – $0.10 per MP4 depending on length/resolution
370
+ - Image generation / edit: cheap (provider passthrough) — use freely
371
+ - AI **video** generation: expensive ($1–$10+/video territory) — ask permission first
372
+ - Auto-decompose smart mode: pass-through of caller's AI provider spend (~$0.10 one-time with GhostCut), or skip it by forking an already-decomposed template
373
+ - Auto-decompose time-slice: free
374
+ - GhostCut subtitle removal: ~$0.10 per 30 seconds of source video
375
+
376
+ Framing:
377
+
378
+ - Vidfarm is optimized for cost efficiency, not markup
379
+ - Total director cost per video is typically far below alternatives
380
+ - Platform retains a small safety buffer for operational correctness
151
381
 
152
382
  ## Agentic editing: the three axes (SWAP ↔ REPLACE)
153
383
 
@@ -162,6 +392,39 @@ Name the plan back in these terms ("I'll SWAP the captions and REPLACE the scene
162
392
 
163
393
  **Audio is natively multi-track — overlay narration + music + SFX, each at its own volume.** A composition mixes UNLIMITED simultaneous `<audio>` layers; each sits on its own `data-track-index` and carries its own `data-volume` (0–2, default 1), and the runtime mixes them with per-track volume honored identically in the preview and the exported MP4 (a real ffmpeg `amix` of every audio layer at render). So you never need a pre-mixed file — lay **narration/voiceover at ~1.0 on one track, a music bed at ~0.1–0.2 on a separate track, and SFX on their own tracks**, each via `add_layer kind=audio` (web) / `vidfarm place --kind audio --volume …` (devcli), tuning levels later with the Inspector's Volume slider or `set_layer_media` (`volume`, `muted`). **The key move when recreating a template whose original baked music + narration into ONE audio track: rebuild it as TWO independent tracks** — a fresh narration track (`/audio/speech`, or same-voice reword via `/audio/regenerate-speech` / `vidfarm speech regenerate`) at ~1.0 and a separate real music track at ~0.1–0.2 — then mute/remove the original combined source-audio layer. This gives the director independent voice and music volume, and works around AI TTS being unable to emit narration+music in one file: you compose the mix on the timeline. Honesty: you can't un-mix / stem-separate the original's baked audio — the two tracks are a fresh narration track **plus** a real music file (owned / user-provided / `browse_files` / generated with `/api/v1/primitives/music/generate` (`vidfarm music`)), never a faked or duplicated voice layer.
164
394
 
395
+ ## The three paintbrushes & two replication harnesses
396
+
397
+ Vidfarm is deliberately founder-friendly and thrift-first: **we do not spend expensive AI credits on every scene.** The three axes above tell you *what* is being re-worked; the **three paintbrushes** tell you *how* to paint each scene once you've decided to REPLACE it. A **technical replication decomposition** is exactly this — for the target template, name **when / where / what** each beat should be painted with.
398
+
399
+ > **This decomposition is now a materialized artifact.** Every decomposed fork carries a **replication harness** (`replication-harness.json` / `editor_context.replication_harness` / `GET .../replication-harness.json`) that already contains BOTH plans as concrete per-beat brush assignments plus reusable-asset guidance and viral-DNA guardrails. When it's present, **execute its beats** instead of re-deriving them; the methodology below is how it was built and the fallback when it's absent (older forks / free tier). See the route section further down.
400
+
401
+ 1. **Raw clips** — cut + remix existing long-form / short-form footage. The workhorse. Source in cost order: the director's own `/raws` + `/files` library → HUNT new raws out of a URL/VOD (`vidfarm raws scan`) → only then generate. Combining a **background video + a foreground video** (greenscreen / picture-in-picture) covers a huge share of "video meme" formats with zero generation.
402
+ 2. **HTML/JS hyperframes** — video-from-HTML. Animate text, image elements, logos, stickers, charts and lower-thirds with CSS/declarative motion (web editor) or JS adapters (anime.js/GSAP/Lottie/Three, devcli only). Deterministic, cheap, re-themeable — the right brush for titles, kinetic captions, data beats, brand cards, and any "graphic" scene.
403
+ 3. **Pure AI generation** — AI image / video / voice / music. The most expensive brush (AI **video** especially, $1–$10+); the last resort for a beat no clip or hyperframe can cover.
404
+
405
+ **Reusable asset library.** Directors accumulate logos, stickers, reactions, b-roll, a-roll, and a brand media kit. The decomposition should have an **opinion on when and where to reuse these** — reactions and stickers for beats, the media kit for brand identity, b-roll to cover cuts. But usage can quietly break the format: reskinning past a load-bearing beat kills what made it go viral. **Anchor every asset/paintbrush call to the harness** (`viral_dna`, `viral_dna.emotional_punch`, `editor-harness.json`, `static_vs_pivot`) — reuse and re-skin the *dressing*, preserve the *DNA*.
406
+
407
+ For any replication, offer the director **two harnesses** and recommend (A) unless they've asked for premium or the budget covers it:
408
+
409
+ **(A) Cheap & efficient** — the default. Reach for, in roughly this order:
410
+ - **Recaption text** — often the whole job is rewriting the caption/title layers (`set_captions` / `set_layer_text`). A two-minute change.
411
+ - **Background + foreground video** memes — composite two clips instead of generating one.
412
+ - **Animate HTML + image elements** with hyperframes (Ken Burns on a still, kinetic type, animated logo/sticker) instead of AI motion.
413
+ - **Reuse media elements** from the library; **AI-generate a reusable element once** (a sticker, a logo treatment, a character sprite) then reuse it across scenes rather than regenerating per-scene.
414
+ - **Greenscreen** — chroma-key a subject onto new backgrounds (`image_remove_background_greenscreen` / `media_overlay`).
415
+ - **Raw-clip long-form and remix** — hunt + rearrange existing footage.
416
+ - Lean on the **library of memes / reactions / b-roll / a-roll** and the **brand media kit**.
417
+ - **Only if genuinely needed**, use AI image → and, last, AI video / voice / music.
418
+
419
+ **(B) Best quality** — when the director wants premium and budget allows:
420
+ - **AI video generation by default** for hero scenes.
421
+ - **Storyboard with AI image first** (cheap stills to lock composition/subject), then generate motion from those references for consistency.
422
+ - **Adversarially grade** the output with a coding agent (Claude Code / Codex / any capable AI agent) — render, critique against the harness, iterate — rather than shipping the first generation.
423
+
424
+ **Be generous to free-tier / no-account directors.** Vidfarm's replication harness is open source, and an everyday capable AI agent should be able to follow this decomposition and rebuild a template **without a Vidfarm wallet** — clipping, hyperframes, and local render (`vidfarm serve`) all run on free compute. Keep the recommendation self-contained and pragmatic; don't gate the cheap path behind paid primitives.
425
+
426
+ **Free tier does its OWN decomposition.** The catch on the free tier (local devcli, no account): nobody has pre-decomposed the reference video for them. There is no `video-context.json`, `editor-harness.json`, `replication-harness.json`, or `scene-annotations.json` to `vidfarm pull` — **the user and their agent must watch the video, break it into scenes/audio/text, read the viral DNA, and choose the three paintbrushes by hand.** Coach them through that; the harness above is the method to do it. **Paid Vidfarm accounts skip the manual pass**: they get a large library of **pre-decomposed viral templates** (the decompose passes already run) plus **prompt-harness best practices distilled from platform-scale learnings** — so a fork arrives already carrying its DNA, editor harness, **replication harness (both cheap + best-quality plans, per-beat brush assignments)**, and per-scene recreation annotations. It's fair to surface that upgrade when a free-tier user is decomposing by hand.
427
+
165
428
  ## Edit in the Trackpad Editor
166
429
 
167
430
  The editor is a full timeline surface:
@@ -336,58 +599,32 @@ Use it whenever you need to know what the video says or shows: writing/translati
336
599
  - **Editor chat (frontend AI)** already receives the harness inline in `editor_context.editor_harness` (no tool call needed); it can also fetch this route via `http_request`.
337
600
  - **Desktop agents (Claude Code / Codex)**: `vidfarm pull` writes `editor-harness.json` to the fork dir alongside `video-context.json`, and also materializes a merged agent bundle at `.harness/context.json` plus `.harness/agent-guide.md`. This deliberately does **not** write a top-level `AGENTS.md`, because many user repos already own that file. For local scripting or agentic edits, read `.harness/agent-guide.md` and `.harness/context.json` FIRST; do not freestyle from `composition.html` alone. The `pull` grounding line reports the generated brief paths.
338
601
 
339
- ## Raws (long-form short-form raws)
340
-
341
- Mine a **long-form** video (podcast, stream VOD, webinar, any YouTube/TikTok/IG/X URL, or an upload) into a library of tagged, searchable **raws**. This is the `/raws` surface — browse it at `https://vidfarm.cc/library/raws` (the Library page's "Approved / Raws" tabs).
602
+ ### `GET /api/v1/compositions/:forkId/replication-harness.json` the technical replication analysis
342
603
 
343
- **Start a hunt** `POST /raws/scan` (async: returns `202 { scan_id }` immediately):
604
+ Read-only, non-billing. The **BUILD** companion to the editor harness (which is STYLE): the same "three paintbrushes & two replication harnesses" methodology above, but materialized per-beat for THIS template. Where `editor-harness.json` says *how to edit to feel right*, this says *which tool to reach for* — so an agent (or a free-tier user) rebuilds thrift-first instead of AI-generating every scene.
344
605
 
345
- ```jsonc
606
+ ```
346
607
  {
347
- // ONE source (pick one):
348
- "source_url": "https://youtube.com/watch?v=…", // downloaded into your temp folder first
349
- "temp_file_id": "…", // a video you uploaded to temporary-files
350
- "attachment_id": "…", // a video already in My Files
351
- "s3_key": "" , // an already-staged object
352
-
353
- // What to hunt for free text; inline hints are parsed too
354
- "prompt": "people holding food up to their face, no text on screen",
355
-
356
- // Hunt controls (all optional; also expressible inline in the prompt):
357
- "ranges": ["12:30-15:45", "20:00-22:10"], // ONLY hunt these source windows big cost saver on long videos
358
- "target_duration_sec": 30, // SOFT length band, not a hard cut: 10→5-20s, 30→20-40s, 60→40-80s, 120→80-160s
359
- "aspect": "9:16", // crop every raw: 9:16 | 16:9 | 4:3 | 1:1 (synonyms: vertical/portrait, horizontal/landscape, square)
360
- "crop_focus": "center", // center | top | bottom | left | right (top biases toward faces)
361
- "avoid_text": true, // prefer scenes WITHOUT burned-in captions/on-screen text — a scene-SELECTION filter, NEVER GhostCut on the source
362
- "tracer": "my-campaign" // rolls up all the hunt's billing/observability events
608
+ "ok": true,
609
+ "status": "ready" | "empty" | "none",
610
+ "harness": {
611
+ "summary": "Recaption + reuse: 90% of this is raw clips + hyperframes captions; AI only for the impossible reveal shot.",
612
+ "recommended_strategy": "cheap_efficient",
613
+ "paintbrush_rationale": "Talking-head + text overlays — footage is huntable and every graphic is HTML; AI video buys almost nothing here.",
614
+ "motion_style": { "animation_feel": "punchy kinetic type, hard cuts, sticker pop-ins", "graphic_elements": ["count-up counter", "arrow pointer", "sticker reactions"], "hyperframes_candidates": ["all captions", "the stat counter", "the end-card logo lockup"], "typography_motion": "per-word pop, active word highlighted" },
615
+ "asset_opportunities": ["reuse the brand-kit logo lockup on the end card", "a 'mind-blown' sticker for the reveal beat"],
616
+ "viral_dna_guardrails": ["never cover the reveal frame with a sticker", "keep the trending sound; land the beat drop on the reveal cut"],
617
+ "free_tier_note": "ffmpeg to clip the source, an HTML→video renderer (hyperframes) for every caption/graphic, chroma-key for greenscreen, a local TTS for narration — no paid primitive needed.",
618
+ "cheap": { "strategy": "cheap_efficient", "one_liner": "...", "method_mix": "80% raw_clip + hyperframes; ai_gen only the reveal", "brush_breakdown": { "raw_clip": "...", "hyperframes": "...", "reusable_asset": "...", "ai_gen": "..." },
619
+ "scenes": [{ "role": "hook", "timestamp": "0:00-0:03", "method": "raw_clip", "fallback_method": "hyperframes", "technique": "recaption over a hunted talking-head clip", "instructions": "...", "assets_needed": ["hook clip from /raws"], "viral_dna_guard": "keep the on-cam energy + first 3 words", "est_credits": "low" }],
620
+ "reusable_assets": [{ "asset_type": "sticker", "where": "reveal beat", "source": "user_library", "purpose": "punch up the reveal", "reuse_note": "reuse across the series", "viral_dna_risk": "do NOT let it cover the reveal frame" }],
621
+ "pipeline": ["hunt hook+payoff raws", "recaption in hyperframes", "reuse logo end-card", "AI-gen only the reveal"], "ai_spend": "one image→video for the reveal only", "tradeoffs": "...", "do": ["..."], "dont": ["..."] },
622
+ "quality": { "strategy": "best_quality", "one_liner": "AI-gen hero scenes, storyboard-first", ... }
623
+ }
363
624
  }
364
625
  ```
365
626
 
366
- - **Poll** `GET /raws/scan/:scanId` until the source `status` is `complete` (or `failed` — crashed pipelines are reconciled to `failed`, pollers never spin).
367
- - **Results**: `GET /raws/feed?source=<source_video_id>` (or the whole library), hybrid search via `POST /raws/search` `{ query: "someone looks confused" }`, per-raw download at `GET /raws/:clipId/download`. Raws carry taxonomy tags, a description, a transcript, an `aspect` field when cropped, the hunt's `tracer`, and (with a gemini/openai key) a semantic embedding.
368
- - **Billing** — **AWS compute only** (`clip_scan_lambda` GB-seconds + Step Functions transitions, a fraction of a cent for typical hunts; the 202 response includes a `compute_estimate`). The AI tagging/refine runs on **your saved provider key (BYOK)** and is never wallet-billed. Submission is wallet-gated (402 when empty).
369
- - **The original stays temporary** — URL-ingested and uploaded sources live ONLY in your temp folder (`users/…/temporary/clip-sources/…`) with a hard **30-day TTL** (auto-deleted from S3 + the temp-file list). The hunted raws themselves are durable.
370
- - **Captions rule** — "no captions / no on-screen text" hunts are handled by scene SELECTION (the refine pass drops text-heavy scenes). **Never run GhostCut caption removal on a long-form source** (it is hard-capped at ~15 minutes); to actually erase burned-in text, apply `POST /api/v1/primitives/videos/remove-captions` to individual FINISHED raws afterwards.
371
- - **Clip an EXACT subrange (no AI hunt)** — when you want one precise hand-picked in/out rather than an open-ended hunt, use the **Clipper**: the web page `https://vidfarm.cc/tools/clipper` (previews the source straight from its URL, HH:MM:SS:NNNN in/out, live length estimate, multi-clip session grouped by tracer), the endpoint `POST /raws/clip-range` `{ source_url|preview_url|temp_file_id, start_sec, end_sec, tracer?, folder_path?, name? }` (trims exactly that window into one raw — compute-only, no AI/key), or the devcli twin `vidfarm clipper <video-url|file> --start <t> --end <t> [--tracer NAME --folder NAME --name TXT]`. The Library "Import source" button opens the Clipper; each save lands in `/raws/<folder>` (defaults to the tracer). Reach for `/raws/scan` for multi-clip hunts, `/raws/clip-range` for surgical single cuts. (The masked-image and time-scoped-video editors live alongside it at `/tools/image` and `/tools/video`.)
372
-
373
- **devcli (local-first — this is the default way to hunt on your own machine):**
374
-
375
- ```bash
376
- # Local machine power: local ffmpeg + your local claude/codex CLI subscription (no API key needed)
377
- vidfarm raws scan ./podcast.mp4 --range "12:30-15:45" --duration 30 --aspect vertical --no-text \
378
- --prompt "guest reaction moments"
379
-
380
- # Provider keys are the fallback (--provider gemini|openai|openrouter); the cloud pipeline is the
381
- # EXPLICIT backup, never the default:
382
- vidfarm raws scan --cloud ./podcast.mp4 --duration 30 --aspect 9:16 --tracer my-campaign
383
- vidfarm raws scan --cloud --url "https://youtube.com/watch?v=…" --range "0:00-10:00"
384
-
385
- # Then search/reuse the library
386
- vidfarm raws search "confused reaction after reading a message"
387
- vidfarm raws export <raw-ids…> --to ./picks
388
- ```
389
-
390
- Local scans persist to a SQLite library under `~/.vidfarm` (free compute, subscription-powered evaluation); `--cloud` uploads to your temp folder (30-day TTL) or passes `--url`, runs the deployed pipeline, and bills AWS compute only.
627
+ **Use it:** when the user wants this cheap / "without burning credits" / in bulk, execute `harness.cheap.scenes[]` beat by beat — `raw_clip` → hunt via `/raws/scan` + `set_layer_media`; `hyperframes` → build the text/graphic as a CSS/anime.js layer (NEVER AI video for on-screen text); `reusable_asset` pull from `/files` or generate ONCE and reuse; `ai_gen` → only where the beat says so. When they want premium, follow `harness.quality`. **Always** honor each scene's `viral_dna_guard` and the top-level `viral_dna_guardrails` that is the discipline that stops an eager sticker/logo/footage-swap from flattening the template. `replication_harness` is delivered inline to the editor chat and pulled to disk as `replication-harness.json` (rendered into `.harness/agent-guide.md`) for desktop agents. `status:"none"` → run `POST /auto-decompose` first.
391
628
 
392
629
  ## Generate AI media and drop it on the timeline
393
630
 
@@ -503,166 +740,141 @@ Beyond the Ken Burns / transition / animated-caption presets, the copilot can ha
503
740
  --src ~/footage/hook-042.mp4 --at 0
504
741
  vidfarm render <forkId> # local, $0.00
505
742
  ```
506
- If you ran `serve --port <N>`, pass `--base-url http://localhost:<N>` so the reference URL points at the right origin. A localhost `/storage` URL only resolves against the serve box that holds the file, so these compositions render/preview **locally**; to render in the cloud or share them, upload the media instead (below).
507
- - **Anywhere else (e.g. a `vidfarm pull` dir headed for cloud render/publish)** — the cloud renderer can't reach your disk, so a local `--src` file is **uploaded to the ephemeral temp store** first, namescoped under a throwaway `temp/` folder by default (override with `--folder`). Use this when the output must be a durable, shareable URL.
508
-
509
- **`/temp` folder convention.** When you *do* need to upload throwaway media to Vidfarm (cloud render, `approve`, one-off URLs), keep it under a dedicated `temp/` folder — `vidfarm upload clip.mp4 --folder temp`, `vidfarm place … --folder temp`, `vidfarm approve … --folder temp` — so scratch assets stay quarantined in one place you can periodically purge, instead of cluttering your persistent My Files library or the temp-store root.
510
-
511
- ## Render (publish to MP4)
512
-
513
- Rendering publishes the fork's current working state to an MP4 using HyperFrames (cloud Lambda fan-out on the deployed host, or the free in-process renderer on a local `vidfarm serve` box). In the Trackpad Editor this is the **Render** button. The REST route is `/render`; the older `/export` path is kept as a deprecated alias for already-published devcli clients (treat "Render", "publish", `/render`, and `/export` as the same operation). The devcli exposes it as `vidfarm render <forkId> [--wait]`; scripted local dirs can use `vidfarm render <forkId> --dir ./work --wait`.
514
-
515
- ```
516
- POST /api/v1/compositions/:forkId/render
517
- Content-Type: application/json
518
-
519
- { "title": "optional", "version": null, "tracer": "optional-trace-id", "html": "optional HTML to save to working state before rendering" }
520
- ```
521
-
522
- Returns 202 with `{ ok, renderId, status, progress, expectedOutputPublicUrl, outputUrl, cost, title, version, ... }` where `expectedOutputPublicUrl` is the deterministic public MP4 URL and `version` is the snapshot version created for this publish. Failure modes: `402` if the fork owner is not on a paid plan, `409` while GhostCut subtitle removal is still `pending` (retry after `/remove-video-captions-poll` reports done/failed), `412` if the provider-key preflight fails. Poll:
523
-
524
- ```
525
- GET /api/v1/compositions/:forkId/renders/:renderId
526
- ```
527
-
528
- Response includes `{ ok, renderId, status: "RUNNING" | "SUCCEEDED" | "FAILED", progress, framesRendered, totalFrames, cost, expectedOutputPublicUrl, outputUrl, outputS3Uri, errors }` (`progress` is 0..1). On success, `expectedOutputPublicUrl` is the durable public MP4 URL and `outputUrl` remains the completion-time field.
529
-
530
- Every publish creates an immutable version snapshot at `versions/<N>/composition.html` and `versions/<N>/composition.json`.
531
-
532
- The Web UI **Render** button and devcli render both use this same endpoint. The fast `202` response includes the deterministic `expectedOutputPublicUrl` so a caller can store or pass along the final public S3 URL before the render has completed, then poll by `renderId` until `status` settles.
533
-
534
- ## Approve a finished post
535
-
536
- A render produces a bare MP4 URL. **Approving** wraps that MP4 (plus caption, title, pinned comment, and any carousel slides) into a shareable preview page — the phone-mockup page a human opens to review and copy the post.
537
-
538
- ```
539
- POST /api/v1/approved/posts
540
- Content-Type: application/json
541
-
542
- { "caption": "required", "title": "optional", "pinned_comment": "optional", "tracer": "optional",
543
- "media": [ { "url": "https://.../output.mp4", "kind": "video", "role": "primary" } ] }
544
- ```
545
-
546
- `caption` is required. `media[]` items take `{ url, kind?: "image"|"video"|..., role?: "primary"|"slide"|... }`. Response (`201`) is the approved post including **`share_url`** — the prod frontend page for previewing/sharing. Surface that URL to the user; it is the headline output of this step.
547
-
548
- - `GET /api/v1/approved/posts` — list your approved posts
549
- - `GET /api/v1/approved/posts/:postId` — read one (returns `share_url`, `download_zip_url`)
550
-
551
- devcli: `vidfarm approve --video <mp4-url> --caption "..."` prints the `share_url` as a first-class openable link; `vidfarm posts` lists, `vidfarm post <id>` reads one.
552
-
553
- ## Schedule a post
554
-
555
- Schedule an approved post to a connected destination channel (FlockPoster social account or email) at one ISO timestamp:
556
-
557
- ```
558
- POST /api/v1/approved/posts/:postId/schedules
559
- Content-Type: application/json
560
-
561
- { "destination_type": "flockposter" | "email", "destination_id": "<channel or email>",
562
- "scheduled_at": "2026-07-10T14:00:00Z", "timezone": "America/New_York", "additional_notes": "optional" }
563
- ```
743
+ If you ran `serve --port <N>`, pass `--base-url http://localhost:<N>` so the reference URL points at the right origin. A localhost `/storage` URL only resolves against the serve box that holds the file, so these compositions render/preview **locally**; to render in the cloud or share them, upload the media instead (below).
744
+ - **Anywhere else (e.g. a `vidfarm pull` dir headed for cloud render/publish)** — the cloud renderer can't reach your disk, so a local `--src` file is **uploaded to the ephemeral temp store** first, namescoped under a throwaway `temp/` folder by default (override with `--folder`). Use this when the output must be a durable, shareable URL.
564
745
 
565
- Minimum 10-minute lead time. Response (`201`) is the schedule record. Browse existing schedules with `GET /api/v1/approved/posts/:postId/schedules`.
746
+ **`/temp` folder convention.** When you *do* need to upload throwaway media to Vidfarm (cloud render, `approve`, one-off URLs), keep it under a dedicated `temp/` folder — `vidfarm upload clip.mp4 --folder temp`, `vidfarm place … --folder temp`, `vidfarm approve … --folder temp` — so scratch assets stay quarantined in one place you can periodically purge, instead of cluttering your persistent My Files library or the temp-store root.
566
747
 
567
- devcli: `vidfarm schedule <postId> --at <iso> --to <destinationId> [--type flockposter|email]`, and `vidfarm schedules <postId>` to browse.
748
+ ## Raws (long-form short-form raws)
568
749
 
569
- ## Version history
750
+ Mine a **long-form** video (podcast, stream VOD, webinar, any YouTube/TikTok/IG/X URL, or an upload) into a library of tagged, searchable **raws**. This is the `/raws` surface — browse it at `https://vidfarm.cc/library/raws` (the Library page's "Approved / Raws" tabs).
570
751
 
571
- ```
572
- GET /api/v1/compositions/:forkId/versions?limit=100
573
- ```
752
+ **Start a hunt** — `POST /raws/scan` (async: returns `202 { scan_id }` immediately):
574
753
 
575
- Returns `{ versions: [{ version, reason, message, created_by, created_at, render_job_id, composition_url, composition_data_url }], next_cursor }`. Reason is `publish`, `clone`, `manual`, or `migration`. To snapshot the working state manually, `POST /api/v1/compositions/:forkId/versions { "message": "optional" }`.
754
+ ```jsonc
755
+ {
756
+ // ONE source (pick one):
757
+ "source_url": "https://youtube.com/watch?v=…", // downloaded into your temp folder first
758
+ "temp_file_id": "…", // a video you uploaded to temporary-files
759
+ "attachment_id": "…", // a video already in My Files
760
+ "s3_key": "…" , // an already-staged object
576
761
 
577
- To view a past cut without reverting:
762
+ // What to hunt for free text; inline hints are parsed too
763
+ "prompt": "people holding food up to their face, no text on screen",
578
764
 
579
- ```
580
- GET /api/v1/compositions/:forkId/versions/:version/composition.html
765
+ // Hunt controls (all optional; also expressible inline in the prompt):
766
+ "ranges": ["12:30-15:45", "20:00-22:10"], // ONLY hunt these source windows — big cost saver on long videos
767
+ "target_duration_sec": 30, // SOFT length band, not a hard cut: 10→5-20s, 30→20-40s, 60→40-80s, 120→80-160s
768
+ "aspect": "9:16", // crop every raw: 9:16 | 16:9 | 4:3 | 1:1 (synonyms: vertical/portrait, horizontal/landscape, square)
769
+ "crop_focus": "center", // center | top | bottom | left | right (top biases toward faces)
770
+ "avoid_text": true, // prefer scenes WITHOUT burned-in captions/on-screen text — a scene-SELECTION filter, NEVER GhostCut on the source
771
+ "tracer": "my-campaign" // rolls up all the hunt's billing/observability events
772
+ }
581
773
  ```
582
774
 
583
- To revert the working state to a version, `PUT` the historical HTML/JSON back to the working keys.
775
+ - **Poll** `GET /raws/scan/:scanId` until the source `status` is `complete` (or `failed` crashed pipelines are reconciled to `failed`, pollers never spin).
776
+ - **Results**: `GET /raws/feed?source=<source_video_id>` (or the whole library), hybrid search via `POST /raws/search` `{ query: "someone looks confused" }`, per-raw download at `GET /raws/:clipId/download`. Raws carry taxonomy tags, a description, a transcript, an `aspect` field when cropped, the hunt's `tracer`, and (with a gemini/openai key) a semantic embedding.
777
+ - **Billing** — **AWS compute only** (`clip_scan_lambda` GB-seconds + Step Functions transitions, a fraction of a cent for typical hunts; the 202 response includes a `compute_estimate`). The AI tagging/refine runs on **your saved provider key (BYOK)** and is never wallet-billed. Submission is wallet-gated (402 when empty).
778
+ - **The original stays temporary** — URL-ingested and uploaded sources live ONLY in your temp folder (`users/…/temporary/clip-sources/…`) with a hard **30-day TTL** (auto-deleted from S3 + the temp-file list). The hunted raws themselves are durable.
779
+ - **Captions rule** — "no captions / no on-screen text" hunts are handled by scene SELECTION (the refine pass drops text-heavy scenes). **Never run GhostCut caption removal on a long-form source** (it is hard-capped at ~15 minutes); to actually erase burned-in text, apply `POST /api/v1/primitives/videos/remove-captions` to individual FINISHED raws afterwards.
780
+ - **Clip an EXACT subrange (no AI hunt)** — when you want one precise hand-picked in/out rather than an open-ended hunt, use the **Clipper**: the web page `https://vidfarm.cc/tools/clipper` (previews the source straight from its URL, HH:MM:SS:NNNN in/out, live length estimate, multi-clip session grouped by tracer), the endpoint `POST /raws/clip-range` `{ source_url|preview_url|temp_file_id, start_sec, end_sec, tracer?, folder_path?, name? }` (trims exactly that window into one raw — compute-only, no AI/key), or the devcli twin `vidfarm clipper <video-url|file> --start <t> --end <t> [--tracer NAME --folder NAME --name TXT]`. The devcli path is now **local-first**: local files clip into the local raws store by default, and URL sources are staged locally before trimming unless you pass `--cloud`. If you do **not** have a `VIDFARM_API_KEY`, URL clipping cannot bootstrap the remote source into the local backend; open the long-form raw video in the browser, save/extract the actual video file locally, then run `vidfarm clipper ./source.mp4 ...`. The Library "Import source" button opens the Clipper; each save lands in `/raws/<folder>` (defaults to the tracer). Reach for `/raws/scan` for multi-clip hunts, `/raws/clip-range` for surgical single cuts. (The masked-image and time-scoped-video editors live alongside it at `/tools/image` and `/tools/video`.)
584
781
 
585
- ## Sharing and visibility
782
+ **devcli (local-first this is the default way to hunt on your own machine):**
586
783
 
587
- Every fork has a visibility: `private` (default), `unlisted` (legacy), or `public` (view-only).
784
+ ```bash
785
+ # Local machine power: local ffmpeg + your local claude/codex CLI subscription (no API key needed)
786
+ vidfarm raws scan ./podcast.mp4 --range "12:30-15:45" --duration 30 --aspect vertical --no-text \
787
+ --prompt "guest reaction moments"
588
788
 
589
- ```
590
- PATCH /api/v1/compositions/:forkId/visibility { "visibility": "public" }
789
+ # Provider keys are the fallback (--provider gemini|openai|openrouter); the cloud pipeline is the
790
+ # EXPLICIT backup, never the default:
791
+ vidfarm raws scan --cloud ./podcast.mp4 --duration 30 --aspect 9:16 --tracer my-campaign
792
+ vidfarm raws scan --cloud --url "https://youtube.com/watch?v=…" --range "0:00-10:00"
793
+
794
+ # Then search/reuse the library
795
+ vidfarm raws search "confused reaction after reading a message"
796
+ vidfarm raws export <raw-ids…> --to ./picks
591
797
  ```
592
798
 
593
- Public forks are accessible at `/editor/<templateId>?fork=<forkId>` (view-only for non-owners). Public visibility is the simplest sharing surface.
799
+ Local scans persist to a SQLite library under `~/.vidfarm` (free compute, subscription-powered evaluation); `--cloud` uploads to your temp folder (30-day TTL) or passes `--url`, runs the deployed pipeline, and bills AWS compute only.
594
800
 
595
- Fine-grained sharing uses per-user permissions and share links (all body fields are snake_case):
801
+ ## My Files (the user's asset library)
596
802
 
597
- - `POST /api/v1/compositions/:forkId/permissions { grantee_email | grantee_customer_id, role, expires_at? }` grant a specific registered user access. Exactly one of `grantee_email` or `grantee_customer_id` is required. Roles: `viewer`, `editor`, `publisher`. Returns `{ permission_id, fork_id, grantee_type, grantee_id, role, granted_by, expires_at, created_at }`.
598
- - `POST /api/v1/compositions/:forkId/share-links { role, expires_at? }` — create a token URL. Anyone with the token gets the role. Returns `{ token, share_url, role, expires_at, ... }`.
599
- - `GET /api/v1/compositions/shared/:token` — access via share link
600
- - `DELETE /api/v1/compositions/:forkId/permissions/:permissionId` — revoke a grant
601
- - `DELETE /api/v1/compositions/:forkId/share-links/:token` — revoke a link
803
+ 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 (About.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.
602
804
 
603
- ## Cloning a fork
805
+ - **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>]`.
806
+ - **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.
807
+ - **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 About.md --folder acme` / `vidfarm put-file --content "…" --as About.md`. This is how an agent saves Getting Started context (About.md, Interview.md, 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).
808
+ - **`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").
604
809
 
605
- Directors can clone another fork (their own or a shared one) as a new starting point:
810
+ ### Metadata notes + vector search (find assets by meaning)
606
811
 
607
- ```
608
- POST /api/v1/compositions/:forkId/clone
609
- Content-Type: application/json
812
+ Every My Files entry carries an optional **`notes`** field — free-form metadata describing what the file *is*, who/what it depicts, and when to use it. Notes are **vector-embedded** on save (same BYOK embedding seam as the raws library: gemini → openai key auto-pick; no key fail-softs to keyword-only), so the library is searchable by *meaning*, not just filename:
610
813
 
611
- { "parent_version": "<N or omit for latest>", "title": "optional" }
612
- ```
814
+ - **Annotate** — `vidfarm annotate-file <id|name> --notes "Sprite card for Zara, our mascot: front/side/back views, teal jacket. Use as the reference image whenever generating Zara."` (`PATCH /me/attachments/:id`, body `{ notes }`; empty string clears). Or set notes at write time: `vidfarm put-file zara.png --folder characters/zara --notes ""`. Web copilot: `browse_files action=annotate` (or `notes` on `action=write`).
815
+ - **Search** — `vidfarm files --search "the fox mascot reference sheet"` (`POST /me/attachments/search`, body `{ query, folder_path?, limit? }`) returns ranked hits with `similarity` + `keyword_match`, mirroring `/raws/search`: keyword survivors over name/folder/notes are semantically re-ranked, and when nothing keyword-matches it relaxes to a pure semantic pass. Web copilot: `browse_files action=search`.
816
+ - **Annotate what you'll want back.** Filenames alone don't survive months of accumulation — any asset a future session should find (character refs, logo variants, recurring backgrounds, key briefs) deserves notes at save time. `list`/`read` responses include each file's `notes`, so a scan of a folder doubles as a manifest of what's in it.
613
817
 
614
- The `:forkId` in the path is the fork being cloned. The new fork inherits the parent's HTML/JSON at the specified version and creates a version-1 snapshot with `reason: "clone"`.
818
+ ### Recurring characters are first-class (mascots, spokespeople, avatars)
615
819
 
616
- ## Delete a fork
820
+ Recurring characters that must look the same across videos have a **dedicated, browsable home**: the **`/files/characters/`** folder, one subfolder per character keyed by a URL-safe **slug** (lowercase, hyphenated) — e.g. `/files/characters/zara/`. This is where the editor copilot looks first, and where a director organizes their cast. Each character is a **trio of files** in that folder:
617
821
 
618
- ```
619
- DELETE /api/v1/compositions/:forkId
620
- ```
822
+ 1. **`<character_id>.json`** — the machine-readable **manifest**, the source of truth the agent reads to hold identity. The file is named after the character's **id**, which is `character_` + the slug (slug `zara` → id `character_zara` → file `character_zara.json`). The id already carries the `character_` prefix — don't double it (never `character_character_zara.json`):
823
+ ```json
824
+ {
825
+ "id": "character_zara",
826
+ "slug": "zara",
827
+ "name": "Zara",
828
+ "role": "brand mascot / friendly fox guide",
829
+ "appearance": "anthropomorphic red fox, large amber eyes, cream chest fluff, expressive brows",
830
+ "wardrobe": "teal bomber jacket, white tee, small satchel",
831
+ "palette": ["#E8622C", "#12B8A6", "#FDF6EC"],
832
+ "voice": "warm, upbeat, slightly cheeky; mid-tempo",
833
+ "do": ["keep the teal jacket", "front-lit, soft shadows"],
834
+ "dont": ["never photorealistic human", "no other jacket colors"],
835
+ "sprite_card_path": "/files/characters/zara/character_sprite_card.png",
836
+ "about_path": "/files/characters/zara/character_about.md",
837
+ "created_at": "2026-07-09"
838
+ }
839
+ ```
840
+ 2. **`character_sprite_card.png`** — ONE reference-sheet image showing the character consistently: full body front/side/back plus a face close-up on a neutral background.
841
+ 3. **`character_about.md`** — the prose the manifest summarizes (personality, backstory, do/don'ts), for richer wording when prompting.
621
842
 
622
- Soft-delete only. The fork's `deletedAt` is set. Versions and share links are preserved for audit.
843
+ **Awareness look before you generate.** When a user refers to "our mascot", "the same character", "the fox", or names a character, **first `browse_files list path='/files/characters'`** (or `search`) to see who exists, then read that character's manifest + about. Never re-imagine a saved character from memory — that's how characters drift off-model between videos.
623
844
 
624
- ## Cost spectrum (free $10+/video)default to saving the director money
845
+ **Consistency is then mechanical.** On every generation featuring the character, **pass the sprite card's `viewUrl` as the reference input** `prompt_attachments` for image generation/edit, `input_references` for `videos/generate` and the editor's `generate_layer` — and lift wording from the manifest/`character_about.md` into the prompt.
625
846
 
626
- There is no single price for a video. The **approach** the director picks sets the cost, across a wide spectrum. Always steer toward the cheapest approach that still meets their goal, and make the tradeoff explicit rather than silently choosing an expensive path.
847
+ **Creating a new character (walkthrough).** If no folder exists yet, guide the director through it and persist as you go:
848
+ 1. Agree on a **name** → derive a **slug** (lowercase, hyphens); the folder is `/files/characters/<slug>/`.
849
+ 2. Gather the description conversationally (appearance, wardrobe, signature colors, personality, voice, do/don'ts) — pull from any reference photos they have.
850
+ 3. **Sprite card** — if they don't already have one, offer to generate it: `POST /api/v1/primitives/images/generate` (image gen is cheap — pass their best photos as `prompt_attachments`, ask for a "character reference sheet / sprite card" layout), then persist the finished URL. devcli: `vidfarm download <url> zara.png && vidfarm put-file zara.png --as character_sprite_card.png --folder characters/zara`. Web copilot: `browse_files action=write` with `source_url` + `file_name='character_sprite_card.png'` + `folder_path='characters/zara'`.
851
+ 4. **Write `character_about.md` and `<character_id>.json`** (e.g. `character_zara.json`) into the same folder (`browse_files action=write` with `content`, or `vidfarm put-file --content`).
852
+ 5. **Annotate all three** with notes naming the character so `files --search "our mascot"` finds them from any phrasing.
627
853
 
628
- | Approach | Typical cost | How |
629
- |---|---|---|
630
- | **Reuse + render locally** | **free** | Fork an already-decomposed template, swap captions / images / video with existing MP4s (from **My Files**, the director's **local computer** — reference files straight off disk with `place --src ./file`, no upload — or a **web search**), and render **locally** via `vidfarm serve` (native in-process HyperFrames render — free and unguarded, no cloud). |
631
- | **Reuse + cloud render** | **~$0.001 – $0.03** | Same reuse, but render on the cloud renderer (`POST /compositions/:forkId/render`, ~$0.01–$0.10 depending on length/res). Cheap **image** generation/edits fit in this band too. |
632
- | **AI-generate some scenes** | **~$1** | Replace a few scenes with AI-generated video clips for high specificity/customization (see the "Generate AI media" section). |
633
- | **Heavy AI generation** | **$10+** | Many/long AI video clips, custom characters, fully bespoke scenes. |
854
+ **Renaming / moving / copying.** The whole directory is reorganizable in place, for **paid AND free** users, via three REST routes, their devcli twins, and the explorer's kebab / right-click menu:
634
855
 
635
- Rules of thumb:
856
+ - **rename** — `POST /api/v1/user/me/directory/rename` `{ path, new_name, file_id? }` · `vidfarm directory rename <path> <new-name> [--file-id <id>]`. Renames a `/files`/`/temp` file (display name only — the view URL keeps working) or a folder in any writable root (its files + nested subfolders come along).
857
+ - **move** — `POST /api/v1/user/me/directory/move` `{ path, to, file_id? }` · `vidfarm directory move <path> <to-folder> [--file-id <id>]`. Relocates a FILE into `to` (with `file_id`) or nests a whole FOLDER under `to`. **Same root only** (`/files`·`/temp`·`/raws`·`/approved`); metadata-only, so S3 objects are untouched.
858
+ - **copy** — `POST /api/v1/user/me/directory/copy` `{ path, to?, file_id?, new_name? }` · `vidfarm directory copy <path> [<to-folder>] [--file-id <id>] [--as <name>]`. Duplicates a file/folder sharing the same underlying S3 object (cheap, no re-upload). Same root only, `/files`·`/temp`·`/raws` (not `/approved` — a ready post is a single publishable unit; not `/projects` — read-only). Omit `to` to duplicate in place.
636
859
 
637
- - **Reusing existing footage or footage the director films themselves is the cheapest.** AI-generating characters/scenes is the most expensive. Editing captions/images on a reused template is nearly free.
638
- - **Local render is free; cloud render costs pennies.** If the director is iterating with a coding agent on a `vidfarm serve` box, they can render locally at no charge until they want the durable cloud MP4.
639
- - **Decompose is a one-time ~$0.10** (smart decompose ≈ provider passthrough + GhostCut ~$0.10/30s) per **new** source video — but the director can **skip it entirely** by forking a template that's **already decomposed** in the catalog.
640
- - **Image generation is cheap** — use it freely, no need to ask permission.
641
- - **AI video generation is expensive — ask the director's permission before using it.** Default to reuse/local/image approaches unless they've okayed video gen or told you the budget covers it.
642
- - **Ask about budget.** During Getting Started (and whenever it's relevant per editor project, or when the director asks about cost), ask roughly what they want to spend per video, and pick the approach band that fits. If they haven't said, assume the cheapest approach that works.
860
+ The web copilot exposes the same three as `browse_files action=rename|move|copy`. If you rename or move a character's folder, update the `id`, `sprite_card_path`, and `about_path` inside its manifest to match (and rename the `<character_id>.json` file itself).
643
861
 
644
- The director always chooses the method that works for them the point is to surface the tradeoff and default to thrift, not to force the cheapest path.
862
+ **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.
645
863
 
646
- ## Billing
864
+ 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 (About.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`.
647
865
 
648
- Vidfarm charges directly in USD from the caller's wallet. There are no credits.
866
+ ## Automate a template via REST
649
867
 
650
- - **Wallet top-up** Stripe checkout via the Settings UI, backed by `POST /settings/wallet/funding-link` (browser session; returns `{ checkout_url, client_reference_id }`)
651
- - **Balance & history** — shown on the Settings page; billing events via `GET /u/:customerId/settings/wallet/events` (browser session). There is no `/api/v1/user/me/wallet` REST endpoint.
652
- - **Cost anchors** (see the Cost spectrum section above for how these combine per approach):
653
- - Local render on a `vidfarm serve` box (in-process HyperFrames): **free**
654
- - Cloud render (HyperFrames Lambda fan-out): typically $0.01 – $0.10 per MP4 depending on length/resolution
655
- - Image generation / edit: cheap (provider passthrough) — use freely
656
- - AI **video** generation: expensive ($1–$10+/video territory) — ask permission first
657
- - Auto-decompose smart mode: pass-through of caller's AI provider spend (~$0.10 one-time with GhostCut), or skip it by forking an already-decomposed template
658
- - Auto-decompose time-slice: free
659
- - GhostCut subtitle removal: ~$0.10 per 30 seconds of source video
868
+ Templates now have the same job-backed REST pattern as primitives, so a script can run them repeatably without the editor UI:
660
869
 
661
- Framing:
870
+ ```
871
+ POST /api/v1/templates/:templateId/operations/:operationName
872
+ Content-Type: application/json
662
873
 
663
- - Vidfarm is optimized for cost efficiency, not markup
664
- - Total director cost per video is typically far below alternatives
665
- - Platform retains a small safety buffer for operational correctness
874
+ { "tracer": "my-run", "payload": { ... }, "webhook_url": "https://..." }
875
+ ```
876
+
877
+ The route returns `202` with a `job_id`, and you can poll the job with `GET /api/v1/user/me/jobs/:jobId` or the template-scoped `GET /api/v1/templates/:templateId/jobs/:jobId`. devcli wraps the same flow as `vidfarm template run <templateId> <operationName> --payload-file payload.json --wait`.
666
878
 
667
879
  ## Automation patterns
668
880
 
@@ -750,11 +962,16 @@ If you need many variants, keep the base fork fixed and fan out by cloning that
750
962
  That is the canonical local scripting path because the pull step now packages:
751
963
 
752
964
  - `video-context.json`: transcript, scene descriptions, viral DNA
753
- - `editor-harness.json`: technical editing brief
965
+ - `editor-harness.json`: technical editing brief (STYLE — *how to edit like this*)
966
+ - `replication-harness.json`: technical replication analysis (BUILD — the 3-paintbrush cheap vs best-quality plans, which beats are raw_clip / hyperframes / reusable_asset / ai_gen, reusable-asset guidance, free-tier note)
754
967
  - `scene-annotations.json`: per-scene replacement/recreation DNA
755
- - `.harness/context.json`: merged agent-facing snapshot
968
+ - `.harness/context.json`: merged agent-facing snapshot (includes `replication_harness`)
756
969
  - `.harness/agent-guide.md`: Vidfarm-specific instruction file telling a generic agent how to use the above without conflicting with any repo-owned `AGENTS.md`
757
970
 
971
+ `agent-guide.md` now renders the **three paintbrushes & two replication harnesses** decomposition CONCRETELY from `replication-harness.json` — the recommended plan's per-beat brush assignment (raw clips vs HTML hyperframes vs reusable assets vs pure AI gen; the (A) cheap-&-efficient default vs (B) best-quality plan), the beats to do in HTML not AI video, the reusable-asset opportunities, and the viral-DNA guardrails — so a desktop agent recreates a template thrift-first (clipping and hyperframes on free compute before any paid generation) and can rebuild it with **no Vidfarm wallet**. See `references/editor-workflows.md` for the full methodology.
972
+
973
+ **Free-tier caveat — the decomposition is the user's job.** The `video-context.json` / `editor-harness.json` / `replication-harness.json` / `scene-annotations.json` grounding files only exist for compositions **Vidfarm has already decomposed** (paid accounts, whose forks pull those files down). On the free tier with no account, `vidfarm pull` won't produce them — **the user and their agent must decompose the reference video themselves** (scenes/audio/text, viral DNA, paintbrush choice) using the method in `references/editor-workflows.md`. Paid Vidfarm accounts get the pre-decomposed viral library and scale-learned prompt-harness best practices instead of doing that pass by hand.
974
+
758
975
  If a local AI script rewrites text or scenes without consuming those files first, treat that as a bug in the script/agent flow.
759
976
 
760
977
  | Command | REST route | Flow step |
@@ -765,7 +982,7 @@ If a local AI script rewrites text or scenes without consuming those files first
765
982
  | `vidfarm inspiration-rm <id>` | `DELETE /discover/templates/:id` | remove a private one |
766
983
  | `vidfarm inspiration-decompose <id>` | `POST /api/v1/inspirations/:id/decompose` | AI-decompose an inspiration |
767
984
  | `vidfarm fork <template_id>` | `POST /api/v1/compositions` | fork a template |
768
- | `vidfarm pull <forkId> [--dir <p>]` | `GET .../compositions/:forkId/{composition.html,json,video-context.json,cast.json,scene-annotations.json,editor-harness.json}` | sync a fork to disk + generate `.harness/context.json` and `.harness/agent-guide.md` + print gaps/scene keys + grounding |
985
+ | `vidfarm pull <forkId> [--dir <p>]` | `GET .../compositions/:forkId/{composition.html,json,video-context.json,cast.json,scene-annotations.json,editor-harness.json,replication-harness.json}` | sync a fork to disk + generate `.harness/context.json` and `.harness/agent-guide.md` + print gaps/scene keys + grounding |
769
986
  | `vidfarm generate <image\|video> --prompt "…"` | `POST /api/v1/primitives/{images,videos}/generate` (polls job) | generate AI media → finished URL |
770
987
  | `vidfarm inpaint <image> --mask <png> --prompt "…" [--region "label=…"] [--ref …] [--out <f>]` | `POST /api/v1/primitives/images/inpaint` (polls job) | masked image EDIT — replace ONLY the transparent-mask region, keep everything else (devcli twin of the /inpaint page) |
771
988
  | `vidfarm create-overlay "<subject>" [--key-color #00FF00] [--aspect-ratio 1:1] [--place <dir>] [--out <f>]` | `POST /api/v1/primitives/images/create-overlay` (polls job) | **Vox-style** transparent OVERLAY — AI image on a forced key-color background, chroma-keyed out in one job → ready-to-composite transparent PNG |
@@ -797,6 +1014,8 @@ If a local AI script rewrites text or scenes without consuming those files first
797
1014
  | `vidfarm provider-keys` / `vidfarm add-provider-key <p> <secret>` | `GET`·`POST /api/v1/user/me/provider-keys` | manage AI keys |
798
1015
  | `vidfarm upload <file> [--folder <path>]` | `POST /api/v1/user/me/temporary-files/upload` | upload → durable URL (ephemeral; prefer `--folder temp` for scratch) |
799
1016
  | `vidfarm download <url> [dest]` | (streams any URL to disk) | download media |
1017
+ | `vidfarm download-post <url> [--quality best\|hd\|full_hd]` | `POST /api/v1/primitives/videos/download` + poll | download a social/media post into a durable MP4 or slideshow |
1018
+ | `vidfarm download-post-audio <url>` | `POST /api/v1/primitives/audio/download` + poll | download a social/media post's audio into a durable audio file |
800
1019
  | `vidfarm files [--folder <path>]` | `GET /api/v1/user/me/attachments` | list My Files assets + folders |
801
1020
  | `vidfarm files --search "…" [--folder <path>]` | `POST /api/v1/user/me/attachments/search` | find My Files assets by MEANING (keyword + vector over name/folder/notes) |
802
1021
  | `vidfarm get-file <id> [dest] [--print]` | (resolve id → view_url, then stream/print) | read one My Files asset |
@@ -804,6 +1023,7 @@ If a local AI script rewrites text or scenes without consuming those files first
804
1023
  | `vidfarm annotate-file <id\|name> --notes "…"` | `PATCH /api/v1/user/me/attachments/:id` | set metadata notes on one My Files entry (vector-embedded) |
805
1024
  | `vidfarm raws scan <video> [--range MM:SS-MM:SS] [--duration <s>] [--aspect 9:16] [--no-text] [--prompt "…"]` | (LOCAL: local ffmpeg + local claude/codex agent → `~/.vidfarm` SQLite) | hunt long-form video into short-form raws on this machine |
806
1025
  | `vidfarm raws scan --cloud <video\|--url <url>> [--tracer <id>]` | temp-file presign/PUT/finalize + `POST /raws/scan` + poll | BACKUP: run the hunt on the deployed pipeline (bills AWS compute only) |
1026
+ | `vidfarm clipper <video-url\|file> [--start <t> --end <t>] [--tracer <id> --folder <name> --name <text>]` | (LOCAL-FIRST: stage source into the local backend, then `POST /raws/clip-range`; add `--cloud` for vidfarm.cc) | trim one exact subrange into `/raws`; without `VIDFARM_API_KEY`, URL sources must be saved locally first |
807
1027
  | `vidfarm raws search "…"` / `raws match "…"` / `raws list` / `raws sources` | (local library; NL→criteria via local agent or provider key) | search/reuse the raws library |
808
1028
  | `vidfarm raws preset list\|run\|save` / `raws export <ids…> --to <dir>` | (local library) | saved queries; copy raw MP4s out |
809
1029
  | `vidfarm lint <dir\|composition.html>` | (local static validation) | pre-publish composition check: timing, overlaps, preset names, media src |
@@ -819,65 +1039,6 @@ If a local AI script rewrites text or scenes without consuming those files first
819
1039
 
820
1040
  **Uploads/downloads/My-Files writes live in the devcli** because they are genuine multi-step / streaming flows: `upload` posts the file as multipart to the **ephemeral** temporary-files route and prints the durable URL to drop into a composition or approved post; `put-file` posts to the **persistent** My Files route (`/me/attachments/upload`) so context docs and brand assets live in the user's library (accepts a local file, `--content <text>`, or piped `--stdin` with `--as <name>`); `download` streams any Vidfarm/media URL to disk.
821
1041
 
822
- ## My Files (the user's asset library)
823
-
824
- 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 (About.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.
825
-
826
- - **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>]`.
827
- - **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.
828
- - **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 About.md --folder acme` / `vidfarm put-file --content "…" --as About.md`. This is how an agent saves Getting Started context (About.md, Interview.md, 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).
829
- - **`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").
830
-
831
- ### Metadata notes + vector search (find assets by meaning)
832
-
833
- Every My Files entry carries an optional **`notes`** field — free-form metadata describing what the file *is*, who/what it depicts, and when to use it. Notes are **vector-embedded** on save (same BYOK embedding seam as the raws library: gemini → openai key auto-pick; no key fail-softs to keyword-only), so the library is searchable by *meaning*, not just filename:
834
-
835
- - **Annotate** — `vidfarm annotate-file <id|name> --notes "Sprite card for Zara, our mascot: front/side/back views, teal jacket. Use as the reference image whenever generating Zara."` (`PATCH /me/attachments/:id`, body `{ notes }`; empty string clears). Or set notes at write time: `vidfarm put-file zara.png --folder characters/zara --notes "…"`. Web copilot: `browse_files action=annotate` (or `notes` on `action=write`).
836
- - **Search** — `vidfarm files --search "the fox mascot reference sheet"` (`POST /me/attachments/search`, body `{ query, folder_path?, limit? }`) returns ranked hits with `similarity` + `keyword_match`, mirroring `/raws/search`: keyword survivors over name/folder/notes are semantically re-ranked, and when nothing keyword-matches it relaxes to a pure semantic pass. Web copilot: `browse_files action=search`.
837
- - **Annotate what you'll want back.** Filenames alone don't survive months of accumulation — any asset a future session should find (character refs, logo variants, recurring backgrounds, key briefs) deserves notes at save time. `list`/`read` responses include each file's `notes`, so a scan of a folder doubles as a manifest of what's in it.
838
-
839
- ### Recurring characters are first-class (mascots, spokespeople, avatars)
840
-
841
- Recurring characters that must look the same across videos have a **dedicated, browsable home**: the **`/files/characters/`** folder, one subfolder per character keyed by a URL-safe **slug** (lowercase, hyphenated) — e.g. `/files/characters/zara/`. This is where the editor copilot looks first, and where a director organizes their cast. Each character is a **trio of files** in that folder:
842
-
843
- 1. **`<character_id>.json`** — the machine-readable **manifest**, the source of truth the agent reads to hold identity. The file is named after the character's **id**, which is `character_` + the slug (slug `zara` → id `character_zara` → file `character_zara.json`). The id already carries the `character_` prefix — don't double it (never `character_character_zara.json`):
844
- ```json
845
- {
846
- "id": "character_zara",
847
- "slug": "zara",
848
- "name": "Zara",
849
- "role": "brand mascot / friendly fox guide",
850
- "appearance": "anthropomorphic red fox, large amber eyes, cream chest fluff, expressive brows",
851
- "wardrobe": "teal bomber jacket, white tee, small satchel",
852
- "palette": ["#E8622C", "#12B8A6", "#FDF6EC"],
853
- "voice": "warm, upbeat, slightly cheeky; mid-tempo",
854
- "do": ["keep the teal jacket", "front-lit, soft shadows"],
855
- "dont": ["never photorealistic human", "no other jacket colors"],
856
- "sprite_card_path": "/files/characters/zara/character_sprite_card.png",
857
- "about_path": "/files/characters/zara/character_about.md",
858
- "created_at": "2026-07-09"
859
- }
860
- ```
861
- 2. **`character_sprite_card.png`** — ONE reference-sheet image showing the character consistently: full body front/side/back plus a face close-up on a neutral background.
862
- 3. **`character_about.md`** — the prose the manifest summarizes (personality, backstory, do/don'ts), for richer wording when prompting.
863
-
864
- **Awareness — look before you generate.** When a user refers to "our mascot", "the same character", "the fox", or names a character, **first `browse_files list path='/files/characters'`** (or `search`) to see who exists, then read that character's manifest + about. Never re-imagine a saved character from memory — that's how characters drift off-model between videos.
865
-
866
- **Consistency is then mechanical.** On every generation featuring the character, **pass the sprite card's `viewUrl` as the reference input** — `prompt_attachments` for image generation/edit, `input_references` for `videos/generate` and the editor's `generate_layer` — and lift wording from the manifest/`character_about.md` into the prompt.
867
-
868
- **Creating a new character (walkthrough).** If no folder exists yet, guide the director through it and persist as you go:
869
- 1. Agree on a **name** → derive a **slug** (lowercase, hyphens); the folder is `/files/characters/<slug>/`.
870
- 2. Gather the description conversationally (appearance, wardrobe, signature colors, personality, voice, do/don'ts) — pull from any reference photos they have.
871
- 3. **Sprite card** — if they don't already have one, offer to generate it: `POST /api/v1/primitives/images/generate` (image gen is cheap — pass their best photos as `prompt_attachments`, ask for a "character reference sheet / sprite card" layout), then persist the finished URL. devcli: `vidfarm download <url> zara.png && vidfarm put-file zara.png --as character_sprite_card.png --folder characters/zara`. Web copilot: `browse_files action=write` with `source_url` + `file_name='character_sprite_card.png'` + `folder_path='characters/zara'`.
872
- 4. **Write `character_about.md` and `<character_id>.json`** (e.g. `character_zara.json`) into the same folder (`browse_files action=write` with `content`, or `vidfarm put-file --content`).
873
- 5. **Annotate all three** with notes naming the character so `files --search "our mascot"` finds them from any phrasing.
874
-
875
- **Renaming / fixing up.** Character folders and files are renamable in place — `browse_files action=rename` (web copilot) or `vidfarm directory rename <path> <new-name> [--file-id <id>]` (devcli); the explorer UI has a **Rename** action on every file/folder row. If you rename a character's folder, update the `id`, `sprite_card_path`, and `about_path` inside its manifest to match (and rename the `<character_id>.json` file itself).
876
-
877
- **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.
878
-
879
- 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=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 (About.md, awareness-levels.md, …) or import a media URL (`source_url`) into that folder, annotating anything worth finding again. `action=rename` renames a `/files` or `/temp` file or folder in place (pass `path` + `new_name`, plus `file_id` for a file) — use it 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`.
880
-
881
1042
  ## Local dev loop (`vidfarm serve`)
882
1043
 
883
1044
  Run the **full** editor locally so a coding agent (Claude Code / Codex) edits composition files on disk while a human finishes in the browser — one source of truth, live sync both ways.
@@ -1031,6 +1192,49 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/images/create-overlay" \
1031
1192
  -d '{"tracer": "vox-overlay", "payload": {"prompt": "a friendly cartoon lightbulb mascot, flat vector illustration", "aspect_ratio": "1:1"}}'
1032
1193
  ```
1033
1194
 
1195
+ ## Primitive: videos/download
1196
+
1197
+ Download a supported social/media post URL into a durable Vidfarm-hosted VISUAL asset. This route is intentionally broader than its old name suggests: it returns either a normal MP4 for video posts, or a slideshow payload for photo/carousel posts.
1198
+
1199
+ - `POST /api/v1/primitives/videos/download`
1200
+ - Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "quality"?: "best" | "hd" | "full_hd", "save_manifest"?: true }, "webhook_url"?: "..." }`
1201
+ - Response: standard primitive job. Poll `GET /api/v1/primitives/jobs/:jobId` until `status: "succeeded"`.
1202
+ - Video post: read `primary_file_url` / `video.file_url` / `videoUrl` for the durable MP4.
1203
+ - Photo/carousel post: read `mediaKind: "slideshow"`, ordered `slideImageUrls[]`, optional `slideAudioUrl`, and `primary_file_url` (first slide image).
1204
+ - Billing: RapidAPI pass-through wallet charge (`rapidapi_video_download`) plus the small mirror/extract pass (`video_download_lambda` when MP4 download is involved).
1205
+ - Free-plan / no-spend rule: do **not** use this paid route when the user wants a free path. In the web app, tell them to use the browser to find a downloader instead. Fallback wording is explicit: suggest Googling `"youtube video downloader"` or `"tiktok/twitter/instagram/etc audio/video downloader"`.
1206
+ - devcli wrapper: `vidfarm download-post <url> [--quality best|hd|full_hd]`
1207
+
1208
+ Example:
1209
+
1210
+ ```bash
1211
+ curl -X POST "$VIDFARM_BASE/api/v1/primitives/videos/download" \
1212
+ -H "vidfarm-api-key: $VIDFARM_API_KEY" \
1213
+ -H "content-type: application/json" \
1214
+ -d '{"tracer": "demo-download-post", "payload": {"source_url": "https://www.tiktok.com/@example/video/123"}}'
1215
+ ```
1216
+
1217
+ ## Primitive: audio/download
1218
+
1219
+ Download just the original audio/music/voice track from a supported social/media post URL into a durable Vidfarm-hosted audio file. It reuses the same RapidAPI resolver as `videos/download`, but returns audio instead of a video/slideshow.
1220
+
1221
+ - `POST /api/v1/primitives/audio/download`
1222
+ - Body: `{ "tracer": "...", "payload": { "source_url": "https://...", "save_manifest"?: true }, "webhook_url"?: "..." }`
1223
+ - Response: standard primitive job. Poll `GET /api/v1/primitives/jobs/:jobId` until `status: "succeeded"`, then read `primary_file_url` / `audio.file_url` / `audioUrl`.
1224
+ - Behavior: prefers the provider's direct audio track when present; otherwise resolves the source video and extracts audio server-side.
1225
+ - Billing: same RapidAPI wallet class as `videos/download`.
1226
+ - Free-plan / no-spend rule: same as the visual download route. Use the browser and, if needed, suggest Googling `"youtube audio downloader"` or `"tiktok/twitter/instagram/etc audio/video downloader"` instead of spending wallet credits.
1227
+ - devcli wrapper: `vidfarm download-post-audio <url>`
1228
+
1229
+ Example:
1230
+
1231
+ ```bash
1232
+ curl -X POST "$VIDFARM_BASE/api/v1/primitives/audio/download" \
1233
+ -H "vidfarm-api-key: $VIDFARM_API_KEY" \
1234
+ -H "content-type: application/json" \
1235
+ -d '{"tracer": "demo-download-audio", "payload": {"source_url": "https://www.youtube.com/watch?v=example"}}'
1236
+ ```
1237
+
1034
1238
  ## Primitive: video_remove_captions
1035
1239
 
1036
1240
  Remove burned-in captions/subtitles/on-screen text from any video URL (GhostCut-powered — the same pipeline auto-decompose uses for its caption-free mirror, exposed as a standalone reusable primitive). Result is a durable caption-free MP4 stored at a Vidfarm URL.
@@ -1242,3 +1446,66 @@ curl -X POST "$VIDFARM_BASE/api/v1/primitives/brainstorm/product_placement" \
1242
1446
  }
1243
1447
  }'
1244
1448
  ```
1449
+
1450
+ ## Recipe: Find and Fork a Template
1451
+
1452
+ Use this when the user already knows the offer or video goal and needs the best starting template.
1453
+
1454
+ 1. Read `references/core-workflows.md`.
1455
+ 2. Search the catalog with `GET /discover/feed?q=<offer>&limit=20`.
1456
+ 3. Rank candidates using `promotions`, `keywords`, `summary`, viral DNA fit, and whether the template is already decomposed.
1457
+ 4. Recommend the best 3-6, but bias toward one strong default.
1458
+ 5. Fork the winner with `POST /api/v1/compositions`.
1459
+ 6. Open the fork in the editor or hand the `fork_id` into the next workflow.
1460
+
1461
+ Default heuristics:
1462
+
1463
+ - Prefer already-decomposed templates.
1464
+ - Prefer reuse over a from-scratch primitive build.
1465
+ - If search is empty, broaden terms or decompose the newly ingested inspiration first.
1466
+
1467
+ ## Recipe: Re-theme a Template While Preserving the Viral DNA
1468
+
1469
+ Use this when the user wants to keep the format’s feel but replace the subject matter.
1470
+
1471
+ 1. Read `references/editor-workflows.md`.
1472
+ 2. Fetch `video-context.json` and `editor-harness.json` first.
1473
+ 3. State the plan in the three axes vocabulary: scenes, audio, text; SWAP vs REPLACE for each.
1474
+ 4. Preserve the hook structure, cut rhythm, emotional punch, and important scenes flagged by the harness.
1475
+ 5. Source footage in this order:
1476
+ - My Files or existing raws
1477
+ - a raws hunt from a long-form source
1478
+ - AI image generation
1479
+ - AI video generation only with permission
1480
+ 6. Rebuild captions and narration so timing, cadence, and joke structure survive the subject change.
1481
+ 7. Render, verify, then approve and schedule only after the director is happy.
1482
+
1483
+ Failure mode to avoid: flattening the format by swapping words but losing the timing, sound, or payoff beat.
1484
+
1485
+ ## Recipe: Local Pull, Edit, Render, Approve
1486
+
1487
+ Use this when a coding agent is doing the work locally or the user wants a reproducible filesystem loop.
1488
+
1489
+ 1. Read `references/automation-and-local-dev.md`.
1490
+ 2. Run `vidfarm pull <forkId> --dir ./work`.
1491
+ 3. Read `./work/.harness/agent-guide.md` and `./work/.harness/context.json` before editing.
1492
+ 4. Make deterministic edits to `composition.html` and optionally `composition.json`.
1493
+ 5. Validate with `vidfarm lint` or `vidfarm stills` when useful.
1494
+ 6. Render with `vidfarm render <forkId> --dir ./work --wait`.
1495
+ 7. Approve the finished MP4 with `vidfarm approve --video <url> --caption "..."`.
1496
+
1497
+ Prefer this path for batch work, CI-like edits, or when the user wants free local rendering through `vidfarm serve`.
1498
+
1499
+ ## Recipe: Onboard a New Director
1500
+
1501
+ Use this only when the director signals they do not know where to start.
1502
+
1503
+ 1. Read `references/onboarding.md`.
1504
+ 2. Capture product context and save durable notes into the correct My Files folder.
1505
+ 3. Determine awareness stages, persuasive angles, and hooks with the brainstorm primitives.
1506
+ 4. Ask about brand assets, demos, and recurring characters; organize them in My Files.
1507
+ 5. Ask about budget and map it to the cost spectrum before recommending expensive generation.
1508
+ 6. Search for the best matching templates and fork one strong default.
1509
+ 7. Transition into the ordinary template-editing workflow.
1510
+
1511
+ Do not force onboarding on users who already know what they want.