@shotstack/cli 0.7.1 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/shotstack.js +56 -55
- package/package.json +2 -2
- package/skills/shotstack/SKILL.md +4 -1
- package/skills/shotstack/references/fonts.md +2 -19
- package/skills/shotstack/references/html5.md +1 -1
- package/skills/shotstack/references/onboarding.md +71 -0
- package/skills/shotstack/shared/agent-core.md +21 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shotstack/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Command-line interface for the Shotstack video rendering API.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/shotstack/shotstack-cli",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"test": "bun test"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@shotstack/schemas": "^1.
|
|
30
|
+
"@shotstack/schemas": "^1.17.0",
|
|
31
31
|
"commander": "^12.1.0",
|
|
32
32
|
"zod": "^4.4.3"
|
|
33
33
|
},
|
|
@@ -13,7 +13,9 @@ license: Apache-2.0
|
|
|
13
13
|
|
|
14
14
|
This skill loads in **terminal-based AI agents** (Claude Code, Cursor, Codex CLI, Gemini CLI, etc.). All operations here are shell commands. There is no embedded UI, no iframe, no inline canvas, no MCP tool surface — only `shotstack` invocations from a terminal. To hand off to a human, run `shotstack studio <file>`; this opens the user's default browser to a `shotstack.studio` URL. Tell the user to click Render *in the browser tab*, not in any UI inside the terminal.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
**Open-ended first render? Ask before composing.** If the ask doesn't state what the video is (subject, format or destination) — e.g. "render my first video", "make me a video", "set up Shotstack" — read [`references/onboarding.md`](references/onboarding.md) and ask its discovery questions, then WAIT for answers. Do not write an Edit first. An open-ended ask is not a specification, and a stray JSON file in the project doesn't make the user experienced. Compose without asking only when the user has stated what the video is, or explicitly says to just show anything.
|
|
17
|
+
|
|
18
|
+
Commands for the Shotstack video rendering API. `render` submits an Edit JSON and returns a render ID; `status` polls a render until done. `ingest` uploads your own local files (or fetches remote URLs) and hosts them so they can be referenced from an Edit. `validate` lints an Edit — run it before every render. `studio` opens an edit in the browser editor for a human to preview, tweak, and render. `template list|get|create|update|delete` reads and writes templates saved to the account: `list` shows saved templates (id + name); `get <id>` fetches a saved template's Edit JSON (e.g. one the user designed in Studio) so you can render or remix it; `create <file> --name <name>` saves an edit as a new template; `update <id> <file>` overwrites one; `delete <id>` removes one.
|
|
17
19
|
|
|
18
20
|
**Default loop — no API key required:** compose → `validate` (offline lint) → `studio` (browser preview). Only the API-backed commands (`render`, `status`, `ingest`) need a key. Reach for `render` when you specifically need a final MP4 exported in the cloud; reach for `studio` for everything else — it is the cheapest way to see an edit and the right default for any creative a human will review.
|
|
19
21
|
|
|
@@ -151,6 +153,7 @@ Authoritative sources, in order of preference:
|
|
|
151
153
|
|
|
152
154
|
This skill ships sub-references for the gnarly bits:
|
|
153
155
|
|
|
156
|
+
- [`references/onboarding.md`](references/onboarding.md) — first-render discovery interview for new users: what to ask, and how answers map to a first Edit
|
|
154
157
|
- [`references/ingest.md`](references/ingest.md) — uploading local files (and fetching URLs) so they can be used in an Edit: the upload→render workflow, `--watch`, status values, supported types
|
|
155
158
|
- [`references/timeline.md`](references/timeline.md) — track layering, transitions, background music via audio assets
|
|
156
159
|
- [`references/positioning.md`](references/positioning.md) — coordinate model, `position`/`offset` (fraction of frame, +y up), clip bounding box & `fit`, text sizing, transform order
|
|
@@ -34,26 +34,9 @@ Load a font from a public URL via `timeline.fonts[]`, then reference its **file
|
|
|
34
34
|
|
|
35
35
|
The `font.family` value is the URL's filename without extension, and it **must** match the URL's basename exactly. For URL `https://fonts.gstatic.com/s/montserrat/v31/JTUSjIg1_i6t8kCHKm45xW5rygbi49c.ttf`, family = `JTUSjIg1_i6t8kCHKm45xW5rygbi49c`. If `family` and the URL basename diverge, the font silently fails to load.
|
|
36
36
|
|
|
37
|
-
## Verified font catalogue
|
|
37
|
+
## Verified font catalogue
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
| Font (style) | family (use in `font.family`) | url (use in `timeline.fonts[].src`) |
|
|
42
|
-
|---|---|---|
|
|
43
|
-
| Inter (sans, variable) | `UcCo3FwrK3iLTfvlaQc78lA2` | `https://fonts.gstatic.com/s/inter/v20/UcCo3FwrK3iLTfvlaQc78lA2.ttf` |
|
|
44
|
-
| Roboto (sans, variable) | `KFOmCnqEu92Fr1Me5WZLCzYlKw` | `https://fonts.gstatic.com/s/roboto/v50/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf` |
|
|
45
|
-
| Open Sans (sans, variable) | `mem8YaGs126MiZpBA-U1UpcaXcl0Aw` | `https://fonts.gstatic.com/s/opensans/v44/mem8YaGs126MiZpBA-U1UpcaXcl0Aw.ttf` |
|
|
46
|
-
| Montserrat (sans, variable) | `JTUSjIg1_i6t8kCHKm45xW5rygbi49c` | `https://fonts.gstatic.com/s/montserrat/v31/JTUSjIg1_i6t8kCHKm45xW5rygbi49c.ttf` |
|
|
47
|
-
| Poppins (sans) | `pxiEyp8kv8JHgFVrFJDUc1NECPY` | `https://fonts.gstatic.com/s/poppins/v24/pxiEyp8kv8JHgFVrFJDUc1NECPY.ttf` |
|
|
48
|
-
| DM Sans (sans, variable) | `rP2Hp2ywxg089UriOZSCHBeHFl0` | `https://fonts.gstatic.com/s/dmsans/v17/rP2Hp2ywxg089UriOZSCHBeHFl0.ttf` |
|
|
49
|
-
| Nunito (sans, variable) | `XRXV3I6Li01BKof4MuyAbsrVcA` | `https://fonts.gstatic.com/s/nunito/v32/XRXV3I6Li01BKof4MuyAbsrVcA.ttf` |
|
|
50
|
-
| Raleway (sans, variable) | `1Ptug8zYS_SKggPN-CoCTqluHfE` | `https://fonts.gstatic.com/s/raleway/v37/1Ptug8zYS_SKggPN-CoCTqluHfE.ttf` |
|
|
51
|
-
| Oswald (display, variable) | `TK3iWkUHHAIjg75GHjUHte5fKg` | `https://fonts.gstatic.com/s/oswald/v57/TK3iWkUHHAIjg75GHjUHte5fKg.ttf` |
|
|
52
|
-
| Bebas Neue (display) | `JTUSjIg69CK48gW7PXooxW5rygbi49c` | `https://fonts.gstatic.com/s/bebasneue/v16/JTUSjIg69CK48gW7PXooxW5rygbi49c.ttf` |
|
|
53
|
-
| Anton (display) | `1Ptgg87LROyAm0K08i4gS7lu` | `https://fonts.gstatic.com/s/anton/v27/1Ptgg87LROyAm0K08i4gS7lu.ttf` |
|
|
54
|
-
| Playfair Display (serif, variable) | `nuFiD-vYSZviVYUb_rj3ij__anPXPTvSgWE_-xU` | `https://fonts.gstatic.com/s/playfairdisplay/v40/nuFiD-vYSZviVYUb_rj3ij__anPXPTvSgWE_-xU.ttf` |
|
|
55
|
-
|
|
56
|
-
Variable fonts (most of the above) cover the full weight range (100–900) from a single URL — set `font.weight` in the clip. For the non-variable fonts (Poppins, Bebas Neue, Anton) the registered URL is weight 400; the SDK falls back gracefully when you request a bolder weight.
|
|
39
|
+
The verified 12-font catalogue lives in [`shared/agent-core.md`](../shared/agent-core.md) (Fonts section) — the single copy, so it can't drift. Copy entries from there verbatim. Non-variable fonts in that table (Poppins, Bebas Neue, Anton) register at weight 400; the SDK falls back gracefully when you request a bolder weight.
|
|
57
40
|
|
|
58
41
|
### Sourcing more fonts
|
|
59
42
|
|
|
@@ -82,7 +82,7 @@ The iframe renders under a strict Content-Security-Policy (`default-src 'none'`)
|
|
|
82
82
|
```
|
|
83
83
|
2. **Use a system family** (`system-ui`, `Arial`, `Georgia`, …) — resolves in the render browser with no load.
|
|
84
84
|
|
|
85
|
-
An unresolved family silently falls back to the browser default — the render won't fail, but the text won't be your font. For a single styled line, a `rich-text` asset (which *does* use `timeline.fonts[]`; verified catalogue in `
|
|
85
|
+
An unresolved family silently falls back to the browser default — the render won't fail, but the text won't be your font. For a single styled line, a `rich-text` asset (which *does* use `timeline.fonts[]`; verified catalogue in `shared/agent-core.md`) is simpler than a `data:` font embed.
|
|
86
86
|
|
|
87
87
|
## Sizing
|
|
88
88
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# First render: discovery interview
|
|
2
|
+
|
|
3
|
+
**The gate: do not compose an Edit until the user has answered the questions
|
|
4
|
+
below (or explicitly said "just show me anything").** Setting up the CLI and
|
|
5
|
+
key can proceed; writing the video cannot.
|
|
6
|
+
|
|
7
|
+
"Render my first video" is not a specification. It says nothing about the
|
|
8
|
+
subject, the content, the format or where the video will go. Neither does
|
|
9
|
+
"set up Shotstack". If any of those are unstated, ask. A first video is only
|
|
10
|
+
useful when it matches what the user is actually trying to make, and a short
|
|
11
|
+
interview gets there faster than guessing.
|
|
12
|
+
|
|
13
|
+
Skip the interview only when the user has stated what the video is (e.g.
|
|
14
|
+
"make a 9:16 promo from the images in ./assets with the tagline X"), or has
|
|
15
|
+
declined to answer.
|
|
16
|
+
|
|
17
|
+
## The interview
|
|
18
|
+
|
|
19
|
+
Ask conversationally, batched into one or two messages, and wait for the
|
|
20
|
+
reply. Skip any question the project already answers (e.g. a Next.js
|
|
21
|
+
e-commerce app full of product images answers question 2).
|
|
22
|
+
|
|
23
|
+
1. **What should the video do?** Social clips, product or listing videos,
|
|
24
|
+
personalised outreach, in-app content, something else? Listen for the job,
|
|
25
|
+
not the feature.
|
|
26
|
+
2. **What content or data exists?** Images, footage, audio or brand assets in
|
|
27
|
+
the project or at URLs; structured data (CSV, JSON, database, CMS) that
|
|
28
|
+
could drive repeated or personalised videos.
|
|
29
|
+
3. **Where will the videos go?** Destination decides format: 9:16 for
|
|
30
|
+
TikTok/Reels/Shorts, 1:1 or 4:5 for feeds, 16:9 for YouTube/web. When in
|
|
31
|
+
doubt for social, default to 1080x1920 (9:16).
|
|
32
|
+
4. **One video or many?** Both make fine first renders, but the composition
|
|
33
|
+
differs: pipelines should use merge fields from the start.
|
|
34
|
+
|
|
35
|
+
## From answers to a first render
|
|
36
|
+
|
|
37
|
+
- Compose ONE Edit JSON that answers the four questions. Make it something
|
|
38
|
+
worth keeping, not a minimal demo.
|
|
39
|
+
- Use the user's real assets when they exist (host local files first:
|
|
40
|
+
`shotstack ingest upload <file> --watch`, then reference the returned URL).
|
|
41
|
+
If nothing is to hand, build from the Shotstack asset library
|
|
42
|
+
(`references/asset-library.md`) and motion-graphic snippets
|
|
43
|
+
(`references/html5-snippets.md`), matched to their stated use case.
|
|
44
|
+
- Use their brand where it exists: colours, fonts and logos from the project
|
|
45
|
+
or their site rather than an invented look. Follow the house motion
|
|
46
|
+
language (`references/motion.md`).
|
|
47
|
+
- If they chose "many": add merge fields (`{{ placeholder }}`) for the parts
|
|
48
|
+
that vary per video, and show one render with sample values.
|
|
49
|
+
- Loop: compose, then `shotstack validate`, then `shotstack render edit.json
|
|
50
|
+
--env stage --watch`.
|
|
51
|
+
|
|
52
|
+
## Wrapping up
|
|
53
|
+
|
|
54
|
+
- Give two links, always: the rendered MP4 and a Studio link (`shotstack
|
|
55
|
+
studio <file> --no-open`, include the printed shotstack.studio URL). The
|
|
56
|
+
Studio link lets the user tweak the design in the browser without touching
|
|
57
|
+
JSON; say so.
|
|
58
|
+
- Describe what they have in plain terms: a reusable template, free stage
|
|
59
|
+
renders, the same JSON in production via `--env v1`, merge fields for the
|
|
60
|
+
parts that vary. Don't surface validator warnings or internal caveats
|
|
61
|
+
unless something is actually broken.
|
|
62
|
+
- End by offering to take the next step towards what they said in the
|
|
63
|
+
interview, concretely, not a generic list. They wanted a data-driven
|
|
64
|
+
pipeline: offer to wire the template to their data source and render a
|
|
65
|
+
batch now. They mentioned their own footage: offer to upload it and swap it
|
|
66
|
+
in. One sharp offer beats five open-ended suggestions.
|
|
67
|
+
|
|
68
|
+
## Tone
|
|
69
|
+
|
|
70
|
+
Brisk, concrete and curious about the user's project. Batch questions, don't
|
|
71
|
+
drip-feed. One good clarifying question beats three generic ones.
|
|
@@ -266,7 +266,26 @@ Use **custom Google Fonts via `timeline.fonts[]`**. System fonts (`Arial`, `Helv
|
|
|
266
266
|
|
|
267
267
|
**CRITICAL: Do NOT construct or fabricate Google Fonts URLs from memory.** Google rotates them (`v26 → v31 → ...`) and the hash filenames change with each version. Any URL you reconstruct from training data is almost certainly a 404. **Use ONLY the verified entries below, copied verbatim.**
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
### Verified font catalogue (12 fonts)
|
|
270
|
+
|
|
271
|
+
Paste the **url** into `timeline.fonts[].src`, paste the **family** into `asset.font.family`.
|
|
272
|
+
|
|
273
|
+
| Font (style) | family (use in `font.family`) | url (use in `timeline.fonts[].src`) |
|
|
274
|
+
|---|---|---|
|
|
275
|
+
| Inter (sans, variable) | `UcCo3FwrK3iLTfvlaQc78lA2` | `https://fonts.gstatic.com/s/inter/v20/UcCo3FwrK3iLTfvlaQc78lA2.ttf` |
|
|
276
|
+
| Roboto (sans, variable) | `KFOmCnqEu92Fr1Me5WZLCzYlKw` | `https://fonts.gstatic.com/s/roboto/v50/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf` |
|
|
277
|
+
| Open Sans (sans, variable) | `mem8YaGs126MiZpBA-U1UpcaXcl0Aw` | `https://fonts.gstatic.com/s/opensans/v44/mem8YaGs126MiZpBA-U1UpcaXcl0Aw.ttf` |
|
|
278
|
+
| Montserrat (sans, variable) | `JTUSjIg1_i6t8kCHKm45xW5rygbi49c` | `https://fonts.gstatic.com/s/montserrat/v31/JTUSjIg1_i6t8kCHKm45xW5rygbi49c.ttf` |
|
|
279
|
+
| Poppins (sans) | `pxiEyp8kv8JHgFVrFJDUc1NECPY` | `https://fonts.gstatic.com/s/poppins/v24/pxiEyp8kv8JHgFVrFJDUc1NECPY.ttf` |
|
|
280
|
+
| DM Sans (sans, variable) | `rP2Hp2ywxg089UriOZSCHBeHFl0` | `https://fonts.gstatic.com/s/dmsans/v17/rP2Hp2ywxg089UriOZSCHBeHFl0.ttf` |
|
|
281
|
+
| Nunito (sans, variable) | `XRXV3I6Li01BKof4MuyAbsrVcA` | `https://fonts.gstatic.com/s/nunito/v32/XRXV3I6Li01BKof4MuyAbsrVcA.ttf` |
|
|
282
|
+
| Raleway (sans, variable) | `1Ptug8zYS_SKggPN-CoCTqluHfE` | `https://fonts.gstatic.com/s/raleway/v37/1Ptug8zYS_SKggPN-CoCTqluHfE.ttf` |
|
|
283
|
+
| Oswald (display, variable) | `TK3iWkUHHAIjg75GHjUHte5fKg` | `https://fonts.gstatic.com/s/oswald/v57/TK3iWkUHHAIjg75GHjUHte5fKg.ttf` |
|
|
284
|
+
| Bebas Neue (display) | `JTUSjIg69CK48gW7PXooxW5rygbi49c` | `https://fonts.gstatic.com/s/bebasneue/v16/JTUSjIg69CK48gW7PXooxW5rygbi49c.ttf` |
|
|
285
|
+
| Anton (display) | `1Ptgg87LROyAm0K08i4gS7lu` | `https://fonts.gstatic.com/s/anton/v27/1Ptgg87LROyAm0K08i4gS7lu.ttf` |
|
|
286
|
+
| Playfair Display (serif, variable) | `nuFiD-vYSZviVYUb_rj3ij__anPXPTvSgWE_-xU` | `https://fonts.gstatic.com/s/playfairdisplay/v40/nuFiD-vYSZviVYUb_rj3ij__anPXPTvSgWE_-xU.ttf` |
|
|
287
|
+
|
|
288
|
+
Variable fonts cover the full weight range (100–900) from a single URL — set `font.weight` on the clip. For a font not listed: copy an entry **verbatim** from the Studio SDK catalogue (~400 fonts) at <https://github.com/shotstack/shotstack-studio-sdk/blob/main/src/core/fonts/google-fonts.ts>, or open the font on <https://fonts.google.com>, view the CSS, and copy the exact `.ttf` URL from the `@font-face` `src`. Never hand-edit the version or hash segments.
|
|
270
289
|
|
|
271
290
|
### Usage example
|
|
272
291
|
|
|
@@ -297,7 +316,7 @@ The `font.family` value MUST match the `family` column in the table above (it's
|
|
|
297
316
|
## Top 5 mistakes
|
|
298
317
|
|
|
299
318
|
1. **Reverse track order.** `tracks[0]` is the TOP layer, not the bottom. Captions go in early tracks; backgrounds go in late tracks.
|
|
300
|
-
2. **System fonts.** `Arial`, `Helvetica`, `Times New Roman`, etc. are not installed. Use Google Fonts via `timeline.fonts[]
|
|
319
|
+
2. **System fonts.** `Arial`, `Helvetica`, `Times New Roman`, etc. are not installed. Use Google Fonts via `timeline.fonts[]`, copied verbatim from the verified catalogue in the Fonts section.
|
|
301
320
|
3. **Captions fill the whole frame.** A `rich-caption` clip without `width`, `height`, and `fit: "none"` covers the entire output. Use a named preset from `references/caption.md`.
|
|
302
321
|
4. **`<text>` inside an SVG asset.** Raw `<text>` is unsupported. Use a `rich-text` asset for any text content; reserve SVG for shapes only.
|
|
303
322
|
5. **Composing custom caption styles when presets exist.** The five named presets (Nico, Kai, Kapow, Lovely Little Lychee, Rizz) cover the common styles. Use one verbatim from `references/caption.md` unless the user asks for something specific.
|