@vibeframe/cli 0.27.0 → 0.29.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 (109) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent/adapters/index.d.ts +1 -0
  3. package/dist/agent/adapters/index.d.ts.map +1 -1
  4. package/dist/agent/adapters/index.js +5 -0
  5. package/dist/agent/adapters/index.js.map +1 -1
  6. package/dist/agent/adapters/openrouter.d.ts +16 -0
  7. package/dist/agent/adapters/openrouter.d.ts.map +1 -0
  8. package/dist/agent/adapters/openrouter.js +100 -0
  9. package/dist/agent/adapters/openrouter.js.map +1 -0
  10. package/dist/agent/types.d.ts +1 -1
  11. package/dist/agent/types.d.ts.map +1 -1
  12. package/dist/commands/agent.d.ts.map +1 -1
  13. package/dist/commands/agent.js +3 -1
  14. package/dist/commands/agent.js.map +1 -1
  15. package/dist/commands/setup.js +5 -2
  16. package/dist/commands/setup.js.map +1 -1
  17. package/dist/config/schema.d.ts +2 -1
  18. package/dist/config/schema.d.ts.map +1 -1
  19. package/dist/config/schema.js +2 -0
  20. package/dist/config/schema.js.map +1 -1
  21. package/dist/index.js +0 -0
  22. package/package.json +16 -12
  23. package/.turbo/turbo-build.log +0 -4
  24. package/.turbo/turbo-lint.log +0 -21
  25. package/.turbo/turbo-test.log +0 -689
  26. package/src/agent/adapters/claude.ts +0 -143
  27. package/src/agent/adapters/gemini.ts +0 -159
  28. package/src/agent/adapters/index.ts +0 -61
  29. package/src/agent/adapters/ollama.ts +0 -231
  30. package/src/agent/adapters/openai.ts +0 -116
  31. package/src/agent/adapters/xai.ts +0 -119
  32. package/src/agent/index.ts +0 -251
  33. package/src/agent/memory/index.ts +0 -151
  34. package/src/agent/prompts/system.ts +0 -106
  35. package/src/agent/tools/ai-editing.ts +0 -845
  36. package/src/agent/tools/ai-generation.ts +0 -1073
  37. package/src/agent/tools/ai-pipeline.ts +0 -1055
  38. package/src/agent/tools/ai.ts +0 -21
  39. package/src/agent/tools/batch.ts +0 -429
  40. package/src/agent/tools/e2e.test.ts +0 -545
  41. package/src/agent/tools/export.ts +0 -184
  42. package/src/agent/tools/filesystem.ts +0 -237
  43. package/src/agent/tools/index.ts +0 -150
  44. package/src/agent/tools/integration.test.ts +0 -775
  45. package/src/agent/tools/media.ts +0 -697
  46. package/src/agent/tools/project.ts +0 -313
  47. package/src/agent/tools/timeline.ts +0 -951
  48. package/src/agent/types.ts +0 -68
  49. package/src/commands/agent.ts +0 -340
  50. package/src/commands/ai-analyze.ts +0 -429
  51. package/src/commands/ai-animated-caption.ts +0 -390
  52. package/src/commands/ai-audio.ts +0 -941
  53. package/src/commands/ai-broll.ts +0 -490
  54. package/src/commands/ai-edit-cli.ts +0 -658
  55. package/src/commands/ai-edit.ts +0 -1542
  56. package/src/commands/ai-fill-gaps.ts +0 -566
  57. package/src/commands/ai-helpers.ts +0 -65
  58. package/src/commands/ai-highlights.ts +0 -1303
  59. package/src/commands/ai-image.ts +0 -761
  60. package/src/commands/ai-motion.ts +0 -347
  61. package/src/commands/ai-narrate.ts +0 -451
  62. package/src/commands/ai-review.ts +0 -309
  63. package/src/commands/ai-script-pipeline-cli.ts +0 -1710
  64. package/src/commands/ai-script-pipeline.ts +0 -1365
  65. package/src/commands/ai-suggest-edit.ts +0 -264
  66. package/src/commands/ai-video-fx.ts +0 -445
  67. package/src/commands/ai-video.ts +0 -915
  68. package/src/commands/ai-viral.ts +0 -595
  69. package/src/commands/ai-visual-fx.ts +0 -601
  70. package/src/commands/ai.test.ts +0 -627
  71. package/src/commands/ai.ts +0 -307
  72. package/src/commands/analyze.ts +0 -282
  73. package/src/commands/audio.ts +0 -644
  74. package/src/commands/batch.test.ts +0 -279
  75. package/src/commands/batch.ts +0 -440
  76. package/src/commands/detect.ts +0 -329
  77. package/src/commands/doctor.ts +0 -237
  78. package/src/commands/edit-cmd.ts +0 -1014
  79. package/src/commands/export.ts +0 -918
  80. package/src/commands/generate.ts +0 -2146
  81. package/src/commands/media.ts +0 -177
  82. package/src/commands/output.ts +0 -142
  83. package/src/commands/pipeline.ts +0 -398
  84. package/src/commands/project.test.ts +0 -127
  85. package/src/commands/project.ts +0 -149
  86. package/src/commands/sanitize.ts +0 -60
  87. package/src/commands/schema.ts +0 -130
  88. package/src/commands/setup.ts +0 -509
  89. package/src/commands/timeline.test.ts +0 -499
  90. package/src/commands/timeline.ts +0 -529
  91. package/src/commands/validate.ts +0 -77
  92. package/src/config/config.test.ts +0 -197
  93. package/src/config/index.ts +0 -125
  94. package/src/config/schema.ts +0 -82
  95. package/src/engine/index.ts +0 -2
  96. package/src/engine/project.test.ts +0 -702
  97. package/src/engine/project.ts +0 -439
  98. package/src/index.ts +0 -146
  99. package/src/utils/api-key.test.ts +0 -41
  100. package/src/utils/api-key.ts +0 -247
  101. package/src/utils/audio.ts +0 -83
  102. package/src/utils/exec-safe.ts +0 -75
  103. package/src/utils/first-run.ts +0 -52
  104. package/src/utils/provider-resolver.ts +0 -56
  105. package/src/utils/remotion.ts +0 -951
  106. package/src/utils/subtitle.test.ts +0 -227
  107. package/src/utils/subtitle.ts +0 -169
  108. package/src/utils/tty.ts +0 -196
  109. package/tsconfig.json +0 -20
@@ -1,347 +0,0 @@
1
- /**
2
- * @module ai-motion
3
- * @description Motion graphics render and composite command.
4
- *
5
- * ## Commands: vibe ai motion
6
- * ## Dependencies: Claude, Gemini, Remotion
7
- *
8
- * Extracted from ai.ts as part of modularisation.
9
- * ai.ts re-exports all public types and functions from this module.
10
- * @see MODELS.md for AI model configuration
11
- */
12
-
13
- import { type Command } from 'commander';
14
- import { resolve } from 'node:path';
15
- import { existsSync } from 'node:fs';
16
- import { readFile, writeFile } from 'node:fs/promises';
17
- import chalk from 'chalk';
18
- import ora from 'ora';
19
- import { ClaudeProvider, GeminiProvider } from '@vibeframe/ai-providers';
20
- import { getApiKey } from '../utils/api-key.js';
21
-
22
- // ── Motion: exported function for Agent tool ────────────────────────────────
23
-
24
- export interface MotionCommandOptions {
25
- description: string;
26
- duration?: number;
27
- width?: number;
28
- height?: number;
29
- fps?: number;
30
- style?: string;
31
- /** If set, render the generated code with Remotion */
32
- render?: boolean;
33
- /** Base video to composite the motion graphic onto */
34
- video?: string;
35
- /** Image to analyze with Gemini — color/mood/composition fed into Claude prompt */
36
- image?: string;
37
- /** Path to existing TSX file to refine instead of generating from scratch */
38
- fromTsx?: string;
39
- /**
40
- * LLM model alias for code generation.
41
- * sonnet (default) | opus | gemini | gemini-3.1-pro
42
- */
43
- model?: string;
44
- /** Output path (TSX if code-only, WebM/MP4 if rendered) */
45
- output?: string;
46
- }
47
-
48
- export interface MotionCommandResult {
49
- success: boolean;
50
- codePath?: string;
51
- renderedPath?: string;
52
- compositedPath?: string;
53
- componentName?: string;
54
- error?: string;
55
- }
56
-
57
- // Map model alias → { provider, modelId }
58
- const MODEL_MAP: Record<string, { provider: "claude" | "gemini"; modelId: string }> = {
59
- sonnet: { provider: "claude", modelId: "claude-sonnet-4-6" },
60
- opus: { provider: "claude", modelId: "claude-opus-4-6" },
61
- gemini: { provider: "gemini", modelId: "gemini-2.5-pro" },
62
- "gemini-3.1-pro": { provider: "gemini", modelId: "gemini-3.1-pro-preview" },
63
- };
64
-
65
- export async function executeMotion(options: MotionCommandOptions): Promise<MotionCommandResult> {
66
- const modelAlias = options.model || "sonnet";
67
- const modelConfig = MODEL_MAP[modelAlias] ?? MODEL_MAP["sonnet"];
68
- const useGemini = modelConfig.provider === "gemini";
69
-
70
- const width = options.width || 1920;
71
- const height = options.height || 1080;
72
- const fps = options.fps || 30;
73
- const duration = options.duration || 5;
74
-
75
- // Resolve API key based on provider
76
- let apiKey: string | null;
77
- if (useGemini) {
78
- apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
79
- if (!apiKey) return { success: false, error: "GOOGLE_API_KEY required for Gemini motion generation. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
80
- } else {
81
- apiKey = await getApiKey("ANTHROPIC_API_KEY", "Anthropic");
82
- if (!apiKey) return { success: false, error: "ANTHROPIC_API_KEY required for Claude motion generation. Run 'vibe setup' or set ANTHROPIC_API_KEY in .env" };
83
- }
84
-
85
- // Step 0 (optional): Analyze image with Gemini, inject into description
86
- let enrichedDescription = options.description;
87
- if (options.image) {
88
- const geminiApiKey = await getApiKey("GOOGLE_API_KEY", "Google");
89
- if (!geminiApiKey) {
90
- return { success: false, error: "GOOGLE_API_KEY required for image analysis (--image). Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
91
- }
92
-
93
- const imagePath = resolve(process.cwd(), options.image);
94
- const imageBuffer = await readFile(imagePath);
95
-
96
- const gemini = new GeminiProvider();
97
- await gemini.initialize({ apiKey: geminiApiKey });
98
-
99
- const analysisResult = await gemini.analyzeImage(imageBuffer, `Analyze this image for motion graphics design purposes. Describe:
100
- 1. Dominant color palette (exact hex values if possible)
101
- 2. Subject placement and safe zones (where NOT to put text/graphics)
102
- 3. Overall mood and atmosphere
103
- 4. Lighting style (warm/cool, bright/dark, dramatic/soft)
104
- 5. Key visual elements and their positions
105
-
106
- Be specific and concise — this analysis will guide a Remotion animation generator.`);
107
-
108
- if (analysisResult.success && analysisResult.response) {
109
- enrichedDescription = `${options.description}
110
-
111
- [Image Analysis Context]
112
- ${analysisResult.response}
113
-
114
- Use this image analysis to inform the color palette, typography placement, and overall aesthetic of the motion graphic.`;
115
- }
116
- }
117
-
118
- type MotionResult = Awaited<ReturnType<InstanceType<typeof ClaudeProvider>["generateMotion"]>>;
119
- let result: MotionResult;
120
-
121
- if (options.fromTsx) {
122
- // Refine mode: modify existing TSX instead of generating from scratch
123
- const tsxPath = resolve(process.cwd(), options.fromTsx);
124
- if (!existsSync(tsxPath)) {
125
- return { success: false, error: `TSX file not found: ${tsxPath}` };
126
- }
127
- const existingCode = await readFile(tsxPath, "utf-8");
128
-
129
- if (useGemini) {
130
- const gemini = new GeminiProvider();
131
- await gemini.initialize({ apiKey });
132
- result = await gemini.refineMotion(existingCode, options.description, {
133
- duration, width, height, fps, model: modelConfig.modelId,
134
- });
135
- } else {
136
- const claude = new ClaudeProvider();
137
- await claude.initialize({ apiKey });
138
- result = await claude.refineMotion(existingCode, options.description, {
139
- duration, width, height, fps,
140
- });
141
- }
142
- } else {
143
- if (useGemini) {
144
- const gemini = new GeminiProvider();
145
- await gemini.initialize({ apiKey });
146
- result = await gemini.generateMotion(enrichedDescription, {
147
- duration, width, height, fps,
148
- style: options.style,
149
- model: modelConfig.modelId,
150
- });
151
- } else {
152
- const claude = new ClaudeProvider();
153
- await claude.initialize({ apiKey });
154
- result = await claude.generateMotion(enrichedDescription, {
155
- duration, width, height, fps,
156
- style: options.style as "minimal" | "corporate" | "playful" | "cinematic" | undefined,
157
- });
158
- }
159
- }
160
-
161
- if (!result.success || !result.component) {
162
- return { success: false, error: result.error || "Motion generation failed" };
163
- }
164
-
165
- const { component } = result;
166
- const defaultOutput = (options.video || options.image) ? "motion-output.mp4" : options.render ? "motion.webm" : "motion.tsx";
167
- const outputPath = resolve(process.cwd(), options.output || defaultOutput);
168
-
169
- // Save TSX code
170
- const codePath = outputPath.replace(/\.\w+$/, ".tsx");
171
- await writeFile(codePath, component.code, "utf-8");
172
-
173
- const shouldRender = options.render || !!options.video || !!options.image;
174
- if (!shouldRender) {
175
- return { success: true, codePath, componentName: component.name };
176
- }
177
-
178
- // Render (and optionally composite onto video)
179
- const {
180
- ensureRemotionInstalled,
181
- renderMotion,
182
- wrapComponentWithVideo,
183
- renderWithEmbeddedVideo,
184
- wrapComponentWithImage,
185
- renderWithEmbeddedImage,
186
- } = await import("../utils/remotion.js");
187
-
188
- const notInstalled = await ensureRemotionInstalled();
189
- if (notInstalled) {
190
- return { success: false, codePath, componentName: component.name, error: notInstalled };
191
- }
192
-
193
- const baseVideo = options.video ? resolve(process.cwd(), options.video) : undefined;
194
- const baseImage = options.image ? resolve(process.cwd(), options.image) : undefined;
195
-
196
- if (baseVideo) {
197
- // Embed video inside the component (no transparency needed)
198
- const videoFileName = "source_video.mp4";
199
- const wrapped = wrapComponentWithVideo(component.code, component.name, videoFileName);
200
-
201
- const renderResult = await renderWithEmbeddedVideo({
202
- componentCode: wrapped.code,
203
- componentName: wrapped.name,
204
- width,
205
- height,
206
- fps,
207
- durationInFrames: component.durationInFrames,
208
- videoPath: baseVideo,
209
- videoFileName,
210
- outputPath,
211
- });
212
-
213
- if (!renderResult.success) {
214
- return { success: false, codePath, componentName: component.name, error: renderResult.error };
215
- }
216
-
217
- return { success: true, codePath, componentName: component.name, compositedPath: renderResult.outputPath };
218
- }
219
-
220
- if (baseImage) {
221
- // Embed image as background — motion graphic overlaid on top
222
- const ext = baseImage.split(".").pop() || "png";
223
- const imageFileName = `source_image.${ext}`;
224
- const wrapped = wrapComponentWithImage(component.code, component.name, imageFileName);
225
-
226
- const renderResult = await renderWithEmbeddedImage({
227
- componentCode: wrapped.code,
228
- componentName: wrapped.name,
229
- width,
230
- height,
231
- fps,
232
- durationInFrames: component.durationInFrames,
233
- imagePath: baseImage,
234
- imageFileName,
235
- outputPath,
236
- });
237
-
238
- if (!renderResult.success) {
239
- return { success: false, codePath, componentName: component.name, error: renderResult.error };
240
- }
241
-
242
- return { success: true, codePath, componentName: component.name, compositedPath: renderResult.outputPath };
243
- }
244
-
245
- // No base media — render standalone
246
- const renderResult = await renderMotion({
247
- componentCode: component.code,
248
- componentName: component.name,
249
- width,
250
- height,
251
- fps,
252
- durationInFrames: component.durationInFrames,
253
- outputPath,
254
- transparent: false,
255
- });
256
-
257
- if (!renderResult.success) {
258
- return { success: false, codePath, componentName: component.name, error: renderResult.error };
259
- }
260
-
261
- return { success: true, codePath, componentName: component.name, renderedPath: renderResult.outputPath };
262
- }
263
-
264
- /**
265
- * Register the 'motion' sub-command on the given parent command.
266
- * Called from ai.ts: registerMotionCommand(aiCommand)
267
- */
268
- export function registerMotionCommand(aiCommand: Command): void {
269
- aiCommand
270
- .command("motion")
271
- .description("Generate motion graphics using Claude + Remotion (render & composite)")
272
- .argument("<description>", "Natural language description of the motion graphic")
273
- .option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)")
274
- .option("-o, --output <path>", "Output file path", "motion.tsx")
275
- .option("-d, --duration <sec>", "Duration in seconds", "5")
276
- .option("-w, --width <px>", "Width in pixels", "1920")
277
- .option("-h, --height <px>", "Height in pixels", "1080")
278
- .option("--fps <fps>", "Frame rate", "30")
279
- .option("-s, --style <style>", "Style preset: minimal, corporate, playful, cinematic")
280
- .option("--render", "Render the generated code with Remotion (output .webm)")
281
- .option("--video <path>", "Base video to composite the motion graphic onto")
282
- .option("--image <path>", "Image to analyze with Gemini — color/mood fed into Claude prompt")
283
- .option("--from-tsx <path>", "Refine an existing TSX file instead of generating from scratch")
284
- .option("-m, --model <alias>", "LLM model: sonnet (default), opus, gemini, gemini-3.1-pro", "sonnet")
285
- .action(async (description: string, options) => {
286
- try {
287
- const shouldRender = options.render || !!options.video || !!options.image;
288
-
289
- const spinner = ora("Generating motion graphic...").start();
290
- if (options.image) {
291
- spinner.text = "Analyzing image with Gemini...";
292
- }
293
-
294
- const result = await executeMotion({
295
- description,
296
- duration: parseFloat(options.duration),
297
- width: parseInt(options.width),
298
- height: parseInt(options.height),
299
- fps: parseInt(options.fps),
300
- style: options.style,
301
- render: options.render,
302
- video: options.video,
303
- image: options.image,
304
- fromTsx: options.fromTsx,
305
- model: options.model,
306
- output: options.output !== "motion.tsx" ? options.output : undefined,
307
- });
308
-
309
- if (!result.success) {
310
- spinner.fail(chalk.red(result.error || "Motion generation failed"));
311
- if (result.codePath) {
312
- console.log(chalk.dim(`TSX code saved to: ${result.codePath}`));
313
- }
314
- process.exit(1);
315
- }
316
-
317
- spinner.succeed(chalk.green("Motion graphic generated"));
318
-
319
- console.log();
320
- console.log(chalk.bold.cyan("Motion Graphics Pipeline"));
321
- console.log(chalk.dim("─".repeat(60)));
322
-
323
- if (result.codePath) {
324
- console.log(chalk.green(` Code: ${result.codePath}`));
325
- }
326
- if (result.renderedPath) {
327
- console.log(chalk.green(` Rendered: ${result.renderedPath}`));
328
- }
329
- if (result.compositedPath) {
330
- console.log(chalk.green(` Composited: ${result.compositedPath}`));
331
- }
332
-
333
- if (!shouldRender) {
334
- console.log();
335
- console.log(chalk.dim("To render, add --render flag or --video <path>:"));
336
- console.log(chalk.dim(` vibe ai motion "${description}" --render -o motion.webm`));
337
- console.log(chalk.dim(` vibe ai motion "${description}" --video input.mp4 -o output.mp4`));
338
- }
339
-
340
- console.log();
341
- } catch (error) {
342
- console.error(chalk.red("Motion generation failed"));
343
- console.error(error);
344
- process.exit(1);
345
- }
346
- });
347
- }