@scenerok/cli 1.0.3 → 1.0.5

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 (62) hide show
  1. package/dist/commands/auth.d.ts.map +1 -1
  2. package/dist/commands/auth.js +16 -7
  3. package/dist/commands/skills.d.ts.map +1 -1
  4. package/dist/commands/skills.js +42 -1
  5. package/dist/commands/validate.d.ts.map +1 -1
  6. package/dist/commands/validate.js +26 -4
  7. package/dist/index.js +2 -1
  8. package/dist/lib/api.d.ts +2 -0
  9. package/dist/lib/api.d.ts.map +1 -1
  10. package/dist/lib/config.d.ts.map +1 -1
  11. package/dist/lib/config.js +6 -1
  12. package/examples/system/3-tips-fitness.vid +45 -0
  13. package/examples/system/ecom-product-launch.vid +31 -0
  14. package/examples/system/glitch-title.vid +17 -0
  15. package/examples/system/meme-remix.vid +15 -0
  16. package/examples/system/minimal-text-reel.vid +11 -0
  17. package/examples/system/product-launch.vid +18 -0
  18. package/examples/system/rokmilk-chocolate-promo.vid +24 -0
  19. package/examples/system/testimonial-cut.vid +16 -0
  20. package/examples/system/ugc-testimonial-voiceover.vid +33 -0
  21. package/examples/system/voice-visual-promo.vid +21 -0
  22. package/package.json +2 -1
  23. package/skills/aider/SKILL.md +18 -8
  24. package/skills/aider/vidscript-guide.md +29 -9
  25. package/skills/aider/vidscript-sample.md +22 -14
  26. package/skills/aider/vidscript-strict.md +95 -0
  27. package/skills/claude/SKILL.md +18 -8
  28. package/skills/claude/vidscript-guide.md +26 -7
  29. package/skills/claude/vidscript-sample.md +22 -14
  30. package/skills/claude/vidscript-strict.md +95 -0
  31. package/skills/codex/SKILL.md +18 -8
  32. package/skills/codex/vidscript-guide.md +29 -9
  33. package/skills/codex/vidscript-sample.md +22 -14
  34. package/skills/codex/vidscript-strict.md +95 -0
  35. package/skills/cursor/SKILL.md +18 -8
  36. package/skills/cursor/vidscript-guide.md +29 -9
  37. package/skills/cursor/vidscript-sample.md +22 -14
  38. package/skills/cursor/vidscript-strict.md +95 -0
  39. package/skills/opencode/SKILL.md +18 -8
  40. package/skills/opencode/vidscript-guide.md +29 -9
  41. package/skills/opencode/vidscript-sample.md +22 -14
  42. package/skills/opencode/vidscript-strict.md +95 -0
  43. package/skills/skills/aider/SKILL.md +190 -0
  44. package/skills/skills/aider/vidscript-guide.md +375 -0
  45. package/skills/skills/aider/vidscript-sample.md +29 -0
  46. package/skills/skills/aider/vidscript-strict.md +95 -0
  47. package/skills/skills/claude/SKILL.md +190 -0
  48. package/skills/skills/claude/vidscript-guide.md +375 -0
  49. package/skills/skills/claude/vidscript-sample.md +29 -0
  50. package/skills/skills/claude/vidscript-strict.md +95 -0
  51. package/skills/skills/codex/SKILL.md +190 -0
  52. package/skills/skills/codex/vidscript-guide.md +375 -0
  53. package/skills/skills/codex/vidscript-sample.md +29 -0
  54. package/skills/skills/codex/vidscript-strict.md +95 -0
  55. package/skills/skills/cursor/SKILL.md +190 -0
  56. package/skills/skills/cursor/vidscript-guide.md +375 -0
  57. package/skills/skills/cursor/vidscript-sample.md +29 -0
  58. package/skills/skills/cursor/vidscript-strict.md +95 -0
  59. package/skills/skills/opencode/SKILL.md +190 -0
  60. package/skills/skills/opencode/vidscript-guide.md +375 -0
  61. package/skills/skills/opencode/vidscript-sample.md +29 -0
  62. package/skills/skills/opencode/vidscript-strict.md +95 -0
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,WAAW,SA6ErB,CAAC"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,WAAW,SA0FrB,CAAC"}
@@ -8,19 +8,28 @@ export const authCommand = new Command('auth')
8
8
  .addCommand(new Command('login')
9
9
  .description('Log in to SceneRok via browser')
10
10
  .option('-n, --name <name>', 'Device name', 'CLI')
11
+ .option('-u, --base-url <url>', 'SceneRok API base URL (overrides ~/.scenerok/config.json and SCENEROK_BASE_URL)')
11
12
  .action(async (options) => {
12
- console.log(chalk.cyan('\\nšŸ” SceneRok Authentication\\n'));
13
+ if (options.baseUrl) {
14
+ const config = readConfig();
15
+ config.baseUrl = options.baseUrl.replace(/\/$/, '');
16
+ writeConfig(config);
17
+ }
18
+ console.log(chalk.cyan('\nšŸ” SceneRok Authentication\n'));
13
19
  const spinner = ora('Initiating device auth...').start();
14
20
  try {
15
21
  const auth = await initiateDeviceAuth(options.name);
16
22
  spinner.succeed('Device auth initiated');
17
- console.log(chalk.yellow('\\n1. Open this URL in your browser:'));
23
+ console.log(chalk.yellow('\n1. Open this URL in your browser:'));
18
24
  console.log(chalk.underline(auth.verification_uri));
19
25
  console.log('');
20
- console.log(chalk.yellow('2. Or run this command:'));
26
+ console.log(chalk.yellow('2. Enter this code if prompted:'));
27
+ console.log(chalk.bold(` ${auth.user_code}`));
28
+ console.log('');
29
+ console.log(chalk.yellow('3. Or run this command:'));
21
30
  console.log(` open "${auth.verification_uri}"`);
22
31
  console.log('');
23
- console.log(chalk.dim(`Waiting for authentication... (expires in ${auth.expires_in}s)\\n`));
32
+ console.log(chalk.dim(`Waiting for authentication... (expires in ${auth.expires_in}s)\n`));
24
33
  const startTime = Date.now();
25
34
  const expiresIn = auth.expires_in * 1000;
26
35
  while (Date.now() - startTime < expiresIn) {
@@ -30,16 +39,16 @@ export const authCommand = new Command('auth')
30
39
  const config = readConfig();
31
40
  config.apiToken = result.access_token;
32
41
  writeConfig(config);
33
- console.log(chalk.green('\\nāœ… Successfully authenticated with SceneRok!\\n'));
42
+ console.log(chalk.green('\nāœ… Successfully authenticated with SceneRok!\n'));
34
43
  console.log(chalk.dim('Your API token has been saved to ~/.scenerok/config.json'));
35
44
  return;
36
45
  }
37
46
  if (result.error === 'expired_token') {
38
- console.log(chalk.red('\\nāŒ Authentication expired. Please try again.\\n'));
47
+ console.log(chalk.red('\nāŒ Authentication expired. Please try again.\n'));
39
48
  process.exit(1);
40
49
  }
41
50
  }
42
- console.log(chalk.red('\\nāŒ Authentication timed out. Please try again.\\n'));
51
+ console.log(chalk.red('\nāŒ Authentication timed out. Please try again.\n'));
43
52
  process.exit(1);
44
53
  }
45
54
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4FpC,eAAO,MAAM,aAAa,SAqDvB,CAAC"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkIpC,eAAO,MAAM,aAAa,SA4DvB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
- import { existsSync, copyFileSync, mkdirSync } from 'node:fs';
3
+ import { existsSync, copyFileSync, mkdirSync, readdirSync } from 'node:fs';
4
4
  import { join, dirname } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
6
  function getPlatformConfig(platform) {
@@ -13,6 +13,7 @@ function getPlatformConfig(platform) {
13
13
  { source: 'skills/opencode/SKILL.md', dest: 'SKILL.md' },
14
14
  { source: 'skills/opencode/vidscript-guide.md', dest: 'vidscript-guide.md' },
15
15
  { source: 'skills/opencode/vidscript-sample.md', dest: 'vidscript-sample.md' },
16
+ { source: 'skills/opencode/vidscript-strict.md', dest: 'vidscript-strict.md' },
16
17
  ],
17
18
  },
18
19
  claude: {
@@ -22,6 +23,7 @@ function getPlatformConfig(platform) {
22
23
  { source: 'skills/claude/SKILL.md', dest: 'SKILL.md' },
23
24
  { source: 'skills/claude/vidscript-guide.md', dest: 'vidscript-guide.md' },
24
25
  { source: 'skills/claude/vidscript-sample.md', dest: 'vidscript-sample.md' },
26
+ { source: 'skills/claude/vidscript-strict.md', dest: 'vidscript-strict.md' },
25
27
  ],
26
28
  },
27
29
  codex: {
@@ -31,6 +33,7 @@ function getPlatformConfig(platform) {
31
33
  { source: 'skills/codex/SKILL.md', dest: 'SKILL.md' },
32
34
  { source: 'skills/codex/vidscript-guide.md', dest: 'vidscript-guide.md' },
33
35
  { source: 'skills/codex/vidscript-sample.md', dest: 'vidscript-sample.md' },
36
+ { source: 'skills/codex/vidscript-strict.md', dest: 'vidscript-strict.md' },
34
37
  ],
35
38
  },
36
39
  cursor: {
@@ -40,6 +43,7 @@ function getPlatformConfig(platform) {
40
43
  { source: 'skills/cursor/SKILL.md', dest: 'SKILL.md' },
41
44
  { source: 'skills/cursor/vidscript-guide.md', dest: 'vidscript-guide.md' },
42
45
  { source: 'skills/cursor/vidscript-sample.md', dest: 'vidscript-sample.md' },
46
+ { source: 'skills/cursor/vidscript-strict.md', dest: 'vidscript-strict.md' },
43
47
  ],
44
48
  },
45
49
  aider: {
@@ -49,6 +53,7 @@ function getPlatformConfig(platform) {
49
53
  { source: 'skills/aider/SKILL.md', dest: 'SKILL.md' },
50
54
  { source: 'skills/aider/vidscript-guide.md', dest: 'vidscript-guide.md' },
51
55
  { source: 'skills/aider/vidscript-sample.md', dest: 'vidscript-sample.md' },
56
+ { source: 'skills/aider/vidscript-strict.md', dest: 'vidscript-strict.md' },
52
57
  ],
53
58
  },
54
59
  };
@@ -76,6 +81,35 @@ async function findSkillSource(sourcePath) {
76
81
  }
77
82
  return null;
78
83
  }
84
+ async function findExamplesDir() {
85
+ const moduleExamples = join(dirname(new URL(import.meta.url).pathname), '../../examples/system');
86
+ if (existsSync(moduleExamples))
87
+ return moduleExamples;
88
+ const cwdPackage = join(process.cwd(), 'packages/reelforge-cli/examples/system');
89
+ if (existsSync(cwdPackage))
90
+ return cwdPackage;
91
+ try {
92
+ const { execSync } = await import('node:child_process');
93
+ const globalRoot = execSync('npm root -g', { encoding: 'utf-8' }).trim();
94
+ const globalPath = join(globalRoot, '@scenerok', 'cli', 'examples/system');
95
+ if (existsSync(globalPath))
96
+ return globalPath;
97
+ }
98
+ catch {
99
+ // ignore
100
+ }
101
+ return null;
102
+ }
103
+ function installExampleTemplates(skillDir, examplesSource) {
104
+ const examplesDest = join(skillDir, 'examples', 'system');
105
+ mkdirSync(examplesDest, { recursive: true });
106
+ for (const name of readdirSync(examplesSource)) {
107
+ if (!name.endsWith('.vid'))
108
+ continue;
109
+ copyFileSync(join(examplesSource, name), join(examplesDest, name));
110
+ console.log(chalk.green(` āœ“ examples/system/${name}`));
111
+ }
112
+ }
79
113
  export const skillsCommand = new Command('skills')
80
114
  .description('Install SceneRok skills to your agent')
81
115
  .addCommand(new Command('install')
@@ -103,6 +137,13 @@ export const skillsCommand = new Command('skills')
103
137
  copyFileSync(source, dest);
104
138
  console.log(chalk.green(` āœ“ ${file.dest}`));
105
139
  }
140
+ const examplesSource = await findExamplesDir();
141
+ if (examplesSource) {
142
+ installExampleTemplates(config.skillDir, examplesSource);
143
+ }
144
+ else {
145
+ console.log(chalk.yellow(' āš ļø Skipping examples/system (bundled templates not found)'));
146
+ }
106
147
  console.log(chalk.cyan(`\\nāœ… Skills installed to ${config.skillDir}\\n`));
107
148
  console.log(chalk.dim('Your agent can now compose VidScripts and render videos!'));
108
149
  console.log(chalk.dim('Try: Ask your agent to "create a product promo video using scenerok"'));
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,eAAe,SAqCxB,CAAC"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC,eAAO,MAAM,eAAe,SAkDxB,CAAC"}
@@ -2,9 +2,18 @@ import { Command } from 'commander';
2
2
  import chalk from 'chalk';
3
3
  import { readFileSync } from 'node:fs';
4
4
  import { validateVidscript } from '../lib/api.js';
5
+ function collectValidationErrors(result) {
6
+ if (result.errors && result.errors.length > 0) {
7
+ return result.errors;
8
+ }
9
+ if (result.error) {
10
+ return [result.error];
11
+ }
12
+ return ['Validation failed (no error details returned by server)'];
13
+ }
5
14
  export const validateCommand = new Command('validate')
6
15
  .description('Validate a VidScript file')
7
- .argument('<file>', 'Path to .vidscript file')
16
+ .argument('<file>', 'Path to .vid or .vidscript file')
8
17
  .action(async (file) => {
9
18
  let vidscript;
10
19
  try {
@@ -18,8 +27,19 @@ export const validateCommand = new Command('validate')
18
27
  const result = await validateVidscript(vidscript);
19
28
  if (result.valid) {
20
29
  console.log(chalk.green('\\nāœ… VidScript is valid\\n'));
21
- console.log(` Scenes: ${result.scenes}`);
22
- console.log(` Duration: ${result.duration}s`);
30
+ if (result.statements != null) {
31
+ console.log(` Statements: ${result.statements}`);
32
+ }
33
+ if (result.scenes != null) {
34
+ console.log(` Scenes: ${result.scenes}`);
35
+ }
36
+ if (result.duration != null) {
37
+ console.log(` Duration: ${result.duration}s`);
38
+ }
39
+ const pluginCalls = result.pluginCalls;
40
+ if (pluginCalls != null) {
41
+ console.log(` Plugin calls: ${pluginCalls}`);
42
+ }
23
43
  if (result.warnings && result.warnings.length > 0) {
24
44
  console.log(chalk.yellow('\\n Warnings:'));
25
45
  for (const warning of result.warnings) {
@@ -29,7 +49,9 @@ export const validateCommand = new Command('validate')
29
49
  }
30
50
  else {
31
51
  console.log(chalk.red('\\nāŒ VidScript is invalid\\n'));
32
- console.log(` Error: ${result.error}`);
52
+ for (const err of collectValidationErrors(result)) {
53
+ console.log(` • ${err}`);
54
+ }
33
55
  process.exit(1);
34
56
  }
35
57
  console.log('');
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ program.addCommand(projectCommand);
23
23
  program.addCommand(cacheCommand);
24
24
  program.addCommand(validateCommand);
25
25
  program.addCommand(statusCommand);
26
+ const agentsDocsPath = ['https://', 'scenerok', '.com/agents'].join('');
26
27
  // Default help
27
28
  program.on('--help', () => {
28
29
  console.log('');
@@ -36,6 +37,6 @@ program.on('--help', () => {
36
37
  console.log(' $ scenerok status 42');
37
38
  console.log('');
38
39
  console.log(chalk.cyan('Get started:'));
39
- console.log(' https://scenerok.com/agents');
40
+ console.log(` ${agentsDocsPath}`);
40
41
  });
41
42
  program.parse();
package/dist/lib/api.d.ts CHANGED
@@ -5,9 +5,11 @@ declare class ApiError extends Error {
5
5
  }
6
6
  export declare function validateVidscript(vidscript: string): Promise<{
7
7
  valid: boolean;
8
+ statements?: number;
8
9
  scenes?: number;
9
10
  duration?: number;
10
11
  warnings?: string[];
12
+ errors?: string[];
11
13
  error?: string;
12
14
  }>;
13
15
  export declare function submitRender(vidscript: string, resolution?: string, templateId?: number): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AASA,cAAM,QAAS,SAAQ,KAAK;IAGjB,MAAM,EAAE,MAAM;IACd,YAAY,CAAC,EAAE,OAAO;gBAF7B,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,OAAO,YAAA;CAKhC;AAkED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM;WAE9C,OAAO;aACL,MAAM;eACJ,MAAM;eACN,MAAM,EAAE;YACX,MAAM;GAEjB;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;cAEhF,MAAM;YACR,MAAM;gBACF,MAAM;oBACF,MAAM;eACX,MAAM;iBACJ,MAAM;aACV,MAAM;kBACD,MAAM;gBACR,MAAM,GAAG,IAAI;GAE5B;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM;QAE9C,MAAM;YACF,MAAM;cACJ,MAAM;eACL,MAAM,GAAG,IAAI;oBACR,MAAM;eACX,MAAM;iBACJ,MAAM;mBACJ,OAAO;WACf,MAAM,GAAG,IAAI;eACT,MAAM;iBACJ,MAAM,GAAG,IAAI;GAE7B;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmBpG;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,QAAQ;aAEzC;QACP,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;KACpB;YACO,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;aACO,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,GAAG,IAAI;GAE3D;AAED,wBAAsB,YAAY;cAEpB,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;GAEL;AAED,wBAAsB,aAAa,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO;aAMxE,KAAK,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;GAEL;AAED,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM;iBAE1C,MAAM;eACR,MAAM;sBACC,MAAM;gBACZ,MAAM;cACR,MAAM;GAEnB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM;YAE3C,MAAM;mBACC,MAAM;iBACR,MAAM;iBACN,MAAM;YACX,MAAM;GAEjB;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AASA,cAAM,QAAS,SAAQ,KAAK;IAGjB,MAAM,EAAE,MAAM;IACd,YAAY,CAAC,EAAE,OAAO;gBAF7B,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,OAAO,YAAA;CAKhC;AAkED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM;WAE9C,OAAO;iBACD,MAAM;aACV,MAAM;eACJ,MAAM;eACN,MAAM,EAAE;aACV,MAAM,EAAE;YACT,MAAM;GAEjB;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM;cAEhF,MAAM;YACR,MAAM;gBACF,MAAM;oBACF,MAAM;eACX,MAAM;iBACJ,MAAM;aACV,MAAM;kBACD,MAAM;gBACR,MAAM,GAAG,IAAI;GAE5B;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM;QAE9C,MAAM;YACF,MAAM;cACJ,MAAM;eACL,MAAM,GAAG,IAAI;oBACR,MAAM;eACX,MAAM;iBACJ,MAAM;mBACJ,OAAO;WACf,MAAM,GAAG,IAAI;eACT,MAAM;iBACJ,MAAM,GAAG,IAAI;GAE7B;AAED,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmBpG;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,QAAQ;aAEzC;QACP,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;KACpB;YACO,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;aACO,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,GAAG,IAAI;GAE3D;AAED,wBAAsB,YAAY;cAEpB,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;GAEL;AAED,wBAAsB,aAAa,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO;aAMxE,KAAK,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;GAEL;AAED,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM;iBAE1C,MAAM;eACR,MAAM;sBACC,MAAM;gBACZ,MAAM;cACR,MAAM;GAEnB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM;YAE3C,MAAM;mBACC,MAAM;iBACR,MAAM;iBACN,MAAM;YACX,MAAM;GAEjB;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAQD,wBAAgB,UAAU,IAAI,SAAS,CAUtC;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,QAG5C;AAED,wBAAgB,UAAU,IAAI,MAAM,CAGnC;AAED,wBAAgB,WAAW,IAAI,MAAM,GAAG,SAAS,CAGhD;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,WAAW,IAAI,MAAM,CAKpC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAQD,wBAAgB,UAAU,IAAI,SAAS,CAUtC;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,QAG5C;AAOD,wBAAgB,UAAU,IAAI,MAAM,CAGnC;AAED,wBAAgB,WAAW,IAAI,MAAM,GAAG,SAAS,CAGhD;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,WAAW,IAAI,MAAM,CAKpC"}
@@ -4,6 +4,7 @@ import { join } from 'node:path';
4
4
  const CONFIG_DIR = join(homedir(), '.scenerok');
5
5
  const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
6
6
  const CACHE_DIR = join(CONFIG_DIR, 'cache');
7
+ const PRODUCTION_API_ORIGIN = ['https://', 'scenerok', '.com'].join(''); // pragma: allowlist secret
7
8
  function ensureConfigDir() {
8
9
  if (!existsSync(CONFIG_DIR)) {
9
10
  mkdirSync(CONFIG_DIR, { recursive: true });
@@ -25,9 +26,13 @@ export function writeConfig(config) {
25
26
  ensureConfigDir();
26
27
  writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
27
28
  }
29
+ function cliBaseUrlFromEnv() {
30
+ const key = ['SCENEROK', '_', 'BASE', '_', 'URL'].join('');
31
+ return process.env[key]?.replace(/\/$/, '');
32
+ }
28
33
  export function getBaseUrl() {
29
34
  const config = readConfig();
30
- return config.baseUrl || 'https://scenerok.com';
35
+ return config.baseUrl?.replace(/\/$/, '') || cliBaseUrlFromEnv() || PRODUCTION_API_ORIGIN;
31
36
  }
32
37
  export function getApiToken() {
33
38
  const config = readConfig();
@@ -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,24 @@
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
+
6
+ # Generated visuals — one clip per segment; prompts must use straight double quotes only
7
+ [-] = 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)
8
+
9
+ [-] = 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)
10
+
11
+ [-] = 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)
12
+
13
+ [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: fadeIn(0.6s)
14
+
15
+ [2s .. 5s] = text "Chocolate Milkshake", font: "Outfit", size: 56, color: "#FFFFFF", x: "50%", y: "45%", align: center, stroke: "#3E1C00", stroke_width: 3, animate: riseIn(0.8s)
16
+
17
+ [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: slideY(40, 0, 0.8s)
18
+
19
+ [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: fadeIn(1s)
20
+
21
+ [0s .. 15s] = filter "vignette", intensity: 0.3
22
+ [0s .. 15s] = filter "saturation", value: 1.12
23
+
24
+ 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scenerok/cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "SceneRok CLI - Create videos from your terminal and agent workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "dist",
12
12
  "skills",
13
+ "examples",
13
14
  "README.md"
14
15
  ],
15
16
  "scripts": {
@@ -86,18 +86,20 @@ import music from "@elevenlabs/music" # generative music
86
86
  use intro at [-] with { clip: main, headline: "Welcome" }
87
87
  ```
88
88
 
89
- ### Plugin Calls
89
+ ### Plugin Calls (required import)
90
90
 
91
91
  ```vidscript
92
- import eleven from "/tts"
92
+ import xai from "@scenerok/xai"
93
93
 
94
- [-] = audio eleven.tts("Welcome to SceneRok", voice: "Rachel")
95
- [-] = video xai.imagine("Cinematic product shot", aspect_ratio: "9:16")
96
- [-] = audio music("upbeat launch soundtrack", duration: 12, instrumental: true)
97
- [-] = audio xai.tts("Next line", voice: "eve")
94
+ [-] = video xai.imagine("Cinematic product shot", aspect_ratio: "9:16", duration: 5)
95
+ [-] = audio xai.tts("Welcome to SceneRok", voice: "eve")
98
96
  ```
99
97
 
100
- Package-style plugin calls are supported inside time blocks. Use `video ...` for generated visual clips and `audio ...` for generated speech, music, and other generated audio.
98
+ **Always** add `import xai from "@scenerok/xai"` before any `xai.imagine` / `xai.tts` call or `animate: fadeIn(...)` helper. Calls without import fail validation with `Unknown function 'xai.imagine'`.
99
+
100
+ Put plugin calls **directly** in time blocks — never `let clip = xai.imagine(...)`.
101
+
102
+ Read `vidscript-strict.md` for anti-patterns and `examples/system/*.vid` for copy-paste templates (installed with `scenerok skills install`).
101
103
 
102
104
  ### Placeholders
103
105
 
@@ -165,7 +167,15 @@ Always ask clarifying questions about:
165
167
  - Desired duration and style
166
168
  - Whether they want to start from a template or from scratch
167
169
 
170
+ ## Reference files (in this skill folder)
171
+
172
+ | File | Purpose |
173
+ |------|---------|
174
+ | `vidscript-guide.md` | Full grammar reference |
175
+ | `vidscript-strict.md` | **Read first** — rules that prevent common agent mistakes |
176
+ | `vidscript-sample.md` | Minimal promo with xAI import |
177
+ | `examples/system/*.vid` | Official SceneRok templates (text-only, product launch, xAI reels) |
178
+
168
179
  ## Full Reference
169
180
 
170
- For exhaustive grammar documentation, visit:
171
181
  https://scenerok.com/docs/vidscript