@scenerok/cli 2.2.0

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 (79) hide show
  1. package/README.md +173 -0
  2. package/dist/commands/auth.d.ts +3 -0
  3. package/dist/commands/auth.d.ts.map +1 -0
  4. package/dist/commands/auth.js +94 -0
  5. package/dist/commands/cache.d.ts +3 -0
  6. package/dist/commands/cache.d.ts.map +1 -0
  7. package/dist/commands/cache.js +65 -0
  8. package/dist/commands/lint-video.d.ts +21 -0
  9. package/dist/commands/lint-video.d.ts.map +1 -0
  10. package/dist/commands/lint-video.js +122 -0
  11. package/dist/commands/project.d.ts +3 -0
  12. package/dist/commands/project.d.ts.map +1 -0
  13. package/dist/commands/project.js +173 -0
  14. package/dist/commands/render.d.ts +4 -0
  15. package/dist/commands/render.d.ts.map +1 -0
  16. package/dist/commands/render.js +242 -0
  17. package/dist/commands/skills.d.ts +3 -0
  18. package/dist/commands/skills.d.ts.map +1 -0
  19. package/dist/commands/skills.js +208 -0
  20. package/dist/commands/validate.d.ts +3 -0
  21. package/dist/commands/validate.d.ts.map +1 -0
  22. package/dist/commands/validate.js +254 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +51 -0
  26. package/dist/lib/api.d.ts +268 -0
  27. package/dist/lib/api.d.ts.map +1 -0
  28. package/dist/lib/api.js +160 -0
  29. package/dist/lib/config.d.ts +13 -0
  30. package/dist/lib/config.d.ts.map +1 -0
  31. package/dist/lib/config.js +53 -0
  32. package/dist/lib/format-api-error.d.ts +2 -0
  33. package/dist/lib/format-api-error.d.ts.map +1 -0
  34. package/dist/lib/format-api-error.js +24 -0
  35. package/dist/lib/language.d.ts +3 -0
  36. package/dist/lib/language.d.ts.map +1 -0
  37. package/dist/lib/language.js +9 -0
  38. package/dist/lib/logger.d.ts +8 -0
  39. package/dist/lib/logger.d.ts.map +1 -0
  40. package/dist/lib/logger.js +51 -0
  41. package/dist/lib/project.d.ts +29 -0
  42. package/dist/lib/project.d.ts.map +1 -0
  43. package/dist/lib/project.js +156 -0
  44. package/dist/lib/sdk-scene-modules.d.ts +7 -0
  45. package/dist/lib/sdk-scene-modules.d.ts.map +1 -0
  46. package/dist/lib/sdk-scene-modules.js +35 -0
  47. package/dist/utils/skillsManifest.d.ts +42 -0
  48. package/dist/utils/skillsManifest.d.ts.map +1 -0
  49. package/dist/utils/skillsManifest.js +122 -0
  50. package/examples/system/3-tips-fitness.vid +45 -0
  51. package/examples/system/ecom-product-launch.vid +31 -0
  52. package/examples/system/glitch-title.vid +17 -0
  53. package/examples/system/meme-remix.vid +15 -0
  54. package/examples/system/minimal-text-reel.vid +11 -0
  55. package/examples/system/product-launch.vid +18 -0
  56. package/examples/system/rokmilk-chocolate-promo.vid +25 -0
  57. package/examples/system/testimonial-cut.vid +16 -0
  58. package/examples/system/ugc-testimonial-voiceover.vid +33 -0
  59. package/examples/system/voice-visual-promo.vid +21 -0
  60. package/package.json +51 -0
  61. package/skills/remote/ai-models.md +65 -0
  62. package/skills/remote/animations-catalog.md +89 -0
  63. package/skills/remote/animations-picker.md +77 -0
  64. package/skills/remote/brand-setup.md +69 -0
  65. package/skills/remote/catalog-index.json +1810 -0
  66. package/skills/remote/environments.md +73 -0
  67. package/skills/remote/intent-router.md +81 -0
  68. package/skills/remote/media-stdlib.md +66 -0
  69. package/skills/remote/music.md +69 -0
  70. package/skills/remote/sdk-guide.md +201 -0
  71. package/skills/remote/shaders-filters.md +97 -0
  72. package/skills/remote/text-styles.md +108 -0
  73. package/skills/remote/transitions-catalog.md +55 -0
  74. package/skills/remote/tts.md +82 -0
  75. package/skills/remote/vidscript-guide.md +574 -0
  76. package/skills/remote/vidscript-sample.md +28 -0
  77. package/skills/remote/vidscript-strict.md +251 -0
  78. package/skills/remote/workflow.md +72 -0
  79. package/skills/shared/SKILL.md +157 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Remote skill freshness: hash flat markdown/json files against the published manifest.
3
+ */
4
+ import { createHash } from 'node:crypto';
5
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
6
+ import { homedir } from 'node:os';
7
+ import { join } from 'node:path';
8
+ export const DEFAULT_MANIFEST_URL = 'https://scenerok.com/.well-known/agent-skills/manifest.json';
9
+ const TEXT_EXT = new Set(['.md', '.json', '.txt']);
10
+ function hashFileContent(content, ext) {
11
+ const h = createHash('sha256');
12
+ const normalized = TEXT_EXT.has(ext) ? content.replace(/\r\n/g, '\n') : content;
13
+ h.update(normalized);
14
+ return h.digest('hex').slice(0, 16);
15
+ }
16
+ /** Map manifest slug to on-disk filename. */
17
+ export function slugToFilename(slug) {
18
+ if (slug === 'catalog-index')
19
+ return 'catalog-index.json';
20
+ return `${slug}.md`;
21
+ }
22
+ export function hashInstalledFile(filePath) {
23
+ if (!existsSync(filePath))
24
+ return null;
25
+ const content = readFileSync(filePath, 'utf8');
26
+ const ext = filePath.slice(filePath.lastIndexOf('.'));
27
+ return { hash: hashFileContent(content, ext), files: 1 };
28
+ }
29
+ export function hashInstalled(skillDir, manifest) {
30
+ const out = {};
31
+ for (const slug of Object.keys(manifest.skills)) {
32
+ const filename = slugToFilename(slug);
33
+ const entry = hashInstalledFile(join(skillDir, filename));
34
+ if (entry)
35
+ out[slug] = entry;
36
+ }
37
+ return out;
38
+ }
39
+ export function diffSkills(installed, latest) {
40
+ const skills = [];
41
+ const summary = { current: 0, outdated: 0, missing: 0 };
42
+ for (const name of Object.keys(latest.skills).sort()) {
43
+ const latestEntry = latest.skills[name];
44
+ const installedEntry = installed[name];
45
+ let status;
46
+ if (!installedEntry)
47
+ status = 'missing';
48
+ else if (installedEntry.hash === latestEntry.hash)
49
+ status = 'current';
50
+ else
51
+ status = 'outdated';
52
+ if (status === 'current')
53
+ summary.current++;
54
+ else if (status === 'outdated')
55
+ summary.outdated++;
56
+ else
57
+ summary.missing++;
58
+ skills.push({
59
+ name,
60
+ status,
61
+ installedHash: installedEntry?.hash,
62
+ latestHash: latestEntry.hash,
63
+ filename: slugToFilename(name),
64
+ });
65
+ }
66
+ return {
67
+ updateAvailable: summary.outdated > 0 || summary.missing > 0,
68
+ summary,
69
+ skills,
70
+ };
71
+ }
72
+ export async function fetchManifest(url = DEFAULT_MANIFEST_URL) {
73
+ const res = await fetch(url, { headers: { Accept: 'application/json' } });
74
+ if (!res.ok)
75
+ throw new Error(`HTTP ${res.status} fetching ${url}`);
76
+ const data = (await res.json());
77
+ if (!data?.skills || typeof data.skills !== 'object') {
78
+ throw new Error(`Malformed skills manifest from ${url}`);
79
+ }
80
+ return data;
81
+ }
82
+ export async function fetchSkillFile(slug, baseUrl) {
83
+ const site = (baseUrl ?? 'https://scenerok.com').replace(/\/$/, '');
84
+ const filename = slugToFilename(slug);
85
+ const url = `${site}/.well-known/agent-skills/${filename}`;
86
+ const res = await fetch(url);
87
+ if (!res.ok)
88
+ throw new Error(`HTTP ${res.status} fetching ${url}`);
89
+ return res.text();
90
+ }
91
+ export function locateSkillDir(platform) {
92
+ const dirs = {
93
+ opencode: join(homedir(), '.agents/skills/scenerok'),
94
+ claude: join(homedir(), '.claude/skills/scenerok'),
95
+ codex: join(homedir(), '.codex/skills/scenerok'),
96
+ cursor: join(homedir(), '.cursor/skills/scenerok'),
97
+ aider: join(homedir(), '.aider/skills/scenerok'),
98
+ };
99
+ if (platform && dirs[platform] && existsSync(dirs[platform])) {
100
+ return dirs[platform];
101
+ }
102
+ for (const dir of Object.values(dirs)) {
103
+ if (existsSync(join(dir, 'SKILL.md')))
104
+ return dir;
105
+ }
106
+ return null;
107
+ }
108
+ export async function checkSkills(opts = {}) {
109
+ const latest = await fetchManifest(opts.manifestUrl);
110
+ const location = opts.dir ?? locateSkillDir() ?? null;
111
+ const installed = location ? hashInstalled(location, latest) : {};
112
+ const diff = diffSkills(installed, latest);
113
+ return { location, ...diff };
114
+ }
115
+ /** List extra local files not in manifest (legacy bundled files). */
116
+ export function listExtraLocalFiles(skillDir, manifest) {
117
+ if (!existsSync(skillDir))
118
+ return [];
119
+ const known = new Set(Object.keys(manifest.skills).map(slugToFilename));
120
+ known.add('SKILL.md');
121
+ return readdirSync(skillDir).filter((f) => !known.has(f));
122
+ }
@@ -0,0 +1,45 @@
1
+ # 3 Tips Fitness Reel - Reusable Template
2
+ # Demonstrates dynamic timing [-] blocks + xAI video generation per tip (explicit import form)
3
+
4
+ import xai from "@scenerok/xai"
5
+
6
+ # === INPUTS (user provides these) ===
7
+ input tip1_text = "{{tip1_text | Tip 1 goes here}}"
8
+ input tip2_text = "{{tip2_text | Tip 2 goes here}}"
9
+ input tip3_text = "{{tip3_text | Tip 3 goes here}}"
10
+ input cta_text = "{{cta_text | Save this for later}}"
11
+
12
+ # === DYNAMIC TIP SECTIONS ===
13
+ # Each [-] block auto-calculates duration based on content
14
+ [-] = text tip1_text, style: title, position: center, color: "#FFFFFF", size: 64, align: center
15
+
16
+ # xAI generates a fresh visual for this specific tip
17
+ [-] = video xai.imagine(
18
+ "Cinematic fitness shot: " + tip1_text,
19
+ aspect_ratio: "9:16",
20
+ resolution: "1k"
21
+ )
22
+
23
+ [-] = text tip2_text, style: title, position: center, color: "#FFFFFF", size: 64, align: center
24
+
25
+ [-] = video xai.imagine(
26
+ "Cinematic fitness shot: " + tip2_text,
27
+ aspect_ratio: "9:16",
28
+ resolution: "1k"
29
+ )
30
+
31
+ [-] = text tip3_text, style: title, position: center, color: "#FFFFFF", size: 64, align: center
32
+
33
+ [-] = video xai.imagine(
34
+ "Cinematic fitness shot: " + tip3_text,
35
+ aspect_ratio: "9:16",
36
+ resolution: "1k"
37
+ )
38
+
39
+ # === CTA + BRANDING ===
40
+ [- 2s] = text cta_text, style: cta, position: bottom, color: "#FF0055", size: 48
41
+
42
+ # Subtle brand watermark
43
+ [-] = text "SceneRok", style: watermark, position: "85%, 92%", color: "#888888", size: 28, opacity: 0.6
44
+
45
+ output to "3-tips-fitness.mp4", resolution: "1080x1920", fps: 30
@@ -0,0 +1,31 @@
1
+ # Ecom Product Launch Reel - Reusable Template
2
+ # Shows product + lifestyle shot generated by xAI (@scenerok/xai), with strong CTA
3
+
4
+ import xai from "@scenerok/xai"
5
+
6
+ # === INPUTS ===
7
+ input product_name = "{{product_name | Your Product}}"
8
+ input hook_text = "{{hook_text | The one thing that actually works}}"
9
+ input price = "{{price | $49}}"
10
+ input cta_text = "{{cta_text | Shop Now}}"
11
+
12
+ # === HOOK ===
13
+ [0s .. 3s] = text hook_text, style: hook, position: center, color: "#FFFFFF", size: 72, align: center
14
+
15
+ # === PRODUCT VISUAL (xAI generated lifestyle video) ===
16
+ [3s .. 10s] = video xai.imagine(
17
+ "Premium lifestyle product shot of " + product_name + ", cinematic lighting, modern aesthetic, 9:16 vertical",
18
+ aspect_ratio: "9:16",
19
+ resolution: "1k"
20
+ )
21
+
22
+ # === PRICE + URGENCY ===
23
+ [8s .. 12s] = text "Only " + price, style: price, position: "center, 70%", color: "#FF0055", size: 56
24
+
25
+ # === STRONG CTA ===
26
+ [- 3s] = text cta_text, style: cta, position: bottom, color: "#FFFFFF", size: 64, stroke: "#000000", stroke_width: 4
27
+
28
+ # Subtle brand element
29
+ [-] = text "SceneRok", style: watermark, position: "85%, 92%", color: "#888888", size: 26, opacity: 0.5
30
+
31
+ output to "product-launch.mp4", resolution: "1080x1920", fps: 30
@@ -0,0 +1,17 @@
1
+ # Glitch Title
2
+ # A stylized intro with glitch effects and bold typography.
3
+ # Great for gaming, tech, or edgy brand content.
4
+
5
+ input background = "{{background_clip}}"
6
+
7
+ [0s .. 5s] = background
8
+ background.Trim(start: 0s, end: 5s)
9
+ background.Opacity(value: 0.8, duration: 5s)
10
+
11
+ [0.5s .. 3s] = text "{{title}}", font: "Bebas Neue", position: center, color: "#00FF88", size: 72, stroke: "#000000", stroke_width: 4, x: "50%", y: "45%", align: center, letter_spacing: 4
12
+
13
+ [3.2s .. 4.5s] = text "{{subtitle}}", font: "Oswald", position: center, color: "#FF0055", size: 96, stroke: "#000000", stroke_width: 4, x: "50%", y: "60%", align: center, rotation: 2, shadow_color: "#00FF88", shadow_blur: 8
14
+
15
+ [0s .. 5s] = filter "glitch", intensity: 0.4
16
+
17
+ output to "glitch-title.mp4", resolution: "1080x1920", fps: 30
@@ -0,0 +1,15 @@
1
+ # Meme Remix
2
+ # Fast-cut format with top and bottom text overlays.
3
+ # Perfect for reaction clips, punchline edits, and viral-style content.
4
+
5
+ input clip = "{{reaction_clip}}"
6
+
7
+ [0s .. 4s] = clip
8
+ clip.Trim(start: 0s, end: 4s)
9
+ clip.Speed(factor: 1.2)
10
+
11
+ [0.1s .. 1.5s] = text "{{hook_text}}", style: title, position: top, color: "#FFFFFF", size: 56, stroke: "#000000", stroke_width: 3
12
+
13
+ [2.5s .. 3.8s] = text "{{punchline}}", style: title, position: bottom, color: "#EF4444", size: 64, stroke: "#000000", stroke_width: 4
14
+
15
+ output to "meme-remix.mp4", resolution: "1080x1920", fps: 30
@@ -0,0 +1,11 @@
1
+ # Minimal Text Reel v2
2
+ # A clean text-only reel demonstrating advanced typography:
3
+ # dynamic placement, rotation, Google Fonts, shadows, letter spacing.
4
+
5
+ [0s .. 2.5s] = text "{{headline}}", font: "Bebas Neue", size: 96, color: "#FFFFFF", x: "50%", y: "30%", align: center, letter_spacing: 8, shadow_color: "#FF0055", shadow_blur: 10, shadow_offset_x: 4, shadow_offset_y: 4
6
+
7
+ [2s .. 4s] = text "{{subtitle}}", font: "Inter", size: 48, color: "#00FF88", x: "50%", y: "55%", align: center, rotation: -3, opacity: 0.9
8
+
9
+ [3.5s .. 5.5s] = text "{{cta}}", font: "Space Grotesk", size: 44, color: "#6366F1", x: "50%", y: "75%", align: center, shadow_color: "#000000", shadow_blur: 16, shadow_offset_y: 6
10
+
11
+ output to "minimal-text-reel.mp4", resolution: "1080x1920", fps: 30
@@ -0,0 +1,18 @@
1
+ # Product Launch
2
+ # Showcase a product with bold headlines and a clear call-to-action.
3
+ # Uses a subtle vignette filter for cinematic depth.
4
+
5
+ input hero = "{{hero_clip}}"
6
+
7
+ [0s .. 6s] = hero
8
+ hero.Trim(start: 0s, end: 6s)
9
+
10
+ [0.3s .. 2.3s] = text "{{headline}}", style: title, position: top, color: "#FFFFFF", size: 64, stroke: "#000000", stroke_width: 3
11
+
12
+ [2.5s .. 4.5s] = text "{{description}}", style: caption, position: center, color: "#F3F4F6", size: 36, stroke: "#000000", stroke_width: 2
13
+
14
+ [4.5s .. 5.8s] = text "{{cta}}", style: title, position: bottom, color: "#F59E0B", size: 52, stroke: "#000000", stroke_width: 3
15
+
16
+ [0s .. 6s] = filter "vignette", intensity: 0.35
17
+
18
+ output to "product-launch.mp4", resolution: "1080x1920", fps: 30
@@ -0,0 +1,25 @@
1
+ # RokMilk Chocolate Milkshake — 15s promo (correct VidScript v2 patterns)
2
+ # Requires: scenerok secrets set XAI_API_KEY=... (or configured on scenerok.com account)
3
+
4
+ import xai from "@scenerok/xai"
5
+ import motion from "@scenerok/basic-animations"
6
+
7
+ # Generated visuals — one clip per segment; prompts must use straight double quotes only
8
+ [-] = video xai.imagine("Cinematic slow-motion pour of rich creamy chocolate milk into a sleek modern 250ml carton on marble, warm studio lighting, shallow depth of field, 9:16 vertical", aspect_ratio: "9:16", duration: 5)
9
+
10
+ [-] = video xai.imagine("Elegant rotating product shot of premium chocolate milk carton labeled RokMilk, brown and gold palette, soft bokeh, advertising visual, 9:16 vertical", aspect_ratio: "9:16", duration: 5)
11
+
12
+ [-] = video xai.imagine("Silky chocolate milk swirl in glass with RokMilk carton blurred in background, golden light, appetizing food cinematography, 9:16 vertical", aspect_ratio: "9:16", duration: 5)
13
+
14
+ [0s .. 2.5s] = text "RokMilk", font: "Bebas Neue", size: 80, color: "#FFD700", x: "50%", y: "18%", align: center, stroke: "#3E1C00", stroke_width: 4, letter_spacing: 6, animate: motion.fadeIn(0.6s)
15
+
16
+ [2s .. 5s] = text "Chocolate Milkshake", font: "Outfit", size: 56, color: "#FFFFFF", x: "50%", y: "45%", align: center, stroke: "#3E1C00", stroke_width: 3, animate: motion.riseIn(0.8s)
17
+
18
+ [5s .. 8s] = text "250ml of Pure Indulgence", font: "Inter", size: 42, color: "#F3E5AB", x: "50%", y: "55%", align: center, stroke: "#3E1C00", stroke_width: 2, animate: motion.slideY(40, 0, 0.8s)
19
+
20
+ [11s .. 14s] = text "visit your nearest grocery store", font: "Bebas Neue", size: 48, color: "#FFD700", x: "50%", y: "82%", align: center, stroke: "#3E1C00", stroke_width: 4, letter_spacing: 3, animate: motion.fadeIn(1s)
21
+
22
+ [0s .. 15s] = filter "vignette", intensity: 0.3
23
+ [0s .. 15s] = filter "saturation", value: 1.12
24
+
25
+ output to "rokmilk-chocolate-promo.mp4", resolution: "1080x1920", fps: 30, background: "#1A0F05"
@@ -0,0 +1,16 @@
1
+ # Testimonial Cut
2
+ # A quote-focused format with a subtle vintage feel.
3
+ # Great for customer stories, reviews, and social proof clips.
4
+
5
+ input speaker = "{{speaker_clip}}"
6
+
7
+ [0s .. 5s] = speaker
8
+ speaker.Trim(start: 0s, end: 5s)
9
+
10
+ [0.5s .. 3s] = text "{{quote}}", style: subtitle, position: center, color: "#FFFFFF", size: 42, stroke: "#000000", stroke_width: 2
11
+
12
+ [3.2s .. 4.8s] = text "{{attribution}}", style: caption, position: bottom, color: "#D1D5DB", size: 32, stroke: "#000000", stroke_width: 2
13
+
14
+ [0s .. 5s] = filter "sepia", intensity: 0.3
15
+
16
+ output to "testimonial.mp4", resolution: "1080x1920", fps: 30
@@ -0,0 +1,33 @@
1
+ # UGC Testimonial with Voiceover - Reusable Template
2
+ # Uses xAI (via explicit import) for TTS voiceover + generated visuals
3
+
4
+ import xai from "@scenerok/xai"
5
+
6
+ # === INPUTS ===
7
+ input quote = "{{quote | This product completely changed how I work}}"
8
+ input customer_name = "{{customer_name | Sarah K.}}"
9
+ input product_name = "{{product_name | SceneRok}}"
10
+
11
+ # === VOICEOVER + VISUAL ===
12
+ [-] = audio xai.tts(
13
+ quote + ". " + customer_name + " from " + product_name,
14
+ voice: "eve",
15
+ speed: 1.05
16
+ )
17
+
18
+ [-] = video xai.imagine(
19
+ "Cinematic lifestyle shot representing: " + quote + ", premium modern aesthetic, 9:16 vertical",
20
+ aspect_ratio: "9:16",
21
+ resolution: "1k"
22
+ )
23
+
24
+ # === QUOTE TEXT (appears while voiceover plays) ===
25
+ [-] = text "\"" + quote + "\"", style: quote, position: center, color: "#FFFFFF", size: 52, align: center
26
+
27
+ # === ATTRIBUTION ===
28
+ [- 2s] = text "— " + customer_name, style: attribution, position: "center, 75%", color: "#CCCCCC", size: 32
29
+
30
+ # === BRANDING ===
31
+ [-] = text "Made with SceneRok", style: watermark, position: "85%, 92%", color: "#888888", size: 24, opacity: 0.6
32
+
33
+ output to "ugc-testimonial.mp4", resolution: "1080x1920", fps: 30
@@ -0,0 +1,21 @@
1
+ # Voice + Visual Promo
2
+ # Combines main footage with voiceover or music and layered titles.
3
+
4
+ input main = "{{main_video}}"
5
+ input voice = "{{voiceover_audio}}"
6
+
7
+ [0s .. 12s] = main
8
+ main.Trim(start: 0s, end: 12s)
9
+
10
+ [0s .. 12s] = audio voice, volume: 0.9
11
+
12
+ [0.5s .. 3s] = text "{{title}}", font: "Inter", size: 68, color: "{{brand_color}}", x: "50%", y: "22%", align: center, stroke: "#111827", stroke_width: 3
13
+
14
+ [3s .. 7s] = text "{{subtitle}}", font: "Inter", size: 34, color: "#F3F4F6", x: "50%", y: "50%", align: center, stroke: "#0F172A", stroke_width: 2
15
+
16
+ [7s .. 11s] = text "{{cta}}", font: "Inter", size: 56, color: "#FFFFFF", x: "50%", y: "78%", align: center, stroke: "#000000", stroke_width: 3
17
+
18
+ [0s .. 12s] = filter "vignette", intensity: 0.22
19
+ [0s .. 12s] = filter "saturation", value: 1.12
20
+
21
+ output to "voice-visual-promo.mp4", resolution: "1080x1920", fps: 30
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@scenerok/cli",
3
+ "version": "2.2.0",
4
+ "description": "SceneRok CLI - Create videos from your terminal and agent workflows",
5
+ "type": "module",
6
+ "bin": {
7
+ "scenerok": "dist/index.js",
8
+ "sr": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "skills",
13
+ "examples",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "tsc --watch",
19
+ "sync:skills": "node ../../scripts/sync-scenerok-skills.mjs",
20
+ "prepublishOnly": "npm run sync:skills && npm run build"
21
+ },
22
+ "keywords": [
23
+ "scenerok",
24
+ "@scenerok/cli",
25
+ "video",
26
+ "cli",
27
+ "vidscript",
28
+ "agent",
29
+ "ai"
30
+ ],
31
+ "author": "SceneRok",
32
+ "license": "MIT",
33
+ "dependencies": {
34
+ "commander": "^12.0.0",
35
+ "chalk": "^5.3.0",
36
+ "ora": "^8.0.1",
37
+ "inquirer": "^9.2.0",
38
+ "node-fetch": "^3.3.2"
39
+ },
40
+ "devDependencies": {
41
+ "@types/inquirer": "^9.0.7",
42
+ "@types/node": "^20.11.0",
43
+ "typescript": "^5.3.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }
@@ -0,0 +1,65 @@
1
+ # AI Models — Video and Image Generation
2
+
3
+ ## Modality picker
4
+
5
+ | Intent | API | When |
6
+ | --- | --- | --- |
7
+ | Clean still / key visual | `xai.image(...)` · `cf.image(...)` | Product shot, poster frame |
8
+ | Text-to-video | `xai.imagine(...)` · `xai.genVideo(...)` · `cf.video(...)` | B-roll, lifestyle, cinematic |
9
+ | Animate one image | `xai.imageToVideo(img, prompt, ...)` · `cf.imageToVideo(...)` | Product photo → motion |
10
+ | Multi-reference scene | `xai.referenceToVideo([imgs], prompt, ...)` | Brand elements guide scene |
11
+ | Extend existing clip | `xai.extendVideo(...)` · `cf.extendVideo(...)` | Continue generated clip |
12
+
13
+ **Always import:** `import xai from "@scenerok/xai"` and/or `import cf from "@scenerok/cloudflare"`.
14
+
15
+ Prefer **720p** (or the model default) for generative video. Cap spend with duration, not exotic post-steps.
16
+
17
+ Generative prompts must require: no text, no words, no letters, no captions, no logos, no watermarks, no readable UI copy.
18
+
19
+ ## Examples
20
+
21
+ ### Grok genVideo()
22
+
23
+ Explicit duration, resolution, and aspect ratio for text-to-video.
24
+
25
+ **Landscape 8 s**
26
+
27
+ ```vidscript
28
+ import xai from "@scenerok/xai"
29
+
30
+ [- 3s] = video xai.genVideo("Timelapse of clouds over desert mesas, wide cinematic, anamorphic flare", duration: 3, aspect_ratio: "16:9", resolution: "720p")
31
+
32
+ [prev .. prev + 0.8s] = text "genVideo()", font: "Inter Bold", size: 44, color: "#FFFFFF", shadow_blur: 10, align: "center", y: "88%"
33
+ ```
34
+
35
+ **Vertical 6 s reel**
36
+
37
+ ```vidscript
38
+ import xai from "@scenerok/xai"
39
+
40
+ [- 3s] = video xai.genVideo("Drone shot over a futuristic city at sunset, lens flare, cinematic", duration: 3, aspect_ratio: "9:16", resolution: "720p")
41
+ ```
42
+
43
+ ### Grok imagine()
44
+
45
+ One-call text-to-video. Default 5 s, 720p, 9:16.
46
+
47
+ **Ocean drone**
48
+
49
+ ```vidscript
50
+ import xai from "@scenerok/xai"
51
+
52
+ [- 3s] = video xai.imagine("Slow aerial drone over turquoise ocean waves, golden hour, cinematic", aspect_ratio: "16:9", resolution: "720p")
53
+
54
+ [prev .. prev + 0.8s] = text "imagine()", font: "Inter Bold", size: 48, color: "#FFFFFF", align: "center", y: "85%"
55
+ ```
56
+
57
+ **Street market**
58
+
59
+ ```vidscript
60
+ import xai from "@scenerok/xai"
61
+
62
+ [- 3s] = video xai.imagine("A neon-lit street market at night, cinematic, volumetric light", aspect_ratio: "9:16", resolution: "720p")
63
+
64
+ [prev .. prev + 0.8s] = text "Grok imagine()", font: "Inter Bold", size: 56, color: "#FFFFFF", shadow_color: "#000000", shadow_blur: 12, position: "bottom", align: "center"
65
+ ```
@@ -0,0 +1,89 @@
1
+ # Animations Catalog
2
+
3
+ All `animations.<fn>(...)` blocks from `@scenerok/animations`. Total: **67** entries.
4
+
5
+ ```vidscript
6
+ import animations from "@scenerok/animations"
7
+ ```
8
+
9
+ | fn | family | verified | defaultDuration | params |
10
+ | --- | --- | --- | --- | --- |
11
+ | `app_showcase` | other | yes | 6s | positional: `imageUrl` · named: `imageUrl, userName, userRole, tagline, accentColor` |
12
+ | `apple_money_count` | other | yes | 5s | positional: `amount` · named: `amount, currency, accentColor` |
13
+ | `blend_caption` | lower-third | yes | 5s | positional: `text` · named: `text, color` |
14
+ | `blue_sweater_intro_video` | other | yes | 12s | positional: `creatorName` · named: `headline, creatorName, creatorHandle, creatorBio, avatarUrl` |
15
+ | `code_3d_extrude` | code-anim | yes | 8s | positional: `code` · named: `code, lang, theme, filename` |
16
+ | `code_diff` | code-anim | yes | 6s | positional: `from, to` · named: `from, to, lang, theme` |
17
+ | `code_highlight` | code-anim | yes | 5s | positional: `code` · named: `code, lang, theme, filename` |
18
+ | `code_morph` | code-anim | yes | 7s | positional: `from, to` · named: `from, to, lang, theme` |
19
+ | `code_particle_assemble` | code-anim | yes | 8s | positional: `code` · named: `code, lang, theme, filename` |
20
+ | `code_scroll` | code-anim | yes | 6s | positional: `code` · named: `code, lang, theme, filename` |
21
+ | `code_shader_dissolve` | code-anim | yes | 7s | positional: `code` · named: `code, lang, theme, filename` |
22
+ | `code_snippet_apple_terminal_basic` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
23
+ | `code_snippet_apple_terminal_clear_dark` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
24
+ | `code_snippet_apple_terminal_clear_light` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
25
+ | `code_snippet_apple_terminal_grass` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
26
+ | `code_snippet_apple_terminal_homebrew` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
27
+ | `code_snippet_apple_terminal_man_page` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
28
+ | `code_snippet_apple_terminal_novel` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
29
+ | `code_snippet_apple_terminal_ocean` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
30
+ | `code_snippet_apple_terminal_pro` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
31
+ | `code_snippet_apple_terminal_red_sands` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
32
+ | `code_snippet_apple_terminal_silver_aerogel` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
33
+ | `code_snippet_apple_terminal_solid_colors` | code-snippet | yes | 12s | positional: `command` · named: `command, output, prompt, title` |
34
+ | `code_snippet_dark_2026` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
35
+ | `code_snippet_dark_modern` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
36
+ | `code_snippet_dark_plus` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
37
+ | `code_snippet_flight` | code-snippet | yes | 6s | positional: `code` · named: `code, lang, theme, filename` |
38
+ | `code_snippet_high_contrast` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
39
+ | `code_snippet_high_contrast_light` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
40
+ | `code_snippet_light_2026` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
41
+ | `code_snippet_light_modern` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
42
+ | `code_snippet_light_plus` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
43
+ | `code_snippet_monokai` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
44
+ | `code_snippet_solarized_light` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
45
+ | `code_snippet_visual_studio_dark` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
46
+ | `code_snippet_visual_studio_light` | code-snippet | yes | 11s | positional: `code` · named: `code, filename, lang` |
47
+ | `code_typing` | code-anim | yes | 5s | positional: `code` · named: `code, lang, theme, filename` |
48
+ | `instagram_follow` | social-card | yes | 5s | positional: `displayName, handle` · named: `displayName, handle, followers, avatarUrl, url` |
49
+ | `ios26_liquid_glass` | other | yes | 15s | positional: `(none)` · named: `(none)` |
50
+ | `liquid_glass_context_menu` | other | yes | 8s | positional: `fileName` · named: `fileName, fileMeta` |
51
+ | `liquid_glass_media_controls` | other | yes | 8s | positional: `trackTitle` · named: `trackTitle, artist` |
52
+ | `liquid_glass_notification` | other | yes | 8s | positional: `title` · named: `title, body, messageText` |
53
+ | `liquid_glass_widgets` | other | yes | 8s | positional: `showcaseTitle` · named: `showcaseTitle, showcaseDescription` |
54
+ | `logo_outro` | brand | yes | 6s | positional: `tagline` · named: `tagline, url` |
55
+ | `lower_third` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle` |
56
+ | `lower_third_bild` | lower-third | yes | 8s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
57
+ | `lt_accent_underline` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
58
+ | `lt_bold_block` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
59
+ | `lt_clean_bar` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
60
+ | `lt_color_block` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
61
+ | `lt_dark_card` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
62
+ | `lt_kicker_name` | lower-third | yes | 5s | positional: `kicker, title` · named: `kicker, title, accent` |
63
+ | `lt_mask_reveal` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
64
+ | `lt_side_rule` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
65
+ | `lt_soft_pill` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
66
+ | `lt_stack_bars` | lower-third | yes | 5s | positional: `title, subtitle` · named: `title, subtitle, accent, bg` |
67
+ | `macos_notification` | social-card | yes | 5s | positional: `appName, title` · named: `appName, timeLabel, title, body` |
68
+ | `macos_tahoe_liquid_glass` | other | yes | 15s | positional: `windowTitle` · named: `wallpaperUrl, windowTitle, toastBody, weatherLocation` |
69
+ | `news_ticker` | social-card | yes | 7s | positional: `headline` · named: `desk, time, label, headline, headlineAccent, ticker` |
70
+ | `reddit_post` | social-card | yes | 5s | positional: `subreddit, title` · named: `subreddit, author, title, body, votes` |
71
+ | `spotify_card` | social-card | yes | 5s | positional: `track, artist` · named: `track, artist` |
72
+ | `tiktok_follow` | social-card | yes | 5s | positional: `displayName, handle` · named: `displayName, handle, followers, avatarUrl, url` |
73
+ | `ui_3d_reveal` | other | yes | 13s | positional: `heroTitle` · named: `heroTitle, outroUrl` |
74
+ | `vfx_iphone_device` | other | yes | 15s | positional: `imageUrl` · named: `imageUrl` |
75
+ | `vpn_youtube_spot` | other | yes | 7s | positional: `headlineLine1` · named: `headlineLine1, headlineLine2, subtext, appTitle, appSubtitle` |
76
+ | `x_post` | social-card | yes | 5s | positional: `displayName, handle` · named: `displayName, handle, body, hashtag, timestamp` |
77
+ | `yt_lower_third` | lower-third | yes | 5s | positional: `channel, subscribers` · named: `channel, subscribers, avatarUrl` |
78
+
79
+ ## Unverified blocks
80
+
81
+ Blocks with `verified: no` vendor upstream demo content and may ignore user params. `scenerok validate` warns but does not error.
82
+
83
+ ## Usage
84
+
85
+ ```vidscript
86
+ [0s .. 6s] = animations.code_typing("const x = 1;")
87
+ ```
88
+
89
+ See `animations-picker.md` for intent-based selection. Preview samples: https://scenerok.com/catalog#animations
@@ -0,0 +1,77 @@
1
+ # Animation Block Picker
2
+
3
+ `import animations from "@scenerok/animations"` surfaces the vendored HyperFrames block catalog as positionable, time-windowed VidScript surfaces. Each `animations.<fn>(...)` call resolves to a `hyperframes` IR surface; no LLM runs at render time.
4
+
5
+ **Reuse-first:** prefer `animations.<fn>(...)` over hand-authoring composition HTML. Full per-fn param shapes: `animations-catalog.md`.
6
+
7
+ ## Intent → `animations.<fn>(...)`
8
+
9
+ | Intent | Family | `animations.<fn>(…)` | Positional args (in order) |
10
+ | --- | --- | --- | --- |
11
+ | typing / live code walk | code-anim | `code_typing` | `code`, `lang`, `theme`, `filename` |
12
+ | scroll through a file | code-anim | `code_scroll` | `code`, `lang`, `theme` |
13
+ | spotlight a line | code-anim | `code_highlight` | `code`, `lang`, `theme` |
14
+ | 3D extrude / shader dissolve / particle title | code-anim | `code_3d_extrude` · `code_shader_dissolve` · `code_particle_assemble` | `code`, `theme` |
15
+ | assemble snippets in flight | code-anim | `code_snippet_flight` | `code`, `theme` |
16
+ | before/after code change | code-anim | `code_morph` · `code_diff` | `from`, `to`, `lang`, `theme` |
17
+ | themed VS Code window | code-snippet | 24 `code_snippet_*` themes | `code`, `filename` |
18
+ | Apple Terminal command + output | code-snippet | 12 `code_snippet_apple_terminal_*` | `command`, `output`, `prompt`, `title` |
19
+ | broadcast name / title bar | lower-third | `lt_clean_bar` · `lt_accent_underline` · `lt_kicker_name` · `lt_mask_reveal` · `lt_stack_bars` | `title`, `subtitle`, `accent?` |
20
+ | lower-third with kicker | lower-third | `lt_kicker_name` · `lt_side_rule` | `kicker`, `title`, `accent?` |
21
+ | YouTube channel card | lower-third | `yt_lower_third` | `channel`, `subscribers`, `avatarUrl?` |
22
+ | Bild-style brand accent | lower-third | `lower_third_bild` | `title`, `subtitle`, `accent?` |
23
+ | inline text over footage | text-overlay | `lower_third` · `blend_caption` | inline `{{title}}` / `{{subtitle}}` tokens |
24
+ | X / Twitter post | social-card | `x_post` | `displayName`, `handle`, `body?`, `hashtag?`, `timestamp?` |
25
+ | Reddit thread | social-card | `reddit_post` | `subreddit`, `title`, `body?`, `author?`, `votes?` |
26
+ | TikTok / Instagram follow | social-card | `tiktok_follow` · `instagram_follow` | `displayName`, `handle`, `avatarUrl?`, `url?` |
27
+ | Spotify now-playing | social-card | `spotify_card` | `track`, `artist` |
28
+ | macOS notification | social-card | `macos_notification` | `appName`, `title`, `body?`, `timeLabel?` |
29
+ | news ticker / headline crawl | social-card | `news_ticker` | `headline`, `headlineAccent?`, `ticker?`, `desk?`, `time?` |
30
+ | logo outro / brand sting | brand | `logo_outro` | `tagline`, `url?` |
31
+ | punchy kinetic title (no block) | kinetic-type | (use `text` + `motion.*` — see `text-styles.md`) | — |
32
+
33
+ Param resolution: **named > positional > default**.
34
+
35
+ ## Gotchas
36
+
37
+ | Block / family | Issue | Fix |
38
+ | --- | --- | --- |
39
+ | `code_diff` | Line indices may be 0-based in adapter | Match sample in `animations-catalog.md` |
40
+ | Unverified blocks | `scenerok validate` warns `verified:false` | Accept demo render or switch to verified family |
41
+ | Maps / liquid-glass / app-showcase | Ignore user params | Use for demo aesthetic only, or hand-author |
42
+ | Adapter blocks | No inline audio/text siblings | Put `audio`, `text`, `filter` on separate time blocks |
43
+
44
+ ## Two-phase compose recipe
45
+
46
+ **Part 1 — classify (before probing assets):**
47
+
48
+ 1. Decide intent family from the table above.
49
+ 2. Pick `animations.<fn>` (or `text` + `motion.*` for kinetic-type).
50
+ 3. Note param values from the user request.
51
+ 4. Draft time blocks: `[<start>s .. <end>s] = animations.<fn>(...)`.
52
+
53
+ **Part 2 — compose (after probing assets):**
54
+
55
+ 1. Probe assets (logo, avatar, code) — never guess dimensions.
56
+ 2. Resolve params: named override positional; defaults fill the rest.
57
+ 3. Emit VidScript call.
58
+ 4. Adjacent `audio`, `text`, `filter` on **separate** time blocks.
59
+ 5. Run `scenerok validate script.vid`.
60
+
61
+ ## Demo script
62
+
63
+ ```vidscript
64
+ import animations from "@scenerok/animations"
65
+ import tr from "@scenerok/transitions"
66
+ output to "demo.mp4", resolution: "1920x1080", fps: 30
67
+
68
+ [0s .. 6s] = animations.code_typing("let abc = 23; abc += 10; console.log(abc);")
69
+ [6s .. 6.6s] = tr.dissolve()
70
+ [6.6s .. 13s] = animations.code_morph("const a = 1", "const a = 2")
71
+ [13s .. 19s] = animations.code_typing(code: "const z = 5;", duration: 6, filename: "app.ts", theme: "monokai")
72
+ [19s .. 24s] = animations.lt_clean_bar("Ada Lovelace", "Engineer", accent: "#00ff00")
73
+ [24s .. 29s] = animations.x_post("SceneRok", "@scenerok", body: "Shipped adapters.", hashtag: "#v1")
74
+ [29s .. 34s] = animations.news_ticker("Tech Desk", time: "15:30 UTC",
75
+ headline: "SceneRok ships", headlineAccent: "53 verified blocks",
76
+ ticker: "Verified count: 53/78\nBeta opens July")
77
+ ```