@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,429 +0,0 @@
1
- /**
2
- * @module ai-analyze
3
- *
4
- * Media analysis execute functions using Gemini multimodal AI.
5
- *
6
- * CLI commands: gemini-video, analyze
7
- *
8
- * Execute functions:
9
- * executeGeminiVideo - Analyze video files or YouTube URLs with Gemini
10
- * executeAnalyze - Unified analysis for images, videos, and YouTube URLs
11
- *
12
- * @dependencies Gemini (Google)
13
- */
14
-
15
- import { Command } from "commander";
16
- import { readFile } from "node:fs/promises";
17
- import { extname, resolve } from "node:path";
18
- import { existsSync } from "node:fs";
19
- import chalk from "chalk";
20
- import ora from "ora";
21
- import { GeminiProvider } from "@vibeframe/ai-providers";
22
- import { getApiKey } from "../utils/api-key.js";
23
-
24
- /** Options for {@link executeGeminiVideo}. */
25
- export interface GeminiVideoOptions {
26
- /** Video file path or YouTube URL */
27
- source: string;
28
- /** Analysis prompt (e.g. "Summarize this video") */
29
- prompt: string;
30
- /** Gemini model shorthand (default: "flash") */
31
- model?: "flash" | "flash-2.5" | "pro";
32
- /** Frames per second for video sampling (default: 1) */
33
- fps?: number;
34
- /** Start offset in seconds for clipping */
35
- start?: number;
36
- /** End offset in seconds for clipping */
37
- end?: number;
38
- /** Use low-resolution mode (fewer tokens, longer videos) */
39
- lowRes?: boolean;
40
- }
41
-
42
- /** Result from {@link executeGeminiVideo}. */
43
- export interface GeminiVideoResult {
44
- /** Whether the analysis succeeded */
45
- success: boolean;
46
- /** Gemini's text response */
47
- response?: string;
48
- /** Model used for analysis */
49
- model?: string;
50
- /** Total tokens consumed */
51
- totalTokens?: number;
52
- /** Prompt tokens consumed */
53
- promptTokens?: number;
54
- /** Response tokens generated */
55
- responseTokens?: number;
56
- /** Error message on failure */
57
- error?: string;
58
- }
59
-
60
- /**
61
- * Analyze a video file or YouTube URL using Gemini multimodal AI.
62
- *
63
- * Supports local video files and YouTube URLs. Provides structured responses
64
- * with optional token usage reporting.
65
- *
66
- * @param options - Video analysis configuration
67
- * @returns Result with Gemini's response text and token usage
68
- */
69
- export async function executeGeminiVideo(
70
- options: GeminiVideoOptions
71
- ): Promise<GeminiVideoResult> {
72
- try {
73
- const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
74
- if (!apiKey) {
75
- return { success: false, error: "Google API key required. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
76
- }
77
-
78
- const isYouTube = options.source.includes("youtube.com") || options.source.includes("youtu.be");
79
-
80
- const modelMap: Record<string, string> = {
81
- flash: "gemini-3-flash-preview",
82
- "flash-2.5": "gemini-2.5-flash",
83
- pro: "gemini-2.5-pro",
84
- };
85
- const modelId = modelMap[options.model || "flash"] || modelMap.flash;
86
-
87
- let videoData: Buffer | string;
88
- if (isYouTube) {
89
- videoData = options.source;
90
- } else {
91
- const absPath = resolve(process.cwd(), options.source);
92
- if (!existsSync(absPath)) {
93
- return { success: false, error: `File not found: ${absPath}` };
94
- }
95
- videoData = await readFile(absPath);
96
- }
97
-
98
- const gemini = new GeminiProvider();
99
- await gemini.initialize({ apiKey });
100
-
101
- const result = await gemini.analyzeVideo(videoData, options.prompt, {
102
- model: modelId as "gemini-3-flash-preview" | "gemini-2.5-flash" | "gemini-2.5-pro",
103
- fps: options.fps,
104
- startOffset: options.start,
105
- endOffset: options.end,
106
- lowResolution: options.lowRes,
107
- });
108
-
109
- if (!result.success) {
110
- return { success: false, error: result.error || "Video analysis failed" };
111
- }
112
-
113
- return {
114
- success: true,
115
- response: result.response,
116
- model: result.model,
117
- totalTokens: result.totalTokens,
118
- promptTokens: result.promptTokens,
119
- responseTokens: result.responseTokens,
120
- };
121
- } catch (error) {
122
- return {
123
- success: false,
124
- error: error instanceof Error ? error.message : String(error),
125
- };
126
- }
127
- }
128
-
129
- /** Options for {@link executeAnalyze}. */
130
- export interface AnalyzeOptions {
131
- /** Image/video file path, image URL, or YouTube URL */
132
- source: string;
133
- /** Analysis prompt (e.g. "Describe this image") */
134
- prompt: string;
135
- /** Gemini model shorthand (default: "flash") */
136
- model?: "flash" | "flash-2.5" | "pro";
137
- /** Frames per second for video sampling (default: 1) */
138
- fps?: number;
139
- /** Start offset in seconds (video only) */
140
- start?: number;
141
- /** End offset in seconds (video only) */
142
- end?: number;
143
- /** Use low-resolution mode (fewer tokens) */
144
- lowRes?: boolean;
145
- }
146
-
147
- /** Result from {@link executeAnalyze}. */
148
- export interface AnalyzeResult {
149
- /** Whether the analysis succeeded */
150
- success: boolean;
151
- /** Gemini's text response */
152
- response?: string;
153
- /** Model used for analysis */
154
- model?: string;
155
- /** Detected source media type */
156
- sourceType?: "image" | "video" | "youtube";
157
- /** Total tokens consumed */
158
- totalTokens?: number;
159
- /** Prompt tokens consumed */
160
- promptTokens?: number;
161
- /** Response tokens generated */
162
- responseTokens?: number;
163
- /** Error message on failure */
164
- error?: string;
165
- }
166
-
167
- /**
168
- * Analyze any media source (image, video, or YouTube URL) using Gemini.
169
- *
170
- * Auto-detects source type from file extension or URL pattern. Supports
171
- * local files, remote URLs, and YouTube links.
172
- *
173
- * @param options - Unified analysis configuration
174
- * @returns Result with Gemini's response, detected source type, and token usage
175
- */
176
- export async function executeAnalyze(
177
- options: AnalyzeOptions
178
- ): Promise<AnalyzeResult> {
179
- try {
180
- const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
181
- if (!apiKey) {
182
- return { success: false, error: "Google API key required. Run 'vibe setup' or set GOOGLE_API_KEY in .env" };
183
- }
184
-
185
- const source = options.source;
186
-
187
- const isYouTube = source.includes("youtube.com") || source.includes("youtu.be");
188
- const isImageUrl = /^https?:\/\/.+\.(png|jpg|jpeg|webp|gif)(\?.*)?$/i.test(source);
189
- const isVideoUrl = /^https?:\/\/.+\.(mp4|mov|webm)(\?.*)?$/i.test(source);
190
- const ext = extname(source).toLowerCase();
191
- const imageExts = [".png", ".jpg", ".jpeg", ".webp", ".gif"];
192
- const videoExts = [".mp4", ".mov", ".webm", ".avi", ".mkv"];
193
- const isLocalImage = imageExts.includes(ext);
194
- const isLocalVideo = videoExts.includes(ext);
195
- const isImage = isImageUrl || isLocalImage;
196
- const isVideo = isYouTube || isVideoUrl || isLocalVideo;
197
-
198
- if (!isImage && !isVideo) {
199
- return {
200
- success: false,
201
- error: "Cannot detect source type. Supported: images (.png/.jpg/.webp/.gif), videos (.mp4/.mov/.webm), YouTube URLs, image URLs.",
202
- };
203
- }
204
-
205
- const modelMap: Record<string, string> = {
206
- flash: "gemini-3-flash-preview",
207
- "flash-2.5": "gemini-2.5-flash",
208
- pro: "gemini-2.5-pro",
209
- };
210
- const modelId = modelMap[options.model || "flash"] || modelMap.flash;
211
-
212
- const gemini = new GeminiProvider();
213
- await gemini.initialize({ apiKey });
214
-
215
- if (isImage) {
216
- let imageBuffer: Buffer;
217
- if (isImageUrl) {
218
- const response = await fetch(source);
219
- if (!response.ok) {
220
- return { success: false, error: `Failed to fetch image: ${response.status}` };
221
- }
222
- imageBuffer = Buffer.from(await response.arrayBuffer());
223
- } else {
224
- const absPath = resolve(process.cwd(), source);
225
- if (!existsSync(absPath)) {
226
- return { success: false, error: `File not found: ${absPath}` };
227
- }
228
- imageBuffer = await readFile(absPath);
229
- }
230
-
231
- const result = await gemini.analyzeImage(imageBuffer, options.prompt, {
232
- model: modelId as "gemini-3-flash-preview" | "gemini-2.5-flash" | "gemini-2.5-pro",
233
- lowResolution: options.lowRes,
234
- });
235
-
236
- if (!result.success) {
237
- return { success: false, error: result.error || "Image analysis failed" };
238
- }
239
-
240
- return {
241
- success: true,
242
- response: result.response,
243
- model: result.model,
244
- sourceType: "image",
245
- totalTokens: result.totalTokens,
246
- promptTokens: result.promptTokens,
247
- responseTokens: result.responseTokens,
248
- };
249
- }
250
-
251
- let videoData: Buffer | string;
252
- let sourceType: "video" | "youtube" = "video";
253
-
254
- if (isYouTube) {
255
- videoData = source;
256
- sourceType = "youtube";
257
- } else if (isVideoUrl) {
258
- const response = await fetch(source);
259
- if (!response.ok) {
260
- return { success: false, error: `Failed to fetch video: ${response.status}` };
261
- }
262
- videoData = Buffer.from(await response.arrayBuffer());
263
- } else {
264
- const absPath = resolve(process.cwd(), source);
265
- if (!existsSync(absPath)) {
266
- return { success: false, error: `File not found: ${absPath}` };
267
- }
268
- videoData = await readFile(absPath);
269
- }
270
-
271
- const result = await gemini.analyzeVideo(videoData, options.prompt, {
272
- model: modelId as "gemini-3-flash-preview" | "gemini-2.5-flash" | "gemini-2.5-pro",
273
- fps: options.fps,
274
- startOffset: options.start,
275
- endOffset: options.end,
276
- lowResolution: options.lowRes,
277
- });
278
-
279
- if (!result.success) {
280
- return { success: false, error: result.error || "Video analysis failed" };
281
- }
282
-
283
- return {
284
- success: true,
285
- response: result.response,
286
- model: result.model,
287
- sourceType,
288
- totalTokens: result.totalTokens,
289
- promptTokens: result.promptTokens,
290
- responseTokens: result.responseTokens,
291
- };
292
- } catch (error) {
293
- return {
294
- success: false,
295
- error: error instanceof Error ? error.message : String(error),
296
- };
297
- }
298
- }
299
-
300
- export function registerAnalyzeCommands(aiCommand: Command): void {
301
- aiCommand
302
- .command("gemini-video")
303
- .description("Analyze video using Gemini (summarize, Q&A, extract info)")
304
- .argument("<source>", "Video file path or YouTube URL")
305
- .argument("<prompt>", "Analysis prompt (e.g., 'Summarize this video')")
306
- .option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)")
307
- .option("-m, --model <model>", "Model: flash (default), flash-2.5, pro", "flash")
308
- .option("--fps <number>", "Frames per second (default: 1, higher for action)")
309
- .option("--start <seconds>", "Start offset in seconds (for clipping)")
310
- .option("--end <seconds>", "End offset in seconds (for clipping)")
311
- .option("--low-res", "Use low resolution mode (fewer tokens, longer videos)")
312
- .option("-v, --verbose", "Show token usage")
313
- .action(async (source: string, prompt: string, options) => {
314
- try {
315
- if (options.apiKey) {
316
- process.env.GOOGLE_API_KEY = options.apiKey;
317
- } else {
318
- const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
319
- if (!apiKey) {
320
- console.error(chalk.red("Google API key required. Set GOOGLE_API_KEY in .env or run: vibe setup"));
321
- console.error(chalk.dim("Use --api-key or set GOOGLE_API_KEY environment variable"));
322
- process.exit(1);
323
- }
324
- }
325
-
326
- const spinner = ora("Analyzing video...").start();
327
- const result = await executeGeminiVideo({
328
- source,
329
- prompt,
330
- model: options.model as "flash" | "flash-2.5" | "pro",
331
- fps: options.fps ? parseFloat(options.fps) : undefined,
332
- start: options.start ? parseInt(options.start, 10) : undefined,
333
- end: options.end ? parseInt(options.end, 10) : undefined,
334
- lowRes: options.lowRes,
335
- });
336
-
337
- if (!result.success) {
338
- spinner.fail(chalk.red(result.error || "Video analysis failed"));
339
- process.exit(1);
340
- }
341
-
342
- spinner.succeed(chalk.green("Video analyzed"));
343
- console.log();
344
- console.log(result.response);
345
- console.log();
346
-
347
- if (options.verbose && result.totalTokens) {
348
- console.log(chalk.dim("-".repeat(40)));
349
- console.log(chalk.dim(`Model: ${result.model}`));
350
- if (result.promptTokens) {
351
- console.log(chalk.dim(`Prompt tokens: ${result.promptTokens.toLocaleString()}`));
352
- }
353
- if (result.responseTokens) {
354
- console.log(chalk.dim(`Response tokens: ${result.responseTokens.toLocaleString()}`));
355
- }
356
- console.log(chalk.dim(`Total tokens: ${result.totalTokens.toLocaleString()}`));
357
- }
358
- } catch (error) {
359
- console.error(chalk.red("Video analysis failed"));
360
- console.error(error);
361
- process.exit(1);
362
- }
363
- });
364
-
365
- aiCommand
366
- .command("analyze")
367
- .description("Analyze any media: images, videos, or YouTube URLs using Gemini")
368
- .argument("<source>", "Image/video file path, image URL, or YouTube URL")
369
- .argument("<prompt>", "Analysis prompt (e.g., 'Describe this image', 'Summarize this video')")
370
- .option("-k, --api-key <key>", "Google API key (or set GOOGLE_API_KEY env)")
371
- .option("-m, --model <model>", "Model: flash (default), flash-2.5, pro", "flash")
372
- .option("--fps <number>", "Frames per second for video (default: 1)")
373
- .option("--start <seconds>", "Start offset in seconds (video only)")
374
- .option("--end <seconds>", "End offset in seconds (video only)")
375
- .option("--low-res", "Use low resolution mode (fewer tokens)")
376
- .option("-v, --verbose", "Show token usage")
377
- .action(async (source: string, prompt: string, options) => {
378
- try {
379
- if (options.apiKey) {
380
- process.env.GOOGLE_API_KEY = options.apiKey;
381
- } else {
382
- const apiKey = await getApiKey("GOOGLE_API_KEY", "Google");
383
- if (!apiKey) {
384
- console.error(chalk.red("Google API key required. Set GOOGLE_API_KEY in .env or run: vibe setup"));
385
- console.error(chalk.dim("Use --api-key or set GOOGLE_API_KEY environment variable"));
386
- process.exit(1);
387
- }
388
- }
389
-
390
- const spinner = ora("Analyzing source...").start();
391
- const result = await executeAnalyze({
392
- source,
393
- prompt,
394
- model: options.model as "flash" | "flash-2.5" | "pro",
395
- fps: options.fps ? parseFloat(options.fps) : undefined,
396
- start: options.start ? parseInt(options.start, 10) : undefined,
397
- end: options.end ? parseInt(options.end, 10) : undefined,
398
- lowRes: options.lowRes,
399
- });
400
-
401
- if (!result.success) {
402
- spinner.fail(chalk.red(result.error || "Analysis failed"));
403
- process.exit(1);
404
- }
405
-
406
- spinner.succeed(chalk.green("Analysis complete"));
407
- console.log();
408
- console.log(result.response);
409
- console.log();
410
-
411
- if (options.verbose && result.totalTokens) {
412
- console.log(chalk.dim("-".repeat(40)));
413
- console.log(chalk.dim(`Source type: ${result.sourceType}`));
414
- console.log(chalk.dim(`Model: ${result.model}`));
415
- if (result.promptTokens) {
416
- console.log(chalk.dim(`Prompt tokens: ${result.promptTokens.toLocaleString()}`));
417
- }
418
- if (result.responseTokens) {
419
- console.log(chalk.dim(`Response tokens: ${result.responseTokens.toLocaleString()}`));
420
- }
421
- console.log(chalk.dim(`Total tokens: ${result.totalTokens.toLocaleString()}`));
422
- }
423
- } catch (error) {
424
- console.error(chalk.red("Analysis failed"));
425
- console.error(error);
426
- process.exit(1);
427
- }
428
- });
429
- }