@vibeframe/cli 0.55.0 → 0.55.2

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 (497) hide show
  1. package/dist/index.js +63450 -233
  2. package/package.json +11 -6
  3. package/dist/agent/adapters/claude.d.ts +0 -15
  4. package/dist/agent/adapters/claude.d.ts.map +0 -1
  5. package/dist/agent/adapters/claude.js +0 -119
  6. package/dist/agent/adapters/claude.js.map +0 -1
  7. package/dist/agent/adapters/gemini.d.ts +0 -15
  8. package/dist/agent/adapters/gemini.d.ts.map +0 -1
  9. package/dist/agent/adapters/gemini.js +0 -132
  10. package/dist/agent/adapters/gemini.js.map +0 -1
  11. package/dist/agent/adapters/index.d.ts +0 -28
  12. package/dist/agent/adapters/index.d.ts.map +0 -1
  13. package/dist/agent/adapters/index.js +0 -43
  14. package/dist/agent/adapters/index.js.map +0 -1
  15. package/dist/agent/adapters/ollama.d.ts +0 -20
  16. package/dist/agent/adapters/ollama.d.ts.map +0 -1
  17. package/dist/agent/adapters/ollama.js +0 -186
  18. package/dist/agent/adapters/ollama.js.map +0 -1
  19. package/dist/agent/adapters/openai.d.ts +0 -15
  20. package/dist/agent/adapters/openai.d.ts.map +0 -1
  21. package/dist/agent/adapters/openai.js +0 -92
  22. package/dist/agent/adapters/openai.js.map +0 -1
  23. package/dist/agent/adapters/openrouter.d.ts +0 -16
  24. package/dist/agent/adapters/openrouter.d.ts.map +0 -1
  25. package/dist/agent/adapters/openrouter.js +0 -100
  26. package/dist/agent/adapters/openrouter.js.map +0 -1
  27. package/dist/agent/adapters/openrouter.test.d.ts +0 -5
  28. package/dist/agent/adapters/openrouter.test.d.ts.map +0 -1
  29. package/dist/agent/adapters/openrouter.test.js +0 -73
  30. package/dist/agent/adapters/openrouter.test.js.map +0 -1
  31. package/dist/agent/adapters/xai.d.ts +0 -15
  32. package/dist/agent/adapters/xai.d.ts.map +0 -1
  33. package/dist/agent/adapters/xai.js +0 -95
  34. package/dist/agent/adapters/xai.js.map +0 -1
  35. package/dist/agent/index.d.ts +0 -69
  36. package/dist/agent/index.d.ts.map +0 -1
  37. package/dist/agent/index.js +0 -180
  38. package/dist/agent/index.js.map +0 -1
  39. package/dist/agent/memory/index.d.ts +0 -70
  40. package/dist/agent/memory/index.d.ts.map +0 -1
  41. package/dist/agent/memory/index.js +0 -132
  42. package/dist/agent/memory/index.js.map +0 -1
  43. package/dist/agent/prompts/system.d.ts +0 -6
  44. package/dist/agent/prompts/system.d.ts.map +0 -1
  45. package/dist/agent/prompts/system.js +0 -103
  46. package/dist/agent/prompts/system.js.map +0 -1
  47. package/dist/agent/tools/ai-editing.d.ts +0 -15
  48. package/dist/agent/tools/ai-editing.d.ts.map +0 -1
  49. package/dist/agent/tools/ai-editing.js +0 -763
  50. package/dist/agent/tools/ai-editing.js.map +0 -1
  51. package/dist/agent/tools/ai-generation.d.ts +0 -13
  52. package/dist/agent/tools/ai-generation.d.ts.map +0 -1
  53. package/dist/agent/tools/ai-generation.js +0 -973
  54. package/dist/agent/tools/ai-generation.js.map +0 -1
  55. package/dist/agent/tools/ai-pipeline.d.ts +0 -14
  56. package/dist/agent/tools/ai-pipeline.d.ts.map +0 -1
  57. package/dist/agent/tools/ai-pipeline.js +0 -961
  58. package/dist/agent/tools/ai-pipeline.js.map +0 -1
  59. package/dist/agent/tools/ai.d.ts +0 -13
  60. package/dist/agent/tools/ai.d.ts.map +0 -1
  61. package/dist/agent/tools/ai.js +0 -19
  62. package/dist/agent/tools/ai.js.map +0 -1
  63. package/dist/agent/tools/batch.d.ts +0 -6
  64. package/dist/agent/tools/batch.d.ts.map +0 -1
  65. package/dist/agent/tools/batch.js +0 -383
  66. package/dist/agent/tools/batch.js.map +0 -1
  67. package/dist/agent/tools/e2e.test.d.ts +0 -26
  68. package/dist/agent/tools/e2e.test.d.ts.map +0 -1
  69. package/dist/agent/tools/e2e.test.js +0 -397
  70. package/dist/agent/tools/e2e.test.js.map +0 -1
  71. package/dist/agent/tools/export.d.ts +0 -6
  72. package/dist/agent/tools/export.d.ts.map +0 -1
  73. package/dist/agent/tools/export.js +0 -171
  74. package/dist/agent/tools/export.js.map +0 -1
  75. package/dist/agent/tools/filesystem.d.ts +0 -6
  76. package/dist/agent/tools/filesystem.d.ts.map +0 -1
  77. package/dist/agent/tools/filesystem.js +0 -212
  78. package/dist/agent/tools/filesystem.js.map +0 -1
  79. package/dist/agent/tools/index.d.ts +0 -66
  80. package/dist/agent/tools/index.d.ts.map +0 -1
  81. package/dist/agent/tools/index.js +0 -123
  82. package/dist/agent/tools/index.js.map +0 -1
  83. package/dist/agent/tools/integration.test.d.ts +0 -11
  84. package/dist/agent/tools/integration.test.d.ts.map +0 -1
  85. package/dist/agent/tools/integration.test.js +0 -659
  86. package/dist/agent/tools/integration.test.js.map +0 -1
  87. package/dist/agent/tools/media.d.ts +0 -6
  88. package/dist/agent/tools/media.d.ts.map +0 -1
  89. package/dist/agent/tools/media.js +0 -616
  90. package/dist/agent/tools/media.js.map +0 -1
  91. package/dist/agent/tools/project.d.ts +0 -6
  92. package/dist/agent/tools/project.d.ts.map +0 -1
  93. package/dist/agent/tools/project.js +0 -284
  94. package/dist/agent/tools/project.js.map +0 -1
  95. package/dist/agent/tools/scene.d.ts +0 -17
  96. package/dist/agent/tools/scene.d.ts.map +0 -1
  97. package/dist/agent/tools/scene.js +0 -263
  98. package/dist/agent/tools/scene.js.map +0 -1
  99. package/dist/agent/tools/scene.test.d.ts +0 -2
  100. package/dist/agent/tools/scene.test.d.ts.map +0 -1
  101. package/dist/agent/tools/scene.test.js +0 -139
  102. package/dist/agent/tools/scene.test.js.map +0 -1
  103. package/dist/agent/tools/timeline.d.ts +0 -6
  104. package/dist/agent/tools/timeline.d.ts.map +0 -1
  105. package/dist/agent/tools/timeline.js +0 -875
  106. package/dist/agent/tools/timeline.js.map +0 -1
  107. package/dist/agent/types.d.ts +0 -59
  108. package/dist/agent/types.d.ts.map +0 -1
  109. package/dist/agent/types.js +0 -5
  110. package/dist/agent/types.js.map +0 -1
  111. package/dist/commands/_shared/scene-audio-mux.d.ts +0 -75
  112. package/dist/commands/_shared/scene-audio-mux.d.ts.map +0 -1
  113. package/dist/commands/_shared/scene-audio-mux.js +0 -159
  114. package/dist/commands/_shared/scene-audio-mux.js.map +0 -1
  115. package/dist/commands/_shared/scene-audio-mux.test.d.ts +0 -7
  116. package/dist/commands/_shared/scene-audio-mux.test.d.ts.map +0 -1
  117. package/dist/commands/_shared/scene-audio-mux.test.js +0 -94
  118. package/dist/commands/_shared/scene-audio-mux.test.js.map +0 -1
  119. package/dist/commands/_shared/scene-audio-scan.d.ts +0 -94
  120. package/dist/commands/_shared/scene-audio-scan.d.ts.map +0 -1
  121. package/dist/commands/_shared/scene-audio-scan.js +0 -144
  122. package/dist/commands/_shared/scene-audio-scan.js.map +0 -1
  123. package/dist/commands/_shared/scene-audio-scan.test.d.ts +0 -6
  124. package/dist/commands/_shared/scene-audio-scan.test.d.ts.map +0 -1
  125. package/dist/commands/_shared/scene-audio-scan.test.js +0 -190
  126. package/dist/commands/_shared/scene-audio-scan.test.js.map +0 -1
  127. package/dist/commands/_shared/scene-html-emit.d.ts +0 -120
  128. package/dist/commands/_shared/scene-html-emit.d.ts.map +0 -1
  129. package/dist/commands/_shared/scene-html-emit.js +0 -422
  130. package/dist/commands/_shared/scene-html-emit.js.map +0 -1
  131. package/dist/commands/_shared/scene-html-emit.test.d.ts +0 -2
  132. package/dist/commands/_shared/scene-html-emit.test.d.ts.map +0 -1
  133. package/dist/commands/_shared/scene-html-emit.test.js +0 -396
  134. package/dist/commands/_shared/scene-html-emit.test.js.map +0 -1
  135. package/dist/commands/_shared/scene-lint.d.ts +0 -110
  136. package/dist/commands/_shared/scene-lint.d.ts.map +0 -1
  137. package/dist/commands/_shared/scene-lint.js +0 -207
  138. package/dist/commands/_shared/scene-lint.js.map +0 -1
  139. package/dist/commands/_shared/scene-lint.test.d.ts +0 -2
  140. package/dist/commands/_shared/scene-lint.test.d.ts.map +0 -1
  141. package/dist/commands/_shared/scene-lint.test.js +0 -208
  142. package/dist/commands/_shared/scene-lint.test.js.map +0 -1
  143. package/dist/commands/_shared/scene-project.d.ts +0 -101
  144. package/dist/commands/_shared/scene-project.d.ts.map +0 -1
  145. package/dist/commands/_shared/scene-project.js +0 -281
  146. package/dist/commands/_shared/scene-project.js.map +0 -1
  147. package/dist/commands/_shared/scene-project.test.d.ts +0 -2
  148. package/dist/commands/_shared/scene-project.test.d.ts.map +0 -1
  149. package/dist/commands/_shared/scene-project.test.js +0 -203
  150. package/dist/commands/_shared/scene-project.test.js.map +0 -1
  151. package/dist/commands/_shared/scene-render.d.ts +0 -85
  152. package/dist/commands/_shared/scene-render.d.ts.map +0 -1
  153. package/dist/commands/_shared/scene-render.js +0 -178
  154. package/dist/commands/_shared/scene-render.js.map +0 -1
  155. package/dist/commands/_shared/scene-render.test.d.ts +0 -2
  156. package/dist/commands/_shared/scene-render.test.d.ts.map +0 -1
  157. package/dist/commands/_shared/scene-render.test.js +0 -145
  158. package/dist/commands/_shared/scene-render.test.js.map +0 -1
  159. package/dist/commands/_shared/segments-to-scenes.d.ts +0 -85
  160. package/dist/commands/_shared/segments-to-scenes.d.ts.map +0 -1
  161. package/dist/commands/_shared/segments-to-scenes.js +0 -202
  162. package/dist/commands/_shared/segments-to-scenes.js.map +0 -1
  163. package/dist/commands/_shared/segments-to-scenes.test.d.ts +0 -2
  164. package/dist/commands/_shared/segments-to-scenes.test.d.ts.map +0 -1
  165. package/dist/commands/_shared/segments-to-scenes.test.js +0 -219
  166. package/dist/commands/_shared/segments-to-scenes.test.js.map +0 -1
  167. package/dist/commands/_shared/tts-resolve.d.ts +0 -75
  168. package/dist/commands/_shared/tts-resolve.d.ts.map +0 -1
  169. package/dist/commands/_shared/tts-resolve.js +0 -89
  170. package/dist/commands/_shared/tts-resolve.js.map +0 -1
  171. package/dist/commands/_shared/tts-resolve.test.d.ts +0 -6
  172. package/dist/commands/_shared/tts-resolve.test.d.ts.map +0 -1
  173. package/dist/commands/_shared/tts-resolve.test.js +0 -147
  174. package/dist/commands/_shared/tts-resolve.test.js.map +0 -1
  175. package/dist/commands/agent.d.ts +0 -21
  176. package/dist/commands/agent.d.ts.map +0 -1
  177. package/dist/commands/agent.js +0 -285
  178. package/dist/commands/agent.js.map +0 -1
  179. package/dist/commands/ai-analyze.d.ts +0 -106
  180. package/dist/commands/ai-analyze.d.ts.map +0 -1
  181. package/dist/commands/ai-analyze.js +0 -320
  182. package/dist/commands/ai-analyze.js.map +0 -1
  183. package/dist/commands/ai-animated-caption.d.ts +0 -64
  184. package/dist/commands/ai-animated-caption.d.ts.map +0 -1
  185. package/dist/commands/ai-animated-caption.js +0 -272
  186. package/dist/commands/ai-animated-caption.js.map +0 -1
  187. package/dist/commands/ai-audio.d.ts +0 -98
  188. package/dist/commands/ai-audio.d.ts.map +0 -1
  189. package/dist/commands/ai-audio.js +0 -1059
  190. package/dist/commands/ai-audio.js.map +0 -1
  191. package/dist/commands/ai-broll.d.ts +0 -15
  192. package/dist/commands/ai-broll.d.ts.map +0 -1
  193. package/dist/commands/ai-broll.js +0 -421
  194. package/dist/commands/ai-broll.js.map +0 -1
  195. package/dist/commands/ai-edit-cli.d.ts +0 -14
  196. package/dist/commands/ai-edit-cli.d.ts.map +0 -1
  197. package/dist/commands/ai-edit-cli.js +0 -585
  198. package/dist/commands/ai-edit-cli.js.map +0 -1
  199. package/dist/commands/ai-edit.d.ts +0 -398
  200. package/dist/commands/ai-edit.d.ts.map +0 -1
  201. package/dist/commands/ai-edit.js +0 -1060
  202. package/dist/commands/ai-edit.js.map +0 -1
  203. package/dist/commands/ai-fill-gaps.d.ts +0 -14
  204. package/dist/commands/ai-fill-gaps.d.ts.map +0 -1
  205. package/dist/commands/ai-fill-gaps.js +0 -447
  206. package/dist/commands/ai-fill-gaps.js.map +0 -1
  207. package/dist/commands/ai-helpers.d.ts +0 -20
  208. package/dist/commands/ai-helpers.d.ts.map +0 -1
  209. package/dist/commands/ai-helpers.js +0 -59
  210. package/dist/commands/ai-helpers.js.map +0 -1
  211. package/dist/commands/ai-highlights.d.ts +0 -127
  212. package/dist/commands/ai-highlights.d.ts.map +0 -1
  213. package/dist/commands/ai-highlights.js +0 -1053
  214. package/dist/commands/ai-highlights.js.map +0 -1
  215. package/dist/commands/ai-image.d.ts +0 -76
  216. package/dist/commands/ai-image.d.ts.map +0 -1
  217. package/dist/commands/ai-image.js +0 -858
  218. package/dist/commands/ai-image.js.map +0 -1
  219. package/dist/commands/ai-motion.d.ts +0 -50
  220. package/dist/commands/ai-motion.d.ts.map +0 -1
  221. package/dist/commands/ai-motion.js +0 -297
  222. package/dist/commands/ai-motion.js.map +0 -1
  223. package/dist/commands/ai-narrate.d.ts +0 -66
  224. package/dist/commands/ai-narrate.d.ts.map +0 -1
  225. package/dist/commands/ai-narrate.js +0 -332
  226. package/dist/commands/ai-narrate.js.map +0 -1
  227. package/dist/commands/ai-review.d.ts +0 -57
  228. package/dist/commands/ai-review.d.ts.map +0 -1
  229. package/dist/commands/ai-review.js +0 -270
  230. package/dist/commands/ai-review.js.map +0 -1
  231. package/dist/commands/ai-script-pipeline-cli.d.ts +0 -9
  232. package/dist/commands/ai-script-pipeline-cli.d.ts.map +0 -1
  233. package/dist/commands/ai-script-pipeline-cli.js +0 -502
  234. package/dist/commands/ai-script-pipeline-cli.js.map +0 -1
  235. package/dist/commands/ai-script-pipeline.d.ts +0 -305
  236. package/dist/commands/ai-script-pipeline.d.ts.map +0 -1
  237. package/dist/commands/ai-script-pipeline.js +0 -1546
  238. package/dist/commands/ai-script-pipeline.js.map +0 -1
  239. package/dist/commands/ai-suggest-edit.d.ts +0 -14
  240. package/dist/commands/ai-suggest-edit.d.ts.map +0 -1
  241. package/dist/commands/ai-suggest-edit.js +0 -215
  242. package/dist/commands/ai-suggest-edit.js.map +0 -1
  243. package/dist/commands/ai-video-fx.d.ts +0 -14
  244. package/dist/commands/ai-video-fx.d.ts.map +0 -1
  245. package/dist/commands/ai-video-fx.js +0 -396
  246. package/dist/commands/ai-video-fx.js.map +0 -1
  247. package/dist/commands/ai-video.d.ts +0 -91
  248. package/dist/commands/ai-video.d.ts.map +0 -1
  249. package/dist/commands/ai-video.js +0 -1055
  250. package/dist/commands/ai-video.js.map +0 -1
  251. package/dist/commands/ai-viral.d.ts +0 -15
  252. package/dist/commands/ai-viral.d.ts.map +0 -1
  253. package/dist/commands/ai-viral.js +0 -507
  254. package/dist/commands/ai-viral.js.map +0 -1
  255. package/dist/commands/ai-visual-fx.d.ts +0 -14
  256. package/dist/commands/ai-visual-fx.d.ts.map +0 -1
  257. package/dist/commands/ai-visual-fx.js +0 -521
  258. package/dist/commands/ai-visual-fx.js.map +0 -1
  259. package/dist/commands/ai.d.ts +0 -38
  260. package/dist/commands/ai.d.ts.map +0 -1
  261. package/dist/commands/ai.js +0 -225
  262. package/dist/commands/ai.js.map +0 -1
  263. package/dist/commands/ai.test.d.ts +0 -2
  264. package/dist/commands/ai.test.d.ts.map +0 -1
  265. package/dist/commands/ai.test.js +0 -557
  266. package/dist/commands/ai.test.js.map +0 -1
  267. package/dist/commands/analyze.d.ts +0 -16
  268. package/dist/commands/analyze.d.ts.map +0 -1
  269. package/dist/commands/analyze.js +0 -247
  270. package/dist/commands/analyze.js.map +0 -1
  271. package/dist/commands/audio.d.ts +0 -18
  272. package/dist/commands/audio.d.ts.map +0 -1
  273. package/dist/commands/audio.js +0 -539
  274. package/dist/commands/audio.js.map +0 -1
  275. package/dist/commands/batch.d.ts +0 -3
  276. package/dist/commands/batch.d.ts.map +0 -1
  277. package/dist/commands/batch.js +0 -419
  278. package/dist/commands/batch.js.map +0 -1
  279. package/dist/commands/batch.test.d.ts +0 -2
  280. package/dist/commands/batch.test.d.ts.map +0 -1
  281. package/dist/commands/batch.test.js +0 -203
  282. package/dist/commands/batch.test.js.map +0 -1
  283. package/dist/commands/context.d.ts +0 -7
  284. package/dist/commands/context.d.ts.map +0 -1
  285. package/dist/commands/context.js +0 -66
  286. package/dist/commands/context.js.map +0 -1
  287. package/dist/commands/demo.d.ts +0 -7
  288. package/dist/commands/demo.d.ts.map +0 -1
  289. package/dist/commands/demo.js +0 -192
  290. package/dist/commands/demo.js.map +0 -1
  291. package/dist/commands/detect.d.ts +0 -56
  292. package/dist/commands/detect.d.ts.map +0 -1
  293. package/dist/commands/detect.js +0 -464
  294. package/dist/commands/detect.js.map +0 -1
  295. package/dist/commands/doctor.d.ts +0 -6
  296. package/dist/commands/doctor.d.ts.map +0 -1
  297. package/dist/commands/doctor.js +0 -322
  298. package/dist/commands/doctor.js.map +0 -1
  299. package/dist/commands/edit-cmd.d.ts +0 -112
  300. package/dist/commands/edit-cmd.d.ts.map +0 -1
  301. package/dist/commands/edit-cmd.js +0 -1041
  302. package/dist/commands/edit-cmd.js.map +0 -1
  303. package/dist/commands/export.d.ts +0 -61
  304. package/dist/commands/export.d.ts.map +0 -1
  305. package/dist/commands/export.js +0 -980
  306. package/dist/commands/export.js.map +0 -1
  307. package/dist/commands/export.test.d.ts +0 -2
  308. package/dist/commands/export.test.d.ts.map +0 -1
  309. package/dist/commands/export.test.js +0 -107
  310. package/dist/commands/export.test.js.map +0 -1
  311. package/dist/commands/generate.d.ts +0 -84
  312. package/dist/commands/generate.d.ts.map +0 -1
  313. package/dist/commands/generate.js +0 -2030
  314. package/dist/commands/generate.js.map +0 -1
  315. package/dist/commands/media.d.ts +0 -3
  316. package/dist/commands/media.d.ts.map +0 -1
  317. package/dist/commands/media.js +0 -163
  318. package/dist/commands/media.js.map +0 -1
  319. package/dist/commands/output.d.ts +0 -51
  320. package/dist/commands/output.d.ts.map +0 -1
  321. package/dist/commands/output.js +0 -219
  322. package/dist/commands/output.js.map +0 -1
  323. package/dist/commands/output.test.d.ts +0 -2
  324. package/dist/commands/output.test.d.ts.map +0 -1
  325. package/dist/commands/output.test.js +0 -151
  326. package/dist/commands/output.test.js.map +0 -1
  327. package/dist/commands/pipeline.d.ts +0 -19
  328. package/dist/commands/pipeline.d.ts.map +0 -1
  329. package/dist/commands/pipeline.js +0 -345
  330. package/dist/commands/pipeline.js.map +0 -1
  331. package/dist/commands/project.d.ts +0 -3
  332. package/dist/commands/project.d.ts.map +0 -1
  333. package/dist/commands/project.js +0 -181
  334. package/dist/commands/project.js.map +0 -1
  335. package/dist/commands/project.test.d.ts +0 -2
  336. package/dist/commands/project.test.d.ts.map +0 -1
  337. package/dist/commands/project.test.js +0 -105
  338. package/dist/commands/project.test.js.map +0 -1
  339. package/dist/commands/run.d.ts +0 -15
  340. package/dist/commands/run.d.ts.map +0 -1
  341. package/dist/commands/run.js +0 -204
  342. package/dist/commands/run.js.map +0 -1
  343. package/dist/commands/sanitize.d.ts +0 -21
  344. package/dist/commands/sanitize.d.ts.map +0 -1
  345. package/dist/commands/sanitize.js +0 -56
  346. package/dist/commands/sanitize.js.map +0 -1
  347. package/dist/commands/scene.d.ts +0 -86
  348. package/dist/commands/scene.d.ts.map +0 -1
  349. package/dist/commands/scene.js +0 -728
  350. package/dist/commands/scene.js.map +0 -1
  351. package/dist/commands/scene.test.d.ts +0 -2
  352. package/dist/commands/scene.test.d.ts.map +0 -1
  353. package/dist/commands/scene.test.js +0 -219
  354. package/dist/commands/scene.test.js.map +0 -1
  355. package/dist/commands/schema.d.ts +0 -13
  356. package/dist/commands/schema.d.ts.map +0 -1
  357. package/dist/commands/schema.js +0 -218
  358. package/dist/commands/schema.js.map +0 -1
  359. package/dist/commands/setup.d.ts +0 -6
  360. package/dist/commands/setup.d.ts.map +0 -1
  361. package/dist/commands/setup.js +0 -461
  362. package/dist/commands/setup.js.map +0 -1
  363. package/dist/commands/timeline.d.ts +0 -3
  364. package/dist/commands/timeline.d.ts.map +0 -1
  365. package/dist/commands/timeline.js +0 -609
  366. package/dist/commands/timeline.js.map +0 -1
  367. package/dist/commands/timeline.test.d.ts +0 -2
  368. package/dist/commands/timeline.test.d.ts.map +0 -1
  369. package/dist/commands/timeline.test.js +0 -334
  370. package/dist/commands/timeline.test.js.map +0 -1
  371. package/dist/commands/validate.d.ts +0 -34
  372. package/dist/commands/validate.d.ts.map +0 -1
  373. package/dist/commands/validate.js +0 -70
  374. package/dist/commands/validate.js.map +0 -1
  375. package/dist/config/config.test.d.ts +0 -2
  376. package/dist/config/config.test.d.ts.map +0 -1
  377. package/dist/config/config.test.js +0 -164
  378. package/dist/config/config.test.js.map +0 -1
  379. package/dist/config/index.d.ts +0 -35
  380. package/dist/config/index.d.ts.map +0 -1
  381. package/dist/config/index.js +0 -101
  382. package/dist/config/index.js.map +0 -1
  383. package/dist/config/schema.d.ts +0 -52
  384. package/dist/config/schema.d.ts.map +0 -1
  385. package/dist/config/schema.js +0 -44
  386. package/dist/config/schema.js.map +0 -1
  387. package/dist/engine/index.d.ts +0 -3
  388. package/dist/engine/index.d.ts.map +0 -1
  389. package/dist/engine/index.js +0 -2
  390. package/dist/engine/index.js.map +0 -1
  391. package/dist/engine/project.d.ts +0 -84
  392. package/dist/engine/project.d.ts.map +0 -1
  393. package/dist/engine/project.js +0 -355
  394. package/dist/engine/project.js.map +0 -1
  395. package/dist/engine/project.test.d.ts +0 -2
  396. package/dist/engine/project.test.d.ts.map +0 -1
  397. package/dist/engine/project.test.js +0 -599
  398. package/dist/engine/project.test.js.map +0 -1
  399. package/dist/index.d.ts +0 -7
  400. package/dist/index.d.ts.map +0 -1
  401. package/dist/index.js.map +0 -1
  402. package/dist/pipeline/executor.d.ts +0 -19
  403. package/dist/pipeline/executor.d.ts.map +0 -1
  404. package/dist/pipeline/executor.js +0 -397
  405. package/dist/pipeline/executor.js.map +0 -1
  406. package/dist/pipeline/index.d.ts +0 -4
  407. package/dist/pipeline/index.d.ts.map +0 -1
  408. package/dist/pipeline/index.js +0 -2
  409. package/dist/pipeline/index.js.map +0 -1
  410. package/dist/pipeline/renderers/__tests__/html-template.test.d.ts +0 -2
  411. package/dist/pipeline/renderers/__tests__/html-template.test.d.ts.map +0 -1
  412. package/dist/pipeline/renderers/__tests__/html-template.test.js +0 -170
  413. package/dist/pipeline/renderers/__tests__/html-template.test.js.map +0 -1
  414. package/dist/pipeline/renderers/__tests__/integration.test.d.ts +0 -2
  415. package/dist/pipeline/renderers/__tests__/integration.test.d.ts.map +0 -1
  416. package/dist/pipeline/renderers/__tests__/integration.test.js +0 -175
  417. package/dist/pipeline/renderers/__tests__/integration.test.js.map +0 -1
  418. package/dist/pipeline/renderers/chrome.d.ts +0 -26
  419. package/dist/pipeline/renderers/chrome.d.ts.map +0 -1
  420. package/dist/pipeline/renderers/chrome.js +0 -48
  421. package/dist/pipeline/renderers/chrome.js.map +0 -1
  422. package/dist/pipeline/renderers/html-clips.d.ts +0 -20
  423. package/dist/pipeline/renderers/html-clips.d.ts.map +0 -1
  424. package/dist/pipeline/renderers/html-clips.js +0 -50
  425. package/dist/pipeline/renderers/html-clips.js.map +0 -1
  426. package/dist/pipeline/renderers/html-runtime.d.ts +0 -7
  427. package/dist/pipeline/renderers/html-runtime.d.ts.map +0 -1
  428. package/dist/pipeline/renderers/html-runtime.js +0 -80
  429. package/dist/pipeline/renderers/html-runtime.js.map +0 -1
  430. package/dist/pipeline/renderers/html-template.d.ts +0 -3
  431. package/dist/pipeline/renderers/html-template.d.ts.map +0 -1
  432. package/dist/pipeline/renderers/html-template.js +0 -36
  433. package/dist/pipeline/renderers/html-template.js.map +0 -1
  434. package/dist/pipeline/renderers/hyperframes.d.ts +0 -8
  435. package/dist/pipeline/renderers/hyperframes.d.ts.map +0 -1
  436. package/dist/pipeline/renderers/hyperframes.js +0 -63
  437. package/dist/pipeline/renderers/hyperframes.js.map +0 -1
  438. package/dist/pipeline/renderers/project-builder.d.ts +0 -8
  439. package/dist/pipeline/renderers/project-builder.d.ts.map +0 -1
  440. package/dist/pipeline/renderers/project-builder.js +0 -65
  441. package/dist/pipeline/renderers/project-builder.js.map +0 -1
  442. package/dist/pipeline/renderers/types.d.ts +0 -41
  443. package/dist/pipeline/renderers/types.d.ts.map +0 -1
  444. package/dist/pipeline/renderers/types.js +0 -11
  445. package/dist/pipeline/renderers/types.js.map +0 -1
  446. package/dist/pipeline/resolver.d.ts +0 -20
  447. package/dist/pipeline/resolver.d.ts.map +0 -1
  448. package/dist/pipeline/resolver.js +0 -74
  449. package/dist/pipeline/resolver.js.map +0 -1
  450. package/dist/pipeline/types.d.ts +0 -82
  451. package/dist/pipeline/types.d.ts.map +0 -1
  452. package/dist/pipeline/types.js +0 -8
  453. package/dist/pipeline/types.js.map +0 -1
  454. package/dist/utils/api-key.d.ts +0 -33
  455. package/dist/utils/api-key.d.ts.map +0 -1
  456. package/dist/utils/api-key.js +0 -228
  457. package/dist/utils/api-key.js.map +0 -1
  458. package/dist/utils/api-key.test.d.ts +0 -2
  459. package/dist/utils/api-key.test.d.ts.map +0 -1
  460. package/dist/utils/api-key.test.js +0 -35
  461. package/dist/utils/api-key.test.js.map +0 -1
  462. package/dist/utils/audio.d.ts +0 -23
  463. package/dist/utils/audio.d.ts.map +0 -1
  464. package/dist/utils/audio.js +0 -79
  465. package/dist/utils/audio.js.map +0 -1
  466. package/dist/utils/exec-safe.d.ts +0 -22
  467. package/dist/utils/exec-safe.d.ts.map +0 -1
  468. package/dist/utils/exec-safe.js +0 -62
  469. package/dist/utils/exec-safe.js.map +0 -1
  470. package/dist/utils/first-run.d.ts +0 -17
  471. package/dist/utils/first-run.d.ts.map +0 -1
  472. package/dist/utils/first-run.js +0 -73
  473. package/dist/utils/first-run.js.map +0 -1
  474. package/dist/utils/input-validation.d.ts +0 -27
  475. package/dist/utils/input-validation.d.ts.map +0 -1
  476. package/dist/utils/input-validation.js +0 -67
  477. package/dist/utils/input-validation.js.map +0 -1
  478. package/dist/utils/provider-resolver.d.ts +0 -19
  479. package/dist/utils/provider-resolver.d.ts.map +0 -1
  480. package/dist/utils/provider-resolver.js +0 -75
  481. package/dist/utils/provider-resolver.js.map +0 -1
  482. package/dist/utils/remotion.d.ts +0 -231
  483. package/dist/utils/remotion.d.ts.map +0 -1
  484. package/dist/utils/remotion.js +0 -879
  485. package/dist/utils/remotion.js.map +0 -1
  486. package/dist/utils/subtitle.d.ts +0 -65
  487. package/dist/utils/subtitle.d.ts.map +0 -1
  488. package/dist/utils/subtitle.js +0 -135
  489. package/dist/utils/subtitle.js.map +0 -1
  490. package/dist/utils/subtitle.test.d.ts +0 -2
  491. package/dist/utils/subtitle.test.d.ts.map +0 -1
  492. package/dist/utils/subtitle.test.js +0 -175
  493. package/dist/utils/subtitle.test.js.map +0 -1
  494. package/dist/utils/tty.d.ts +0 -45
  495. package/dist/utils/tty.d.ts.map +0 -1
  496. package/dist/utils/tty.js +0 -231
  497. package/dist/utils/tty.js.map +0 -1
@@ -1,1041 +0,0 @@
1
- /**
2
- * @module edit-cmd
3
- *
4
- * Top-level `vibe edit` command group for post-production editing.
5
- *
6
- * Commands:
7
- * edit silence-cut - Remove silent segments (FFmpeg / Gemini)
8
- * edit jump-cut - Remove filler words (Whisper + FFmpeg)
9
- * edit caption - Add styled captions (Whisper + FFmpeg)
10
- * edit noise-reduce - Audio/video noise removal (FFmpeg)
11
- * edit fade - Fade in/out effects (FFmpeg)
12
- * edit translate-srt - Translate SRT subtitles (Claude/OpenAI)
13
- * edit grade - Color grading (Claude + FFmpeg)
14
- * edit text-overlay - Text overlays (FFmpeg drawtext)
15
- * edit speed-ramp - Speed ramping (Whisper + Claude + FFmpeg)
16
- * edit reframe - Reframe aspect ratio (Claude Vision + FFmpeg)
17
- * edit image - Image editing (Gemini/OpenAI/Grok)
18
- * edit interpolate - Frame interpolation / slow motion (FFmpeg)
19
- * edit upscale-video - Video upscaling (FFmpeg / Replicate)
20
- * edit fill-gaps - Fill timeline gaps with AI video (Kling)
21
- *
22
- * @dependencies Whisper, Claude, Gemini, Kling, Replicate, FFmpeg
23
- */
24
- import { Command } from "commander";
25
- import { resolve, dirname } from "node:path";
26
- import { readFile, writeFile, mkdir } from "node:fs/promises";
27
- import chalk from "chalk";
28
- import ora from "ora";
29
- import { WhisperProvider, ClaudeProvider, GeminiProvider, OpenAIImageProvider, GrokProvider, } from "@vibeframe/ai-providers";
30
- import { requireApiKey } from "../utils/api-key.js";
31
- import { execSafe, commandExists } from "../utils/exec-safe.js";
32
- import { formatTime } from "./ai-helpers.js";
33
- import { applyTextOverlays } from "./ai-edit.js";
34
- import { registerEditCommands } from "./ai-edit-cli.js";
35
- import { registerFillGapsCommand } from "./ai-fill-gaps.js";
36
- import { isJsonMode, outputResult, exitWithError, usageError, notFoundError, apiError, generalError } from "./output.js";
37
- import { rejectControlChars, validateOutputPath } from "./validate.js";
38
- export const editCommand = new Command("edit")
39
- .alias("ed")
40
- .description("Edit and post-process media (silence-cut, caption, grade, reframe, upscale...)")
41
- .addHelpText("after", `
42
- Examples:
43
- $ vibe edit silence-cut interview.mp4 -o clean.mp4
44
- $ vibe edit caption video.mp4 -o captioned.mp4 -s bold
45
- $ vibe edit grade video.mp4 -o graded.mp4 --preset cinematic-warm
46
- $ vibe edit reframe landscape.mp4 -o vertical.mp4 -a 9:16
47
- $ vibe edit image photo.png "add sunset background" -o edited.png
48
- $ vibe edit text-overlay video.mp4 -t "Title" -s center-bold -o out.mp4
49
- $ vibe edit noise-reduce noisy.mp4 -o clean.mp4 -s high
50
- $ vibe edit fade video.mp4 -o faded.mp4 --fade-in 1 --fade-out 1
51
-
52
- API Keys (varies by subcommand):
53
- No key needed silence-cut, noise-reduce, fade, interpolate, text-overlay
54
- OPENAI_API_KEY caption, jump-cut (Whisper transcription)
55
- ANTHROPIC_API_KEY grade, speed-ramp, reframe (Claude analysis)
56
- GOOGLE_API_KEY image editing (Gemini, default)
57
-
58
- Run 'vibe schema edit.<command>' for structured parameter info.
59
- `);
60
- // ── edit silence-cut, jump-cut, caption, noise-reduce, fade, translate-srt ──
61
- registerEditCommands(editCommand);
62
- // ── edit fill-gaps ──────────────────────────────────────────────────────
63
- registerFillGapsCommand(editCommand);
64
- // ── edit grade ──────────────────────────────────────────────────────────
65
- editCommand
66
- .command("grade")
67
- .description("Apply AI-generated color grading (Claude + FFmpeg)")
68
- .argument("<video>", "Video file path")
69
- .option("-s, --style <prompt>", "Style description (e.g., 'cinematic warm')")
70
- .option("--preset <name>", "Built-in preset: film-noir, vintage, cinematic-warm, cool-tones, high-contrast, pastel, cyberpunk, horror")
71
- .option("-o, --output <path>", "Output video file path")
72
- .option("--analyze-only", "Show filter without applying")
73
- .option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)")
74
- .option("--dry-run", "Preview parameters without executing")
75
- .action(async (videoPath, options) => {
76
- try {
77
- if (options.style)
78
- rejectControlChars(options.style);
79
- if (options.output) {
80
- validateOutputPath(options.output);
81
- }
82
- if (!options.style && !options.preset) {
83
- exitWithError(usageError("Either --style or --preset is required", 'Examples: vibe edit grade video.mp4 --style "warm sunset" or --preset cinematic-warm'));
84
- }
85
- // Check FFmpeg
86
- if (!commandExists("ffmpeg")) {
87
- exitWithError(notFoundError("FFmpeg not found. Install with: brew install ffmpeg"));
88
- }
89
- if (options.dryRun) {
90
- outputResult({
91
- dryRun: true,
92
- command: "edit grade",
93
- params: {
94
- videoPath: resolve(process.cwd(), videoPath),
95
- style: options.style || options.preset,
96
- analyzeOnly: options.analyzeOnly || false,
97
- },
98
- });
99
- return;
100
- }
101
- const spinner = ora("Analyzing color grade...").start();
102
- // Get API key if using style (not preset)
103
- let gradeResult;
104
- if (options.preset) {
105
- const claude = new ClaudeProvider();
106
- gradeResult = await claude.analyzeColorGrade("", options.preset);
107
- }
108
- else {
109
- let apiKey;
110
- try {
111
- apiKey = await requireApiKey("ANTHROPIC_API_KEY", "Anthropic", options.apiKey);
112
- }
113
- catch (err) {
114
- spinner.fail(err.message);
115
- return;
116
- }
117
- const claude = new ClaudeProvider();
118
- await claude.initialize({ apiKey });
119
- gradeResult = await claude.analyzeColorGrade(options.style);
120
- }
121
- spinner.succeed(chalk.green("Color grade analyzed"));
122
- if (isJsonMode()) {
123
- const absPath = resolve(process.cwd(), videoPath);
124
- const gradeOutputPath = options.output
125
- ? resolve(process.cwd(), options.output)
126
- : absPath.replace(/(\.[^.]+)$/, "-graded$1");
127
- outputResult({
128
- success: true,
129
- style: options.preset || options.style,
130
- description: gradeResult.description,
131
- ffmpegFilter: gradeResult.ffmpegFilter,
132
- outputPath: options.analyzeOnly ? undefined : gradeOutputPath,
133
- });
134
- return;
135
- }
136
- console.log();
137
- console.log(chalk.bold.cyan("Color Grade"));
138
- console.log(chalk.dim("─".repeat(60)));
139
- console.log(`Style: ${options.preset || options.style}`);
140
- console.log(`Description: ${gradeResult.description}`);
141
- console.log();
142
- console.log(chalk.dim("FFmpeg filter:"));
143
- console.log(chalk.cyan(gradeResult.ffmpegFilter));
144
- console.log();
145
- if (options.analyzeOnly) {
146
- console.log(chalk.dim("Use without --analyze-only to apply the grade."));
147
- return;
148
- }
149
- const absPath = resolve(process.cwd(), videoPath);
150
- const outputPath = options.output
151
- ? resolve(process.cwd(), options.output)
152
- : absPath.replace(/(\.[^.]+)$/, "-graded$1");
153
- spinner.start("Applying color grade...");
154
- await execSafe("ffmpeg", ["-i", absPath, "-vf", gradeResult.ffmpegFilter, "-c:a", "copy", outputPath, "-y"], { timeout: 600000 });
155
- spinner.succeed(chalk.green("Color grade applied"));
156
- console.log(chalk.green(`Output: ${outputPath}`));
157
- console.log();
158
- }
159
- catch (error) {
160
- exitWithError(apiError(`Color grading failed: ${error.message}`));
161
- }
162
- });
163
- // ── edit text-overlay ───────────────────────────────────────────────────
164
- editCommand
165
- .command("text-overlay")
166
- .description("Apply text overlays to video (FFmpeg drawtext)")
167
- .argument("<video>", "Video file path")
168
- .option("-t, --text <texts...>", "Text lines to overlay (repeat for multiple)")
169
- .option("-s, --style <style>", "Overlay style: lower-third, center-bold, subtitle, minimal", "lower-third")
170
- .option("--font-size <size>", "Font size in pixels (auto-calculated if omitted)")
171
- .option("--font-color <color>", "Font color (default: white)", "white")
172
- .option("--fade <seconds>", "Fade in/out duration in seconds", "0.3")
173
- .option("--start <seconds>", "Start time in seconds", "0")
174
- .option("--end <seconds>", "End time in seconds (default: video duration)")
175
- .option("-o, --output <path>", "Output video file path")
176
- .option("--dry-run", "Preview parameters without executing")
177
- .action(async (videoPath, options) => {
178
- try {
179
- if (!options.text || options.text.length === 0) {
180
- exitWithError(usageError("At least one --text option is required", 'Example: vibe edit text-overlay video.mp4 -t "NEXUS AI" --style center-bold'));
181
- }
182
- for (const t of options.text)
183
- rejectControlChars(t);
184
- if (options.output) {
185
- validateOutputPath(options.output);
186
- }
187
- // Check FFmpeg
188
- if (!commandExists("ffmpeg")) {
189
- exitWithError(generalError("FFmpeg not found", "Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux)"));
190
- }
191
- if (options.dryRun) {
192
- outputResult({
193
- dryRun: true,
194
- command: "edit text-overlay",
195
- params: {
196
- videoPath: resolve(process.cwd(), videoPath),
197
- texts: options.text,
198
- style: options.style,
199
- fontSize: options.fontSize ? parseInt(options.fontSize) : undefined,
200
- fontColor: options.fontColor,
201
- fade: parseFloat(options.fade),
202
- start: parseFloat(options.start),
203
- end: options.end ? parseFloat(options.end) : undefined,
204
- },
205
- });
206
- return;
207
- }
208
- const absPath = resolve(process.cwd(), videoPath);
209
- const outputPath = options.output
210
- ? resolve(process.cwd(), options.output)
211
- : absPath.replace(/(\.[^.]+)$/, "-overlay$1");
212
- const spinner = ora("Applying text overlays...").start();
213
- const result = await applyTextOverlays({
214
- videoPath: absPath,
215
- texts: options.text,
216
- outputPath,
217
- style: options.style,
218
- fontSize: options.fontSize ? parseInt(options.fontSize) : undefined,
219
- fontColor: options.fontColor,
220
- fadeDuration: parseFloat(options.fade),
221
- startTime: parseFloat(options.start),
222
- endTime: options.end ? parseFloat(options.end) : undefined,
223
- });
224
- if (!result.success) {
225
- spinner.fail(result.error || "Text overlay failed");
226
- exitWithError(apiError(result.error || "Text overlay failed", true));
227
- }
228
- spinner.succeed(chalk.green("Text overlays applied"));
229
- if (isJsonMode()) {
230
- outputResult({
231
- success: true,
232
- style: options.style,
233
- texts: options.text,
234
- outputPath: result.outputPath,
235
- });
236
- return;
237
- }
238
- console.log();
239
- console.log(chalk.bold.cyan("Text Overlay"));
240
- console.log(chalk.dim("─".repeat(60)));
241
- console.log(`Style: ${options.style}`);
242
- console.log(`Texts: ${options.text.join(", ")}`);
243
- console.log(`Output: ${result.outputPath}`);
244
- console.log();
245
- }
246
- catch (error) {
247
- const msg = error instanceof Error ? error.message : String(error);
248
- exitWithError(generalError(`Text overlay failed: ${msg}`));
249
- }
250
- });
251
- // ── edit speed-ramp ─────────────────────────────────────────────────────
252
- editCommand
253
- .command("speed-ramp")
254
- .description("Apply content-aware speed ramping (Whisper + Claude + FFmpeg)")
255
- .argument("<video>", "Video file path")
256
- .option("-o, --output <path>", "Output video file path")
257
- .option("-s, --style <style>", "Style: dramatic, smooth, action", "dramatic")
258
- .option("--min-speed <factor>", "Minimum speed factor", "0.25")
259
- .option("--max-speed <factor>", "Maximum speed factor", "4.0")
260
- .option("--analyze-only", "Show keyframes without applying")
261
- .option("-l, --language <lang>", "Language code for transcription")
262
- .option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)")
263
- .option("--dry-run", "Preview parameters without executing")
264
- .action(async (videoPath, options) => {
265
- try {
266
- if (options.output) {
267
- validateOutputPath(options.output);
268
- }
269
- // Check FFmpeg
270
- if (!commandExists("ffmpeg")) {
271
- exitWithError(generalError("FFmpeg not found", "Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux)"));
272
- }
273
- if (options.dryRun) {
274
- outputResult({
275
- dryRun: true,
276
- command: "edit speed-ramp",
277
- params: {
278
- videoPath: resolve(process.cwd(), videoPath),
279
- style: options.style,
280
- minSpeed: parseFloat(options.minSpeed),
281
- maxSpeed: parseFloat(options.maxSpeed),
282
- analyzeOnly: options.analyzeOnly || false,
283
- },
284
- });
285
- return;
286
- }
287
- const openaiApiKey = await requireApiKey("OPENAI_API_KEY", "OpenAI");
288
- const claudeApiKey = await requireApiKey("ANTHROPIC_API_KEY", "Anthropic", options.apiKey);
289
- const absPath = resolve(process.cwd(), videoPath);
290
- // Step 1: Check for audio stream
291
- const spinner = ora("Extracting audio...").start();
292
- const { stdout: speedRampProbe } = await execSafe("ffprobe", [
293
- "-v", "error", "-select_streams", "a", "-show_entries", "stream=codec_type", "-of", "csv=p=0", absPath,
294
- ]);
295
- if (!speedRampProbe.trim()) {
296
- spinner.fail("Video has no audio track");
297
- exitWithError(usageError("Video has no audio stream. Speed ramping requires audio for content-aware analysis.", "Please use a video with an audio track."));
298
- }
299
- const tempAudio = absPath.replace(/(\.[^.]+)$/, "-temp-audio.mp3");
300
- await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
301
- // Step 2: Transcribe
302
- spinner.text = "Transcribing audio...";
303
- const whisper = new WhisperProvider();
304
- await whisper.initialize({ apiKey: openaiApiKey });
305
- const audioBuffer = await readFile(tempAudio);
306
- const audioBlob = new Blob([audioBuffer]);
307
- const transcript = await whisper.transcribe(audioBlob, options.language);
308
- if (!transcript.segments || transcript.segments.length === 0) {
309
- spinner.fail("No transcript segments found");
310
- exitWithError(apiError("No transcript segments found", true));
311
- }
312
- // Step 3: Analyze with Claude
313
- spinner.text = "Analyzing for speed ramping...";
314
- const claude = new ClaudeProvider();
315
- await claude.initialize({ apiKey: claudeApiKey });
316
- const speedResult = await claude.analyzeForSpeedRamp(transcript.segments, {
317
- style: options.style,
318
- minSpeed: parseFloat(options.minSpeed),
319
- maxSpeed: parseFloat(options.maxSpeed),
320
- });
321
- // Clean up temp file
322
- try {
323
- const { unlink } = await import("node:fs/promises");
324
- await unlink(tempAudio);
325
- }
326
- catch { /* ignore cleanup errors */ }
327
- spinner.succeed(chalk.green(`Found ${speedResult.keyframes.length} speed keyframes`));
328
- if (isJsonMode()) {
329
- const avgSpeed = speedResult.keyframes.reduce((sum, kf) => sum + kf.speed, 0) / speedResult.keyframes.length;
330
- const speedRampOutputPath = options.output
331
- ? resolve(process.cwd(), options.output)
332
- : absPath.replace(/(\.[^.]+)$/, "-ramped$1");
333
- outputResult({
334
- success: true,
335
- keyframes: speedResult.keyframes,
336
- avgSpeed,
337
- outputPath: options.analyzeOnly ? undefined : speedRampOutputPath,
338
- });
339
- return;
340
- }
341
- console.log();
342
- console.log(chalk.bold.cyan("Speed Ramp Keyframes"));
343
- console.log(chalk.dim("─".repeat(60)));
344
- for (const kf of speedResult.keyframes) {
345
- const speedColor = kf.speed < 1 ? chalk.blue : kf.speed > 1 ? chalk.yellow : chalk.white;
346
- console.log(` ${formatTime(kf.time)} → ${speedColor(`${kf.speed.toFixed(2)}x`)} - ${kf.reason}`);
347
- }
348
- console.log();
349
- if (options.analyzeOnly) {
350
- console.log(chalk.dim("Use without --analyze-only to apply speed ramps."));
351
- return;
352
- }
353
- if (speedResult.keyframes.length < 2) {
354
- console.log(chalk.yellow("Not enough keyframes for speed ramping."));
355
- return;
356
- }
357
- spinner.start("Applying speed ramps...");
358
- // Build FFmpeg filter for speed ramping (segment-based)
359
- const outputPath = options.output
360
- ? resolve(process.cwd(), options.output)
361
- : absPath.replace(/(\.[^.]+)$/, "-ramped$1");
362
- // For simplicity, we'll create segments and concatenate
363
- // A full implementation would use complex filter expressions
364
- // Here we use setpts with a simple approach
365
- // For demo, apply average speed or first segment's speed
366
- const avgSpeed = speedResult.keyframes.reduce((sum, kf) => sum + kf.speed, 0) / speedResult.keyframes.length;
367
- // Use setpts for speed change (1/speed for setpts)
368
- const setpts = `setpts=${(1 / avgSpeed).toFixed(3)}*PTS`;
369
- const atempo = avgSpeed >= 0.5 && avgSpeed <= 2.0 ? `atempo=${avgSpeed.toFixed(3)}` : "";
370
- if (atempo) {
371
- await execSafe("ffmpeg", ["-i", absPath, "-filter_complex", `[0:v]${setpts}[v];[0:a]${atempo}[a]`, "-map", "[v]", "-map", "[a]", outputPath, "-y"], { timeout: 600000 });
372
- }
373
- else {
374
- await execSafe("ffmpeg", ["-i", absPath, "-vf", setpts, "-an", outputPath, "-y"], { timeout: 600000 });
375
- }
376
- spinner.succeed(chalk.green("Speed ramp applied"));
377
- console.log(chalk.green(`Output: ${outputPath}`));
378
- console.log(chalk.dim(`Average speed: ${avgSpeed.toFixed(2)}x`));
379
- console.log();
380
- }
381
- catch (error) {
382
- const msg = error instanceof Error ? error.message : String(error);
383
- exitWithError(generalError(`Speed ramping failed: ${msg}`));
384
- }
385
- });
386
- // ── edit reframe ────────────────────────────────────────────────────────
387
- editCommand
388
- .command("reframe")
389
- .description("Auto-reframe video to different aspect ratio (Claude Vision + FFmpeg)")
390
- .argument("<video>", "Video file path")
391
- .option("-a, --aspect <ratio>", "Target aspect ratio: 9:16, 1:1, 4:5", "9:16")
392
- .option("-f, --focus <mode>", "Focus mode: auto, face, center, action", "auto")
393
- .option("-o, --output <path>", "Output video file path")
394
- .option("--analyze-only", "Show crop regions without applying")
395
- .option("--keyframes <path>", "Export keyframes to JSON file")
396
- .option("-k, --api-key <key>", "Anthropic API key (or set ANTHROPIC_API_KEY env)")
397
- .option("--dry-run", "Preview parameters without executing")
398
- .action(async (videoPath, options) => {
399
- try {
400
- if (options.output) {
401
- validateOutputPath(options.output);
402
- }
403
- // Check FFmpeg
404
- if (!commandExists("ffmpeg")) {
405
- exitWithError(generalError("FFmpeg not found", "Install with: brew install ffmpeg (macOS) or apt install ffmpeg (Linux)"));
406
- }
407
- if (options.dryRun) {
408
- outputResult({
409
- dryRun: true,
410
- command: "edit reframe",
411
- params: {
412
- videoPath: resolve(process.cwd(), videoPath),
413
- aspect: options.aspect,
414
- focus: options.focus,
415
- analyzeOnly: options.analyzeOnly || false,
416
- },
417
- });
418
- return;
419
- }
420
- const absPath = resolve(process.cwd(), videoPath);
421
- // Get video dimensions
422
- const spinner = ora("Analyzing video...").start();
423
- const { stdout: probeOut } = await execSafe("ffprobe", [
424
- "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", absPath,
425
- ]);
426
- const [width, height, durationStr] = probeOut.trim().split(",");
427
- const sourceWidth = parseInt(width);
428
- const sourceHeight = parseInt(height);
429
- const duration = parseFloat(durationStr);
430
- spinner.text = "Extracting keyframes...";
431
- // Extract keyframes every 2 seconds for analysis
432
- const keyframeInterval = 2;
433
- const numKeyframes = Math.ceil(duration / keyframeInterval);
434
- const tempDir = `/tmp/vibe-reframe-${Date.now()}`;
435
- const { mkdir: mkdirFs } = await import("node:fs/promises");
436
- await mkdirFs(tempDir, { recursive: true });
437
- await execSafe("ffmpeg", ["-i", absPath, "-vf", `fps=1/${keyframeInterval}`, "-frame_pts", "1", `${tempDir}/frame-%04d.jpg`, "-y"]);
438
- // Get API key
439
- let apiKey;
440
- try {
441
- apiKey = await requireApiKey("ANTHROPIC_API_KEY", "Anthropic", options.apiKey);
442
- }
443
- catch (err) {
444
- spinner.fail(err.message);
445
- return;
446
- }
447
- const claude = new ClaudeProvider();
448
- await claude.initialize({ apiKey });
449
- // Analyze keyframes
450
- spinner.text = "Analyzing frames for subject tracking...";
451
- const cropKeyframes = [];
452
- for (let i = 1; i <= numKeyframes && i <= 30; i++) {
453
- // Limit to 30 frames
454
- const framePath = `${tempDir}/frame-${i.toString().padStart(4, "0")}.jpg`;
455
- try {
456
- const frameBuffer = await readFile(framePath);
457
- const frameBase64 = frameBuffer.toString("base64");
458
- const result = await claude.analyzeFrameForReframe(frameBase64, options.aspect, {
459
- focusMode: options.focus,
460
- sourceWidth,
461
- sourceHeight,
462
- mimeType: "image/jpeg",
463
- });
464
- cropKeyframes.push({
465
- time: (i - 1) * keyframeInterval,
466
- ...result,
467
- });
468
- spinner.text = `Analyzing frames... ${i}/${Math.min(numKeyframes, 30)}`;
469
- }
470
- catch {
471
- // Skip failed frames
472
- }
473
- // Small delay to avoid rate limiting
474
- await new Promise((r) => setTimeout(r, 200));
475
- }
476
- // Clean up temp files
477
- try {
478
- const { rm: rmFs } = await import("node:fs/promises");
479
- await rmFs(tempDir, { recursive: true, force: true });
480
- }
481
- catch { /* ignore cleanup errors */ }
482
- spinner.succeed(chalk.green(`Analyzed ${cropKeyframes.length} keyframes`));
483
- if (isJsonMode()) {
484
- const reframeOutputPath = options.output
485
- ? resolve(process.cwd(), options.output)
486
- : absPath.replace(/(\.[^.]+)$/, `-${options.aspect.replace(":", "x")}$1`);
487
- outputResult({
488
- success: true,
489
- sourceWidth,
490
- sourceHeight,
491
- aspect: options.aspect,
492
- cropKeyframes,
493
- outputPath: options.analyzeOnly ? undefined : reframeOutputPath,
494
- });
495
- return;
496
- }
497
- console.log();
498
- console.log(chalk.bold.cyan("Reframe Analysis"));
499
- console.log(chalk.dim("─".repeat(60)));
500
- console.log(`Source: ${sourceWidth}x${sourceHeight}`);
501
- console.log(`Target: ${options.aspect}`);
502
- console.log(`Focus: ${options.focus}`);
503
- console.log();
504
- if (cropKeyframes.length > 0) {
505
- const avgConf = cropKeyframes.reduce((sum, kf) => sum + kf.confidence, 0) / cropKeyframes.length;
506
- console.log(`Average confidence: ${(avgConf * 100).toFixed(0)}%`);
507
- console.log();
508
- console.log(chalk.dim("Sample keyframes:"));
509
- for (const kf of cropKeyframes.slice(0, 5)) {
510
- console.log(` ${formatTime(kf.time)} → crop=${kf.cropX},${kf.cropY} (${kf.subjectDescription})`);
511
- }
512
- if (cropKeyframes.length > 5) {
513
- console.log(chalk.dim(` ... and ${cropKeyframes.length - 5} more`));
514
- }
515
- }
516
- console.log();
517
- // Export keyframes if requested
518
- if (options.keyframes) {
519
- const keyframesPath = resolve(process.cwd(), options.keyframes);
520
- await writeFile(keyframesPath, JSON.stringify(cropKeyframes, null, 2));
521
- console.log(chalk.green(`Keyframes saved to: ${keyframesPath}`));
522
- }
523
- if (options.analyzeOnly) {
524
- console.log(chalk.dim("Use without --analyze-only to apply reframe."));
525
- return;
526
- }
527
- // Apply reframe using average crop position
528
- const avgCropX = Math.round(cropKeyframes.reduce((sum, kf) => sum + kf.cropX, 0) / cropKeyframes.length);
529
- const avgCropY = Math.round(cropKeyframes.reduce((sum, kf) => sum + kf.cropY, 0) / cropKeyframes.length);
530
- const cropWidth = cropKeyframes[0]?.cropWidth || sourceWidth;
531
- const cropHeight = cropKeyframes[0]?.cropHeight || sourceHeight;
532
- const outputPath = options.output
533
- ? resolve(process.cwd(), options.output)
534
- : absPath.replace(/(\.[^.]+)$/, `-${options.aspect.replace(":", "x")}$1`);
535
- spinner.start("Applying reframe...");
536
- await execSafe("ffmpeg", ["-i", absPath, "-vf", `crop=${cropWidth}:${cropHeight}:${avgCropX}:${avgCropY}`, "-c:a", "copy", outputPath, "-y"], { timeout: 600000 });
537
- spinner.succeed(chalk.green("Reframe applied"));
538
- console.log(chalk.green(`Output: ${outputPath}`));
539
- console.log(chalk.dim(`Crop: ${cropWidth}x${cropHeight} at (${avgCropX}, ${avgCropY})`));
540
- console.log();
541
- }
542
- catch (error) {
543
- console.error(chalk.red("Reframe failed"));
544
- const msg = error instanceof Error ? error.message : String(error);
545
- exitWithError(generalError(`Reframe failed: ${msg}`));
546
- }
547
- });
548
- // ── edit image (Gemini multi-image editing) ─────────────────────────────
549
- editCommand
550
- .command("image")
551
- .description("Edit image(s) using AI (Gemini/OpenAI/Grok)")
552
- .argument("<images...>", "Input image file(s) followed by edit prompt")
553
- .option("-p, --provider <provider>", "Provider: gemini (default), openai, grok", "gemini")
554
- .option("-k, --api-key <key>", "API key (or set env variable)")
555
- .option("-o, --output <path>", "Output file path", "edited.png")
556
- .option("-m, --model <model>", "Model: flash/3.1-flash/latest/pro (Gemini only)", "flash")
557
- .option("-r, --ratio <ratio>", "Output aspect ratio")
558
- .option("-s, --size <resolution>", "Resolution: 1K, 2K, 4K (Gemini Pro only)")
559
- .option("--dry-run", "Preview parameters without executing")
560
- .action(async (args, options) => {
561
- try {
562
- // Last argument is the prompt, rest are image paths
563
- if (args.length < 2) {
564
- exitWithError(usageError("Need at least one image and a prompt"));
565
- }
566
- const prompt = args[args.length - 1];
567
- rejectControlChars(prompt);
568
- if (options.output) {
569
- validateOutputPath(options.output);
570
- }
571
- const imagePaths = args.slice(0, -1);
572
- const provider = options.provider;
573
- // Grok only supports 1 image
574
- if (provider === "grok" && imagePaths.length > 1) {
575
- exitWithError(usageError("Grok supports only 1 input image for editing.", "Use -p gemini (up to 14 images) or -p openai (up to 16 images) for multi-image editing."));
576
- }
577
- if (options.dryRun) {
578
- outputResult({
579
- dryRun: true,
580
- command: "edit image",
581
- params: {
582
- imagePaths: imagePaths.map((p) => resolve(process.cwd(), p)),
583
- prompt,
584
- provider,
585
- model: options.model,
586
- ratio: options.ratio,
587
- size: options.size,
588
- },
589
- });
590
- return;
591
- }
592
- // Provider-specific API key resolution
593
- const apiKeyMap = {
594
- gemini: { envVar: "GOOGLE_API_KEY", label: "Google" },
595
- openai: { envVar: "OPENAI_API_KEY", label: "OpenAI" },
596
- grok: { envVar: "XAI_API_KEY", label: "xAI" },
597
- };
598
- const keyInfo = apiKeyMap[provider] || apiKeyMap.gemini;
599
- const apiKey = await requireApiKey(keyInfo.envVar, keyInfo.label, options.apiKey);
600
- const spinner = ora(`Reading ${imagePaths.length} image(s)...`).start();
601
- // Load all images
602
- const imageBuffers = [];
603
- for (const imagePath of imagePaths) {
604
- const absPath = resolve(process.cwd(), imagePath);
605
- const buffer = await readFile(absPath);
606
- imageBuffers.push(buffer);
607
- }
608
- let result;
609
- if (provider === "openai") {
610
- spinner.text = "Editing with GPT Image 1.5...";
611
- const openaiImage = new OpenAIImageProvider();
612
- await openaiImage.initialize({ apiKey });
613
- result = await openaiImage.editImage(imageBuffers, prompt);
614
- }
615
- else if (provider === "grok") {
616
- spinner.text = "Editing with Grok Imagine...";
617
- const grok = new GrokProvider();
618
- await grok.initialize({ apiKey });
619
- result = await grok.editImage(imageBuffers[0], prompt, {
620
- aspectRatio: options.ratio,
621
- });
622
- }
623
- else {
624
- // Gemini (default)
625
- const editModelNames = {
626
- flash: "gemini-2.5-flash-image",
627
- "3.1-flash": "gemini-3.1-flash-image-preview",
628
- latest: "gemini-3.1-flash-image-preview",
629
- pro: "gemini-3-pro-image-preview",
630
- };
631
- const editModelName = editModelNames[options.model] || editModelNames.flash;
632
- spinner.text = `Editing with ${editModelName}...`;
633
- const gemini = new GeminiProvider();
634
- await gemini.initialize({ apiKey });
635
- result = await gemini.editImage(imageBuffers, prompt, {
636
- model: options.model,
637
- aspectRatio: options.ratio,
638
- resolution: options.size,
639
- });
640
- // Auto-fallback: if latest/3.1-flash fails, retry with flash
641
- const fallbackModels = ["latest", "3.1-flash"];
642
- if (!result.success && fallbackModels.includes(options.model)) {
643
- spinner.text = `${chalk.dim(result.error || `${editModelName} failed`)} — retrying with flash...`;
644
- result = await gemini.editImage(imageBuffers, prompt, {
645
- model: "flash",
646
- aspectRatio: options.ratio,
647
- resolution: options.size,
648
- });
649
- }
650
- }
651
- if (!result.success || !result.images || result.images.length === 0) {
652
- spinner.fail(result.error || "Image editing failed");
653
- exitWithError(apiError(result.error || "Image editing failed", true));
654
- }
655
- spinner.succeed(chalk.green("Image edited"));
656
- // Save image — handle both base64 and URL responses
657
- const img = result.images[0];
658
- const outputPath = resolve(process.cwd(), options.output);
659
- const saveImage = async () => {
660
- await mkdir(dirname(outputPath), { recursive: true });
661
- if (img.base64) {
662
- const buffer = Buffer.from(img.base64, "base64");
663
- await writeFile(outputPath, buffer);
664
- }
665
- else if (img.url) {
666
- const resp = await fetch(img.url);
667
- const arrayBuf = await resp.arrayBuffer();
668
- await writeFile(outputPath, Buffer.from(arrayBuf));
669
- }
670
- };
671
- // Gemini results may include a `model` field
672
- const resultModel = result.model;
673
- if (isJsonMode()) {
674
- outputResult({
675
- success: true,
676
- provider,
677
- model: resultModel || options.model,
678
- outputPath,
679
- });
680
- await saveImage();
681
- return;
682
- }
683
- if (resultModel) {
684
- console.log(chalk.dim(`Model: ${resultModel}`));
685
- }
686
- await saveImage();
687
- console.log(chalk.green(`Saved to: ${outputPath}`));
688
- }
689
- catch (error) {
690
- const msg = error instanceof Error ? error.message : String(error);
691
- exitWithError(apiError(`Image editing failed: ${msg}`, true));
692
- }
693
- });
694
- // ── edit interpolate (frame interpolation / slow motion) ────────────────
695
- editCommand
696
- .command("interpolate")
697
- .description("Create slow motion with frame interpolation (FFmpeg)")
698
- .argument("<video>", "Video file path")
699
- .option("-o, --output <path>", "Output file path")
700
- .option("-f, --factor <number>", "Slow motion factor: 2, 4, or 8", "2")
701
- .option("--fps <number>", "Target output FPS")
702
- .option("-q, --quality <mode>", "Quality: fast or quality", "quality")
703
- .option("--dry-run", "Preview parameters without executing")
704
- .action(async (videoPath, options) => {
705
- try {
706
- if (options.output) {
707
- validateOutputPath(options.output);
708
- }
709
- const absPath = resolve(process.cwd(), videoPath);
710
- const factor = parseInt(options.factor);
711
- if (![2, 4, 8].includes(factor)) {
712
- exitWithError(usageError("Factor must be 2, 4, or 8"));
713
- }
714
- if (options.dryRun) {
715
- outputResult({
716
- dryRun: true,
717
- command: "edit interpolate",
718
- params: {
719
- videoPath: absPath,
720
- factor,
721
- fps: options.fps ? parseInt(options.fps) : undefined,
722
- quality: options.quality,
723
- },
724
- });
725
- return;
726
- }
727
- const outputPath = options.output
728
- ? resolve(process.cwd(), options.output)
729
- : absPath.replace(/(\.[^.]+)$/, `-slow${factor}x$1`);
730
- const spinner = ora(`Creating ${factor}x slow motion...`).start();
731
- try {
732
- // Get original FPS
733
- const { stdout: fpsOut } = await execSafe("ffprobe", [
734
- "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=r_frame_rate", "-of", "default=noprint_wrappers=1:nokey=1", absPath,
735
- ]);
736
- const [num, den] = fpsOut.trim().split("/").map(Number);
737
- const originalFps = num / (den || 1);
738
- // Calculate target FPS
739
- const targetFps = options.fps ? parseInt(options.fps) : originalFps * factor;
740
- // Use minterpolate for frame interpolation
741
- const mi = options.quality === "fast" ? "mi_mode=mci" : "mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1";
742
- spinner.text = `Interpolating frames (${originalFps.toFixed(1)} → ${targetFps}fps)...`;
743
- // First interpolate frames, then slow down
744
- await execSafe("ffmpeg", ["-i", absPath, "-filter:v", `minterpolate='${mi}:fps=${targetFps}',setpts=${factor}*PTS`, "-an", outputPath, "-y"], { timeout: 600000 });
745
- spinner.succeed(chalk.green(`Created ${factor}x slow motion`));
746
- if (isJsonMode()) {
747
- outputResult({
748
- success: true,
749
- originalFps,
750
- targetFps,
751
- factor,
752
- outputPath,
753
- });
754
- return;
755
- }
756
- console.log();
757
- console.log(chalk.dim("─".repeat(60)));
758
- console.log(`Original FPS: ${originalFps.toFixed(1)}`);
759
- console.log(`Interpolated FPS: ${targetFps}`);
760
- console.log(`Slow factor: ${factor}x`);
761
- console.log(`Output: ${outputPath}`);
762
- console.log();
763
- }
764
- catch (err) {
765
- spinner.fail("Frame interpolation failed");
766
- if (err instanceof Error && err.message.includes("timeout")) {
767
- exitWithError(generalError("Frame interpolation timed out", "Try with a shorter video or --quality fast"));
768
- }
769
- else {
770
- const msg = err instanceof Error ? err.message : String(err);
771
- exitWithError(generalError(`Frame interpolation failed: ${msg}`));
772
- }
773
- }
774
- }
775
- catch (error) {
776
- const msg = error instanceof Error ? error.message : String(error);
777
- exitWithError(generalError(`Frame interpolation failed: ${msg}`));
778
- }
779
- });
780
- // ── edit upscale-video (video upscaling) ────────────────────────────────
781
- editCommand
782
- .command("upscale-video")
783
- .description("Upscale video resolution using AI or FFmpeg")
784
- .argument("<video>", "Video file path")
785
- .option("-o, --output <path>", "Output file path")
786
- .option("-s, --scale <factor>", "Scale factor: 2 or 4", "2")
787
- .option("-m, --model <model>", "Model: real-esrgan, topaz", "real-esrgan")
788
- .option("--ffmpeg", "Use FFmpeg lanczos (free, no API)")
789
- .option("-k, --api-key <key>", "Replicate API token (or set REPLICATE_API_TOKEN env)")
790
- .option("--no-wait", "Start processing and return task ID without waiting")
791
- .option("--dry-run", "Preview parameters without executing")
792
- .action(async (videoPath, options) => {
793
- try {
794
- if (options.output) {
795
- validateOutputPath(options.output);
796
- }
797
- const absPath = resolve(process.cwd(), videoPath);
798
- const scale = parseInt(options.scale);
799
- if (scale !== 2 && scale !== 4) {
800
- exitWithError(usageError("Scale must be 2 or 4"));
801
- }
802
- if (options.dryRun) {
803
- outputResult({
804
- dryRun: true,
805
- command: "edit upscale-video",
806
- params: {
807
- videoPath: absPath,
808
- scale,
809
- model: options.model,
810
- ffmpeg: options.ffmpeg || false,
811
- },
812
- });
813
- return;
814
- }
815
- // Use FFmpeg if requested (free fallback)
816
- if (options.ffmpeg) {
817
- const outputPath = options.output
818
- ? resolve(process.cwd(), options.output)
819
- : absPath.replace(/(\.[^.]+)$/, `-upscaled-${scale}x$1`);
820
- const spinner = ora(`Upscaling video with FFmpeg (${scale}x)...`).start();
821
- try {
822
- // Get original dimensions
823
- const { stdout: probeOut } = await execSafe("ffprobe", [
824
- "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of", "csv=p=0", absPath,
825
- ]);
826
- const [width, height] = probeOut.trim().split(",").map(Number);
827
- const newWidth = width * scale;
828
- const newHeight = height * scale;
829
- // Use lanczos scaling
830
- await execSafe("ffmpeg", ["-i", absPath, "-vf", `scale=${newWidth}:${newHeight}:flags=lanczos`, "-c:a", "copy", outputPath, "-y"]);
831
- spinner.succeed(chalk.green(`Upscaled to ${newWidth}x${newHeight}`));
832
- if (isJsonMode()) {
833
- outputResult({
834
- success: true,
835
- dimensions: `${newWidth}x${newHeight}`,
836
- outputPath,
837
- });
838
- return;
839
- }
840
- console.log(`Output: ${outputPath}`);
841
- }
842
- catch (err) {
843
- spinner.fail("FFmpeg upscaling failed");
844
- const msg = err instanceof Error ? err.message : String(err);
845
- exitWithError(generalError(`FFmpeg upscaling failed: ${msg}`));
846
- }
847
- return;
848
- }
849
- // Use Replicate API
850
- const apiKey = await requireApiKey("REPLICATE_API_TOKEN", "Replicate", options.apiKey);
851
- const spinner = ora("Initializing Replicate...").start();
852
- const { ReplicateProvider } = await import("@vibeframe/ai-providers");
853
- const replicate = new ReplicateProvider();
854
- await replicate.initialize({ apiKey });
855
- // For Replicate, we need a URL. Upload to temporary hosting or require URL
856
- spinner.text = "Note: Replicate requires video URL. Reading file...";
857
- // For now, we'll show an error suggesting URL or ffmpeg
858
- spinner.fail("Replicate requires a video URL");
859
- exitWithError(usageError("Replicate requires a video URL", "Use --ffmpeg for local processing, or upload video to a URL."));
860
- }
861
- catch (error) {
862
- const msg = error instanceof Error ? error.message : String(error);
863
- exitWithError(generalError(`Video upscaling failed: ${msg}`));
864
- }
865
- });
866
- export async function executeGrade(options) {
867
- const { videoPath, style, preset, output, analyzeOnly, apiKey } = options;
868
- try {
869
- if (!style && !preset)
870
- return { success: false, error: "Either style or preset is required" };
871
- if (!commandExists("ffmpeg"))
872
- return { success: false, error: "FFmpeg not found" };
873
- const absPath = resolve(process.cwd(), videoPath);
874
- let gradeResult;
875
- if (preset) {
876
- const claude = new ClaudeProvider();
877
- gradeResult = await claude.analyzeColorGrade("", preset);
878
- }
879
- else {
880
- const key = apiKey || process.env.ANTHROPIC_API_KEY;
881
- if (!key)
882
- return { success: false, error: "ANTHROPIC_API_KEY required for custom style grading" };
883
- const claude = new ClaudeProvider();
884
- await claude.initialize({ apiKey: key });
885
- gradeResult = await claude.analyzeColorGrade(style);
886
- }
887
- if (analyzeOnly) {
888
- return { success: true, style: preset || style, description: gradeResult.description, ffmpegFilter: gradeResult.ffmpegFilter };
889
- }
890
- const outputPath = output ? resolve(process.cwd(), output) : absPath.replace(/(\.[^.]+)$/, "-graded$1");
891
- await execSafe("ffmpeg", ["-i", absPath, "-vf", gradeResult.ffmpegFilter, "-c:a", "copy", outputPath, "-y"], { timeout: 600000 });
892
- return { success: true, outputPath, style: preset || style, description: gradeResult.description, ffmpegFilter: gradeResult.ffmpegFilter };
893
- }
894
- catch (error) {
895
- return { success: false, error: `Color grading failed: ${error instanceof Error ? error.message : String(error)}` };
896
- }
897
- }
898
- export async function executeSpeedRamp(options) {
899
- const { videoPath, output, style = "dramatic", minSpeed = 0.25, maxSpeed = 4.0, analyzeOnly, language, apiKey } = options;
900
- try {
901
- if (!commandExists("ffmpeg"))
902
- return { success: false, error: "FFmpeg not found" };
903
- const openaiKey = process.env.OPENAI_API_KEY;
904
- const claudeKey = apiKey || process.env.ANTHROPIC_API_KEY;
905
- if (!openaiKey)
906
- return { success: false, error: "OPENAI_API_KEY required for Whisper transcription" };
907
- if (!claudeKey)
908
- return { success: false, error: "ANTHROPIC_API_KEY required for Claude analysis" };
909
- const absPath = resolve(process.cwd(), videoPath);
910
- // Extract audio
911
- const tempAudio = absPath.replace(/(\.[^.]+)$/, "-temp-audio.mp3");
912
- await execSafe("ffmpeg", ["-i", absPath, "-vn", "-acodec", "libmp3lame", "-q:a", "2", tempAudio, "-y"]);
913
- // Transcribe
914
- const whisper = new WhisperProvider();
915
- await whisper.initialize({ apiKey: openaiKey });
916
- const audioBuffer = await readFile(tempAudio);
917
- const transcript = await whisper.transcribe(new Blob([audioBuffer]), language);
918
- // Cleanup temp
919
- try {
920
- const { unlink } = await import("node:fs/promises");
921
- await unlink(tempAudio);
922
- }
923
- catch { /* best-effort */ }
924
- if (!transcript.segments || transcript.segments.length === 0) {
925
- return { success: false, error: "No transcript segments found" };
926
- }
927
- // Analyze with Claude
928
- const claude = new ClaudeProvider();
929
- await claude.initialize({ apiKey: claudeKey });
930
- const speedResult = await claude.analyzeForSpeedRamp(transcript.segments, { style, minSpeed, maxSpeed });
931
- const avgSpeed = speedResult.keyframes.reduce((sum, kf) => sum + kf.speed, 0) / speedResult.keyframes.length;
932
- if (analyzeOnly) {
933
- return { success: true, keyframes: speedResult.keyframes, avgSpeed };
934
- }
935
- if (speedResult.keyframes.length < 2) {
936
- return { success: false, error: "Not enough keyframes for speed ramping" };
937
- }
938
- const outputPath = output ? resolve(process.cwd(), output) : absPath.replace(/(\.[^.]+)$/, "-ramped$1");
939
- const setpts = `setpts=${(1 / avgSpeed).toFixed(3)}*PTS`;
940
- const atempo = avgSpeed >= 0.5 && avgSpeed <= 2.0 ? `atempo=${avgSpeed.toFixed(3)}` : "";
941
- if (atempo) {
942
- await execSafe("ffmpeg", ["-i", absPath, "-filter_complex", `[0:v]${setpts}[v];[0:a]${atempo}[a]`, "-map", "[v]", "-map", "[a]", outputPath, "-y"], { timeout: 600000 });
943
- }
944
- else {
945
- await execSafe("ffmpeg", ["-i", absPath, "-vf", setpts, "-an", outputPath, "-y"], { timeout: 600000 });
946
- }
947
- return { success: true, outputPath, keyframes: speedResult.keyframes, avgSpeed };
948
- }
949
- catch (error) {
950
- return { success: false, error: `Speed ramping failed: ${error instanceof Error ? error.message : String(error)}` };
951
- }
952
- }
953
- export async function executeReframe(options) {
954
- const { videoPath, aspect = "9:16", output, analyzeOnly } = options;
955
- try {
956
- if (!commandExists("ffmpeg"))
957
- return { success: false, error: "FFmpeg not found" };
958
- const absPath = resolve(process.cwd(), videoPath);
959
- // Get video dimensions
960
- const { stdout: probeOut } = await execSafe("ffprobe", [
961
- "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height,duration", "-of", "csv=p=0", absPath,
962
- ]);
963
- const [widthStr, heightStr] = probeOut.trim().split(",");
964
- const sourceWidth = parseInt(widthStr);
965
- const sourceHeight = parseInt(heightStr);
966
- // Parse target aspect ratio
967
- const [tw, th] = aspect.split(":").map(Number);
968
- const targetRatio = tw / th;
969
- let cropWidth, cropHeight;
970
- if (targetRatio > sourceWidth / sourceHeight) {
971
- cropWidth = sourceWidth;
972
- cropHeight = Math.round(sourceWidth / targetRatio);
973
- }
974
- else {
975
- cropHeight = sourceHeight;
976
- cropWidth = Math.round(sourceHeight * targetRatio);
977
- }
978
- // Center crop (simple reframe without AI for MCP — Claude Vision analysis is expensive per frame)
979
- const cropX = Math.round((sourceWidth - cropWidth) / 2);
980
- const cropY = Math.round((sourceHeight - cropHeight) / 2);
981
- if (analyzeOnly) {
982
- return {
983
- success: true,
984
- sourceAspect: `${sourceWidth}:${sourceHeight}`,
985
- targetAspect: aspect,
986
- keyframeCount: 1,
987
- };
988
- }
989
- const outputPath = output ? resolve(process.cwd(), output) : absPath.replace(/(\.[^.]+)$/, `-${aspect.replace(":", "x")}$1`);
990
- await execSafe("ffmpeg", ["-i", absPath, "-vf", `crop=${cropWidth}:${cropHeight}:${cropX}:${cropY}`, "-c:a", "copy", outputPath, "-y"], { timeout: 600000 });
991
- return { success: true, outputPath, sourceAspect: `${sourceWidth}:${sourceHeight}`, targetAspect: aspect };
992
- }
993
- catch (error) {
994
- return { success: false, error: `Reframe failed: ${error instanceof Error ? error.message : String(error)}` };
995
- }
996
- }
997
- export async function executeInterpolate(options) {
998
- const { videoPath, output, factor = 2, quality = "quality" } = options;
999
- try {
1000
- if (!commandExists("ffmpeg"))
1001
- return { success: false, error: "FFmpeg not found" };
1002
- if (![2, 4, 8].includes(factor))
1003
- return { success: false, error: "Factor must be 2, 4, or 8" };
1004
- const absPath = resolve(process.cwd(), videoPath);
1005
- const { stdout: fpsOut } = await execSafe("ffprobe", [
1006
- "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=r_frame_rate", "-of", "default=noprint_wrappers=1:nokey=1", absPath,
1007
- ]);
1008
- const [num, den] = fpsOut.trim().split("/").map(Number);
1009
- const originalFps = num / (den || 1);
1010
- const targetFps = options.fps || originalFps * factor;
1011
- const mi = quality === "fast" ? "mi_mode=mci" : "mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1";
1012
- const outputPath = output ? resolve(process.cwd(), output) : absPath.replace(/(\.[^.]+)$/, `-slow${factor}x$1`);
1013
- await execSafe("ffmpeg", ["-i", absPath, "-filter:v", `minterpolate='${mi}:fps=${targetFps}',setpts=${factor}*PTS`, "-an", outputPath, "-y"], { timeout: 600000 });
1014
- return { success: true, outputPath, originalFps, targetFps, factor };
1015
- }
1016
- catch (error) {
1017
- return { success: false, error: `Frame interpolation failed: ${error instanceof Error ? error.message : String(error)}` };
1018
- }
1019
- }
1020
- export async function executeUpscale(options) {
1021
- const { videoPath, output, scale = 2, quality = "quality" } = options;
1022
- try {
1023
- if (!commandExists("ffmpeg"))
1024
- return { success: false, error: "FFmpeg not found" };
1025
- const absPath = resolve(process.cwd(), videoPath);
1026
- const { stdout: probeOut } = await execSafe("ffprobe", [
1027
- "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of", "csv=p=0", absPath,
1028
- ]);
1029
- const [w, h] = probeOut.trim().split(",").map(Number);
1030
- const targetW = w * scale;
1031
- const targetH = h * scale;
1032
- const scaleFilter = quality === "quality" ? `scale=${targetW}:${targetH}:flags=lanczos` : `scale=${targetW}:${targetH}`;
1033
- const outputPath = output ? resolve(process.cwd(), output) : absPath.replace(/(\.[^.]+)$/, `-${scale}x$1`);
1034
- await execSafe("ffmpeg", ["-i", absPath, "-vf", scaleFilter, "-c:a", "copy", outputPath, "-y"], { timeout: 600000 });
1035
- return { success: true, outputPath, originalRes: `${w}x${h}`, targetRes: `${targetW}x${targetH}` };
1036
- }
1037
- catch (error) {
1038
- return { success: false, error: `Upscale failed: ${error instanceof Error ? error.message : String(error)}` };
1039
- }
1040
- }
1041
- //# sourceMappingURL=edit-cmd.js.map