@vibeframe/cli 0.27.0 → 0.30.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 (118) 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/ai-edit-cli.d.ts.map +1 -1
  16. package/dist/commands/ai-edit-cli.js +18 -0
  17. package/dist/commands/ai-edit-cli.js.map +1 -1
  18. package/dist/commands/generate.js +14 -0
  19. package/dist/commands/generate.js.map +1 -1
  20. package/dist/commands/schema.d.ts +1 -0
  21. package/dist/commands/schema.d.ts.map +1 -1
  22. package/dist/commands/schema.js +122 -21
  23. package/dist/commands/schema.js.map +1 -1
  24. package/dist/commands/setup.js +5 -2
  25. package/dist/commands/setup.js.map +1 -1
  26. package/dist/config/schema.d.ts +2 -1
  27. package/dist/config/schema.d.ts.map +1 -1
  28. package/dist/config/schema.js +2 -0
  29. package/dist/config/schema.js.map +1 -1
  30. package/dist/index.js +0 -0
  31. package/package.json +16 -12
  32. package/.turbo/turbo-build.log +0 -4
  33. package/.turbo/turbo-lint.log +0 -21
  34. package/.turbo/turbo-test.log +0 -689
  35. package/src/agent/adapters/claude.ts +0 -143
  36. package/src/agent/adapters/gemini.ts +0 -159
  37. package/src/agent/adapters/index.ts +0 -61
  38. package/src/agent/adapters/ollama.ts +0 -231
  39. package/src/agent/adapters/openai.ts +0 -116
  40. package/src/agent/adapters/xai.ts +0 -119
  41. package/src/agent/index.ts +0 -251
  42. package/src/agent/memory/index.ts +0 -151
  43. package/src/agent/prompts/system.ts +0 -106
  44. package/src/agent/tools/ai-editing.ts +0 -845
  45. package/src/agent/tools/ai-generation.ts +0 -1073
  46. package/src/agent/tools/ai-pipeline.ts +0 -1055
  47. package/src/agent/tools/ai.ts +0 -21
  48. package/src/agent/tools/batch.ts +0 -429
  49. package/src/agent/tools/e2e.test.ts +0 -545
  50. package/src/agent/tools/export.ts +0 -184
  51. package/src/agent/tools/filesystem.ts +0 -237
  52. package/src/agent/tools/index.ts +0 -150
  53. package/src/agent/tools/integration.test.ts +0 -775
  54. package/src/agent/tools/media.ts +0 -697
  55. package/src/agent/tools/project.ts +0 -313
  56. package/src/agent/tools/timeline.ts +0 -951
  57. package/src/agent/types.ts +0 -68
  58. package/src/commands/agent.ts +0 -340
  59. package/src/commands/ai-analyze.ts +0 -429
  60. package/src/commands/ai-animated-caption.ts +0 -390
  61. package/src/commands/ai-audio.ts +0 -941
  62. package/src/commands/ai-broll.ts +0 -490
  63. package/src/commands/ai-edit-cli.ts +0 -658
  64. package/src/commands/ai-edit.ts +0 -1542
  65. package/src/commands/ai-fill-gaps.ts +0 -566
  66. package/src/commands/ai-helpers.ts +0 -65
  67. package/src/commands/ai-highlights.ts +0 -1303
  68. package/src/commands/ai-image.ts +0 -761
  69. package/src/commands/ai-motion.ts +0 -347
  70. package/src/commands/ai-narrate.ts +0 -451
  71. package/src/commands/ai-review.ts +0 -309
  72. package/src/commands/ai-script-pipeline-cli.ts +0 -1710
  73. package/src/commands/ai-script-pipeline.ts +0 -1365
  74. package/src/commands/ai-suggest-edit.ts +0 -264
  75. package/src/commands/ai-video-fx.ts +0 -445
  76. package/src/commands/ai-video.ts +0 -915
  77. package/src/commands/ai-viral.ts +0 -595
  78. package/src/commands/ai-visual-fx.ts +0 -601
  79. package/src/commands/ai.test.ts +0 -627
  80. package/src/commands/ai.ts +0 -307
  81. package/src/commands/analyze.ts +0 -282
  82. package/src/commands/audio.ts +0 -644
  83. package/src/commands/batch.test.ts +0 -279
  84. package/src/commands/batch.ts +0 -440
  85. package/src/commands/detect.ts +0 -329
  86. package/src/commands/doctor.ts +0 -237
  87. package/src/commands/edit-cmd.ts +0 -1014
  88. package/src/commands/export.ts +0 -918
  89. package/src/commands/generate.ts +0 -2146
  90. package/src/commands/media.ts +0 -177
  91. package/src/commands/output.ts +0 -142
  92. package/src/commands/pipeline.ts +0 -398
  93. package/src/commands/project.test.ts +0 -127
  94. package/src/commands/project.ts +0 -149
  95. package/src/commands/sanitize.ts +0 -60
  96. package/src/commands/schema.ts +0 -130
  97. package/src/commands/setup.ts +0 -509
  98. package/src/commands/timeline.test.ts +0 -499
  99. package/src/commands/timeline.ts +0 -529
  100. package/src/commands/validate.ts +0 -77
  101. package/src/config/config.test.ts +0 -197
  102. package/src/config/index.ts +0 -125
  103. package/src/config/schema.ts +0 -82
  104. package/src/engine/index.ts +0 -2
  105. package/src/engine/project.test.ts +0 -702
  106. package/src/engine/project.ts +0 -439
  107. package/src/index.ts +0 -146
  108. package/src/utils/api-key.test.ts +0 -41
  109. package/src/utils/api-key.ts +0 -247
  110. package/src/utils/audio.ts +0 -83
  111. package/src/utils/exec-safe.ts +0 -75
  112. package/src/utils/first-run.ts +0 -52
  113. package/src/utils/provider-resolver.ts +0 -56
  114. package/src/utils/remotion.ts +0 -951
  115. package/src/utils/subtitle.test.ts +0 -227
  116. package/src/utils/subtitle.ts +0 -169
  117. package/src/utils/tty.ts +0 -196
  118. 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
- }