@vibeframe/cli 0.27.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 (420) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-lint.log +21 -0
  3. package/.turbo/turbo-test.log +689 -0
  4. package/dist/agent/adapters/claude.d.ts +15 -0
  5. package/dist/agent/adapters/claude.d.ts.map +1 -0
  6. package/dist/agent/adapters/claude.js +119 -0
  7. package/dist/agent/adapters/claude.js.map +1 -0
  8. package/dist/agent/adapters/gemini.d.ts +15 -0
  9. package/dist/agent/adapters/gemini.d.ts.map +1 -0
  10. package/dist/agent/adapters/gemini.js +132 -0
  11. package/dist/agent/adapters/gemini.js.map +1 -0
  12. package/dist/agent/adapters/index.d.ts +27 -0
  13. package/dist/agent/adapters/index.d.ts.map +1 -0
  14. package/dist/agent/adapters/index.js +38 -0
  15. package/dist/agent/adapters/index.js.map +1 -0
  16. package/dist/agent/adapters/ollama.d.ts +20 -0
  17. package/dist/agent/adapters/ollama.d.ts.map +1 -0
  18. package/dist/agent/adapters/ollama.js +186 -0
  19. package/dist/agent/adapters/ollama.js.map +1 -0
  20. package/dist/agent/adapters/openai.d.ts +15 -0
  21. package/dist/agent/adapters/openai.d.ts.map +1 -0
  22. package/dist/agent/adapters/openai.js +92 -0
  23. package/dist/agent/adapters/openai.js.map +1 -0
  24. package/dist/agent/adapters/xai.d.ts +15 -0
  25. package/dist/agent/adapters/xai.d.ts.map +1 -0
  26. package/dist/agent/adapters/xai.js +95 -0
  27. package/dist/agent/adapters/xai.js.map +1 -0
  28. package/dist/agent/index.d.ts +69 -0
  29. package/dist/agent/index.d.ts.map +1 -0
  30. package/dist/agent/index.js +180 -0
  31. package/dist/agent/index.js.map +1 -0
  32. package/dist/agent/memory/index.d.ts +70 -0
  33. package/dist/agent/memory/index.d.ts.map +1 -0
  34. package/dist/agent/memory/index.js +132 -0
  35. package/dist/agent/memory/index.js.map +1 -0
  36. package/dist/agent/prompts/system.d.ts +6 -0
  37. package/dist/agent/prompts/system.d.ts.map +1 -0
  38. package/dist/agent/prompts/system.js +103 -0
  39. package/dist/agent/prompts/system.js.map +1 -0
  40. package/dist/agent/tools/ai-editing.d.ts +15 -0
  41. package/dist/agent/tools/ai-editing.d.ts.map +1 -0
  42. package/dist/agent/tools/ai-editing.js +763 -0
  43. package/dist/agent/tools/ai-editing.js.map +1 -0
  44. package/dist/agent/tools/ai-generation.d.ts +13 -0
  45. package/dist/agent/tools/ai-generation.d.ts.map +1 -0
  46. package/dist/agent/tools/ai-generation.js +973 -0
  47. package/dist/agent/tools/ai-generation.js.map +1 -0
  48. package/dist/agent/tools/ai-pipeline.d.ts +14 -0
  49. package/dist/agent/tools/ai-pipeline.d.ts.map +1 -0
  50. package/dist/agent/tools/ai-pipeline.js +961 -0
  51. package/dist/agent/tools/ai-pipeline.js.map +1 -0
  52. package/dist/agent/tools/ai.d.ts +13 -0
  53. package/dist/agent/tools/ai.d.ts.map +1 -0
  54. package/dist/agent/tools/ai.js +19 -0
  55. package/dist/agent/tools/ai.js.map +1 -0
  56. package/dist/agent/tools/batch.d.ts +6 -0
  57. package/dist/agent/tools/batch.d.ts.map +1 -0
  58. package/dist/agent/tools/batch.js +383 -0
  59. package/dist/agent/tools/batch.js.map +1 -0
  60. package/dist/agent/tools/e2e.test.d.ts +26 -0
  61. package/dist/agent/tools/e2e.test.d.ts.map +1 -0
  62. package/dist/agent/tools/e2e.test.js +397 -0
  63. package/dist/agent/tools/e2e.test.js.map +1 -0
  64. package/dist/agent/tools/export.d.ts +6 -0
  65. package/dist/agent/tools/export.d.ts.map +1 -0
  66. package/dist/agent/tools/export.js +171 -0
  67. package/dist/agent/tools/export.js.map +1 -0
  68. package/dist/agent/tools/filesystem.d.ts +6 -0
  69. package/dist/agent/tools/filesystem.d.ts.map +1 -0
  70. package/dist/agent/tools/filesystem.js +212 -0
  71. package/dist/agent/tools/filesystem.js.map +1 -0
  72. package/dist/agent/tools/index.d.ts +65 -0
  73. package/dist/agent/tools/index.d.ts.map +1 -0
  74. package/dist/agent/tools/index.js +120 -0
  75. package/dist/agent/tools/index.js.map +1 -0
  76. package/dist/agent/tools/integration.test.d.ts +11 -0
  77. package/dist/agent/tools/integration.test.d.ts.map +1 -0
  78. package/dist/agent/tools/integration.test.js +659 -0
  79. package/dist/agent/tools/integration.test.js.map +1 -0
  80. package/dist/agent/tools/media.d.ts +6 -0
  81. package/dist/agent/tools/media.d.ts.map +1 -0
  82. package/dist/agent/tools/media.js +616 -0
  83. package/dist/agent/tools/media.js.map +1 -0
  84. package/dist/agent/tools/project.d.ts +6 -0
  85. package/dist/agent/tools/project.d.ts.map +1 -0
  86. package/dist/agent/tools/project.js +284 -0
  87. package/dist/agent/tools/project.js.map +1 -0
  88. package/dist/agent/tools/timeline.d.ts +6 -0
  89. package/dist/agent/tools/timeline.d.ts.map +1 -0
  90. package/dist/agent/tools/timeline.js +873 -0
  91. package/dist/agent/tools/timeline.js.map +1 -0
  92. package/dist/agent/types.d.ts +59 -0
  93. package/dist/agent/types.d.ts.map +1 -0
  94. package/dist/agent/types.js +5 -0
  95. package/dist/agent/types.js.map +1 -0
  96. package/dist/commands/agent.d.ts +21 -0
  97. package/dist/commands/agent.d.ts.map +1 -0
  98. package/dist/commands/agent.js +290 -0
  99. package/dist/commands/agent.js.map +1 -0
  100. package/dist/commands/ai-analyze.d.ts +106 -0
  101. package/dist/commands/ai-analyze.d.ts.map +1 -0
  102. package/dist/commands/ai-analyze.js +327 -0
  103. package/dist/commands/ai-analyze.js.map +1 -0
  104. package/dist/commands/ai-animated-caption.d.ts +64 -0
  105. package/dist/commands/ai-animated-caption.d.ts.map +1 -0
  106. package/dist/commands/ai-animated-caption.js +272 -0
  107. package/dist/commands/ai-animated-caption.js.map +1 -0
  108. package/dist/commands/ai-audio.d.ts +20 -0
  109. package/dist/commands/ai-audio.d.ts.map +1 -0
  110. package/dist/commands/ai-audio.js +808 -0
  111. package/dist/commands/ai-audio.js.map +1 -0
  112. package/dist/commands/ai-broll.d.ts +15 -0
  113. package/dist/commands/ai-broll.d.ts.map +1 -0
  114. package/dist/commands/ai-broll.js +406 -0
  115. package/dist/commands/ai-broll.js.map +1 -0
  116. package/dist/commands/ai-edit-cli.d.ts +14 -0
  117. package/dist/commands/ai-edit-cli.d.ts.map +1 -0
  118. package/dist/commands/ai-edit-cli.js +579 -0
  119. package/dist/commands/ai-edit-cli.js.map +1 -0
  120. package/dist/commands/ai-edit.d.ts +398 -0
  121. package/dist/commands/ai-edit.d.ts.map +1 -0
  122. package/dist/commands/ai-edit.js +1019 -0
  123. package/dist/commands/ai-edit.js.map +1 -0
  124. package/dist/commands/ai-fill-gaps.d.ts +14 -0
  125. package/dist/commands/ai-fill-gaps.d.ts.map +1 -0
  126. package/dist/commands/ai-fill-gaps.js +451 -0
  127. package/dist/commands/ai-fill-gaps.js.map +1 -0
  128. package/dist/commands/ai-helpers.d.ts +20 -0
  129. package/dist/commands/ai-helpers.d.ts.map +1 -0
  130. package/dist/commands/ai-helpers.js +59 -0
  131. package/dist/commands/ai-helpers.js.map +1 -0
  132. package/dist/commands/ai-highlights.d.ts +127 -0
  133. package/dist/commands/ai-highlights.d.ts.map +1 -0
  134. package/dist/commands/ai-highlights.js +1026 -0
  135. package/dist/commands/ai-highlights.js.map +1 -0
  136. package/dist/commands/ai-image.d.ts +34 -0
  137. package/dist/commands/ai-image.d.ts.map +1 -0
  138. package/dist/commands/ai-image.js +653 -0
  139. package/dist/commands/ai-image.js.map +1 -0
  140. package/dist/commands/ai-motion.d.ts +50 -0
  141. package/dist/commands/ai-motion.d.ts.map +1 -0
  142. package/dist/commands/ai-motion.js +271 -0
  143. package/dist/commands/ai-motion.js.map +1 -0
  144. package/dist/commands/ai-narrate.d.ts +66 -0
  145. package/dist/commands/ai-narrate.d.ts.map +1 -0
  146. package/dist/commands/ai-narrate.js +329 -0
  147. package/dist/commands/ai-narrate.js.map +1 -0
  148. package/dist/commands/ai-review.d.ts +57 -0
  149. package/dist/commands/ai-review.d.ts.map +1 -0
  150. package/dist/commands/ai-review.js +251 -0
  151. package/dist/commands/ai-review.js.map +1 -0
  152. package/dist/commands/ai-script-pipeline-cli.d.ts +9 -0
  153. package/dist/commands/ai-script-pipeline-cli.d.ts.map +1 -0
  154. package/dist/commands/ai-script-pipeline-cli.js +1494 -0
  155. package/dist/commands/ai-script-pipeline-cli.js.map +1 -0
  156. package/dist/commands/ai-script-pipeline.d.ts +259 -0
  157. package/dist/commands/ai-script-pipeline.d.ts.map +1 -0
  158. package/dist/commands/ai-script-pipeline.js +1027 -0
  159. package/dist/commands/ai-script-pipeline.js.map +1 -0
  160. package/dist/commands/ai-suggest-edit.d.ts +14 -0
  161. package/dist/commands/ai-suggest-edit.d.ts.map +1 -0
  162. package/dist/commands/ai-suggest-edit.js +220 -0
  163. package/dist/commands/ai-suggest-edit.js.map +1 -0
  164. package/dist/commands/ai-video-fx.d.ts +14 -0
  165. package/dist/commands/ai-video-fx.d.ts.map +1 -0
  166. package/dist/commands/ai-video-fx.js +395 -0
  167. package/dist/commands/ai-video-fx.js.map +1 -0
  168. package/dist/commands/ai-video.d.ts +15 -0
  169. package/dist/commands/ai-video.d.ts.map +1 -0
  170. package/dist/commands/ai-video.js +785 -0
  171. package/dist/commands/ai-video.js.map +1 -0
  172. package/dist/commands/ai-viral.d.ts +15 -0
  173. package/dist/commands/ai-viral.d.ts.map +1 -0
  174. package/dist/commands/ai-viral.js +519 -0
  175. package/dist/commands/ai-viral.js.map +1 -0
  176. package/dist/commands/ai-visual-fx.d.ts +14 -0
  177. package/dist/commands/ai-visual-fx.d.ts.map +1 -0
  178. package/dist/commands/ai-visual-fx.js +505 -0
  179. package/dist/commands/ai-visual-fx.js.map +1 -0
  180. package/dist/commands/ai.d.ts +38 -0
  181. package/dist/commands/ai.d.ts.map +1 -0
  182. package/dist/commands/ai.js +225 -0
  183. package/dist/commands/ai.js.map +1 -0
  184. package/dist/commands/ai.test.d.ts +2 -0
  185. package/dist/commands/ai.test.d.ts.map +1 -0
  186. package/dist/commands/ai.test.js +554 -0
  187. package/dist/commands/ai.test.js.map +1 -0
  188. package/dist/commands/analyze.d.ts +16 -0
  189. package/dist/commands/analyze.d.ts.map +1 -0
  190. package/dist/commands/analyze.js +247 -0
  191. package/dist/commands/analyze.js.map +1 -0
  192. package/dist/commands/audio.d.ts +18 -0
  193. package/dist/commands/audio.d.ts.map +1 -0
  194. package/dist/commands/audio.js +539 -0
  195. package/dist/commands/audio.js.map +1 -0
  196. package/dist/commands/batch.d.ts +3 -0
  197. package/dist/commands/batch.d.ts.map +1 -0
  198. package/dist/commands/batch.js +366 -0
  199. package/dist/commands/batch.js.map +1 -0
  200. package/dist/commands/batch.test.d.ts +2 -0
  201. package/dist/commands/batch.test.d.ts.map +1 -0
  202. package/dist/commands/batch.test.js +203 -0
  203. package/dist/commands/batch.test.js.map +1 -0
  204. package/dist/commands/detect.d.ts +3 -0
  205. package/dist/commands/detect.d.ts.map +1 -0
  206. package/dist/commands/detect.js +273 -0
  207. package/dist/commands/detect.js.map +1 -0
  208. package/dist/commands/doctor.d.ts +6 -0
  209. package/dist/commands/doctor.d.ts.map +1 -0
  210. package/dist/commands/doctor.js +191 -0
  211. package/dist/commands/doctor.js.map +1 -0
  212. package/dist/commands/edit-cmd.d.ts +26 -0
  213. package/dist/commands/edit-cmd.d.ts.map +1 -0
  214. package/dist/commands/edit-cmd.js +870 -0
  215. package/dist/commands/edit-cmd.js.map +1 -0
  216. package/dist/commands/export.d.ts +39 -0
  217. package/dist/commands/export.d.ts.map +1 -0
  218. package/dist/commands/export.js +730 -0
  219. package/dist/commands/export.js.map +1 -0
  220. package/dist/commands/generate.d.ts +25 -0
  221. package/dist/commands/generate.d.ts.map +1 -0
  222. package/dist/commands/generate.js +1885 -0
  223. package/dist/commands/generate.js.map +1 -0
  224. package/dist/commands/media.d.ts +3 -0
  225. package/dist/commands/media.d.ts.map +1 -0
  226. package/dist/commands/media.js +165 -0
  227. package/dist/commands/media.js.map +1 -0
  228. package/dist/commands/output.d.ts +45 -0
  229. package/dist/commands/output.d.ts.map +1 -0
  230. package/dist/commands/output.js +122 -0
  231. package/dist/commands/output.js.map +1 -0
  232. package/dist/commands/pipeline.d.ts +19 -0
  233. package/dist/commands/pipeline.d.ts.map +1 -0
  234. package/dist/commands/pipeline.js +345 -0
  235. package/dist/commands/pipeline.js.map +1 -0
  236. package/dist/commands/project.d.ts +3 -0
  237. package/dist/commands/project.d.ts.map +1 -0
  238. package/dist/commands/project.js +139 -0
  239. package/dist/commands/project.js.map +1 -0
  240. package/dist/commands/project.test.d.ts +2 -0
  241. package/dist/commands/project.test.d.ts.map +1 -0
  242. package/dist/commands/project.test.js +105 -0
  243. package/dist/commands/project.test.js.map +1 -0
  244. package/dist/commands/sanitize.d.ts +21 -0
  245. package/dist/commands/sanitize.d.ts.map +1 -0
  246. package/dist/commands/sanitize.js +56 -0
  247. package/dist/commands/sanitize.js.map +1 -0
  248. package/dist/commands/schema.d.ts +11 -0
  249. package/dist/commands/schema.d.ts.map +1 -0
  250. package/dist/commands/schema.js +101 -0
  251. package/dist/commands/schema.js.map +1 -0
  252. package/dist/commands/setup.d.ts +6 -0
  253. package/dist/commands/setup.d.ts.map +1 -0
  254. package/dist/commands/setup.js +440 -0
  255. package/dist/commands/setup.js.map +1 -0
  256. package/dist/commands/timeline.d.ts +3 -0
  257. package/dist/commands/timeline.d.ts.map +1 -0
  258. package/dist/commands/timeline.js +469 -0
  259. package/dist/commands/timeline.js.map +1 -0
  260. package/dist/commands/timeline.test.d.ts +2 -0
  261. package/dist/commands/timeline.test.d.ts.map +1 -0
  262. package/dist/commands/timeline.test.js +320 -0
  263. package/dist/commands/timeline.test.js.map +1 -0
  264. package/dist/commands/validate.d.ts +32 -0
  265. package/dist/commands/validate.d.ts.map +1 -0
  266. package/dist/commands/validate.js +63 -0
  267. package/dist/commands/validate.js.map +1 -0
  268. package/dist/config/config.test.d.ts +2 -0
  269. package/dist/config/config.test.d.ts.map +1 -0
  270. package/dist/config/config.test.js +164 -0
  271. package/dist/config/config.test.js.map +1 -0
  272. package/dist/config/index.d.ts +35 -0
  273. package/dist/config/index.d.ts.map +1 -0
  274. package/dist/config/index.js +101 -0
  275. package/dist/config/index.js.map +1 -0
  276. package/dist/config/schema.d.ts +43 -0
  277. package/dist/config/schema.d.ts.map +1 -0
  278. package/dist/config/schema.js +42 -0
  279. package/dist/config/schema.js.map +1 -0
  280. package/dist/engine/index.d.ts +3 -0
  281. package/dist/engine/index.d.ts.map +1 -0
  282. package/dist/engine/index.js +2 -0
  283. package/dist/engine/index.js.map +1 -0
  284. package/dist/engine/project.d.ts +84 -0
  285. package/dist/engine/project.d.ts.map +1 -0
  286. package/dist/engine/project.js +355 -0
  287. package/dist/engine/project.js.map +1 -0
  288. package/dist/engine/project.test.d.ts +2 -0
  289. package/dist/engine/project.test.d.ts.map +1 -0
  290. package/dist/engine/project.test.js +599 -0
  291. package/dist/engine/project.test.js.map +1 -0
  292. package/dist/index.d.ts +7 -0
  293. package/dist/index.d.ts.map +1 -0
  294. package/dist/index.js +131 -0
  295. package/dist/index.js.map +1 -0
  296. package/dist/utils/api-key.d.ts +36 -0
  297. package/dist/utils/api-key.d.ts.map +1 -0
  298. package/dist/utils/api-key.js +211 -0
  299. package/dist/utils/api-key.js.map +1 -0
  300. package/dist/utils/api-key.test.d.ts +2 -0
  301. package/dist/utils/api-key.test.d.ts.map +1 -0
  302. package/dist/utils/api-key.test.js +35 -0
  303. package/dist/utils/api-key.test.js.map +1 -0
  304. package/dist/utils/audio.d.ts +23 -0
  305. package/dist/utils/audio.d.ts.map +1 -0
  306. package/dist/utils/audio.js +79 -0
  307. package/dist/utils/audio.js.map +1 -0
  308. package/dist/utils/exec-safe.d.ts +22 -0
  309. package/dist/utils/exec-safe.d.ts.map +1 -0
  310. package/dist/utils/exec-safe.js +62 -0
  311. package/dist/utils/exec-safe.js.map +1 -0
  312. package/dist/utils/first-run.d.ts +13 -0
  313. package/dist/utils/first-run.d.ts.map +1 -0
  314. package/dist/utils/first-run.js +48 -0
  315. package/dist/utils/first-run.js.map +1 -0
  316. package/dist/utils/provider-resolver.d.ts +15 -0
  317. package/dist/utils/provider-resolver.d.ts.map +1 -0
  318. package/dist/utils/provider-resolver.js +42 -0
  319. package/dist/utils/provider-resolver.js.map +1 -0
  320. package/dist/utils/remotion.d.ts +210 -0
  321. package/dist/utils/remotion.d.ts.map +1 -0
  322. package/dist/utils/remotion.js +731 -0
  323. package/dist/utils/remotion.js.map +1 -0
  324. package/dist/utils/subtitle.d.ts +65 -0
  325. package/dist/utils/subtitle.d.ts.map +1 -0
  326. package/dist/utils/subtitle.js +135 -0
  327. package/dist/utils/subtitle.js.map +1 -0
  328. package/dist/utils/subtitle.test.d.ts +2 -0
  329. package/dist/utils/subtitle.test.d.ts.map +1 -0
  330. package/dist/utils/subtitle.test.js +175 -0
  331. package/dist/utils/subtitle.test.js.map +1 -0
  332. package/dist/utils/tty.d.ts +45 -0
  333. package/dist/utils/tty.d.ts.map +1 -0
  334. package/dist/utils/tty.js +172 -0
  335. package/dist/utils/tty.js.map +1 -0
  336. package/package.json +102 -0
  337. package/src/agent/adapters/claude.ts +143 -0
  338. package/src/agent/adapters/gemini.ts +159 -0
  339. package/src/agent/adapters/index.ts +61 -0
  340. package/src/agent/adapters/ollama.ts +231 -0
  341. package/src/agent/adapters/openai.ts +116 -0
  342. package/src/agent/adapters/xai.ts +119 -0
  343. package/src/agent/index.ts +251 -0
  344. package/src/agent/memory/index.ts +151 -0
  345. package/src/agent/prompts/system.ts +106 -0
  346. package/src/agent/tools/ai-editing.ts +845 -0
  347. package/src/agent/tools/ai-generation.ts +1073 -0
  348. package/src/agent/tools/ai-pipeline.ts +1055 -0
  349. package/src/agent/tools/ai.ts +21 -0
  350. package/src/agent/tools/batch.ts +429 -0
  351. package/src/agent/tools/e2e.test.ts +545 -0
  352. package/src/agent/tools/export.ts +184 -0
  353. package/src/agent/tools/filesystem.ts +237 -0
  354. package/src/agent/tools/index.ts +150 -0
  355. package/src/agent/tools/integration.test.ts +775 -0
  356. package/src/agent/tools/media.ts +697 -0
  357. package/src/agent/tools/project.ts +313 -0
  358. package/src/agent/tools/timeline.ts +951 -0
  359. package/src/agent/types.ts +68 -0
  360. package/src/commands/agent.ts +340 -0
  361. package/src/commands/ai-analyze.ts +429 -0
  362. package/src/commands/ai-animated-caption.ts +390 -0
  363. package/src/commands/ai-audio.ts +941 -0
  364. package/src/commands/ai-broll.ts +490 -0
  365. package/src/commands/ai-edit-cli.ts +658 -0
  366. package/src/commands/ai-edit.ts +1542 -0
  367. package/src/commands/ai-fill-gaps.ts +566 -0
  368. package/src/commands/ai-helpers.ts +65 -0
  369. package/src/commands/ai-highlights.ts +1303 -0
  370. package/src/commands/ai-image.ts +761 -0
  371. package/src/commands/ai-motion.ts +347 -0
  372. package/src/commands/ai-narrate.ts +451 -0
  373. package/src/commands/ai-review.ts +309 -0
  374. package/src/commands/ai-script-pipeline-cli.ts +1710 -0
  375. package/src/commands/ai-script-pipeline.ts +1365 -0
  376. package/src/commands/ai-suggest-edit.ts +264 -0
  377. package/src/commands/ai-video-fx.ts +445 -0
  378. package/src/commands/ai-video.ts +915 -0
  379. package/src/commands/ai-viral.ts +595 -0
  380. package/src/commands/ai-visual-fx.ts +601 -0
  381. package/src/commands/ai.test.ts +627 -0
  382. package/src/commands/ai.ts +307 -0
  383. package/src/commands/analyze.ts +282 -0
  384. package/src/commands/audio.ts +644 -0
  385. package/src/commands/batch.test.ts +279 -0
  386. package/src/commands/batch.ts +440 -0
  387. package/src/commands/detect.ts +329 -0
  388. package/src/commands/doctor.ts +237 -0
  389. package/src/commands/edit-cmd.ts +1014 -0
  390. package/src/commands/export.ts +918 -0
  391. package/src/commands/generate.ts +2146 -0
  392. package/src/commands/media.ts +177 -0
  393. package/src/commands/output.ts +142 -0
  394. package/src/commands/pipeline.ts +398 -0
  395. package/src/commands/project.test.ts +127 -0
  396. package/src/commands/project.ts +149 -0
  397. package/src/commands/sanitize.ts +60 -0
  398. package/src/commands/schema.ts +130 -0
  399. package/src/commands/setup.ts +509 -0
  400. package/src/commands/timeline.test.ts +499 -0
  401. package/src/commands/timeline.ts +529 -0
  402. package/src/commands/validate.ts +77 -0
  403. package/src/config/config.test.ts +197 -0
  404. package/src/config/index.ts +125 -0
  405. package/src/config/schema.ts +82 -0
  406. package/src/engine/index.ts +2 -0
  407. package/src/engine/project.test.ts +702 -0
  408. package/src/engine/project.ts +439 -0
  409. package/src/index.ts +146 -0
  410. package/src/utils/api-key.test.ts +41 -0
  411. package/src/utils/api-key.ts +247 -0
  412. package/src/utils/audio.ts +83 -0
  413. package/src/utils/exec-safe.ts +75 -0
  414. package/src/utils/first-run.ts +52 -0
  415. package/src/utils/provider-resolver.ts +56 -0
  416. package/src/utils/remotion.ts +951 -0
  417. package/src/utils/subtitle.test.ts +227 -0
  418. package/src/utils/subtitle.ts +169 -0
  419. package/src/utils/tty.ts +196 -0
  420. package/tsconfig.json +20 -0
@@ -0,0 +1,490 @@
1
+ /**
2
+ * @module ai-broll
3
+ * @description B-Roll Matcher command. Matches B-roll footage to narration
4
+ * content using Whisper transcription and Claude Vision analysis.
5
+ *
6
+ * ## Commands: vibe ai b-roll
7
+ * ## Dependencies: Whisper, Claude
8
+ *
9
+ * Extracted from ai.ts as part of modularisation.
10
+ * ai.ts calls registerBrollCommand(aiCommand).
11
+ * @see MODELS.md for AI model configuration
12
+ */
13
+
14
+ import { type Command } from "commander";
15
+ import { readFile, writeFile, readdir } from "node:fs/promises";
16
+ import { resolve, basename, extname } from "node:path";
17
+ import { existsSync } from "node:fs";
18
+ import chalk from "chalk";
19
+ import ora from "ora";
20
+ import {
21
+ WhisperProvider,
22
+ ClaudeProvider,
23
+ type BrollClipInfo,
24
+ type BrollMatch,
25
+ type BrollMatchResult,
26
+ } from "@vibeframe/ai-providers";
27
+ import { Project } from "../engine/index.js";
28
+ import { getApiKey } from "../utils/api-key.js";
29
+ import { execSafe, commandExists, ffprobeDuration } from "../utils/exec-safe.js";
30
+ import { formatTime } from "./ai-helpers.js";
31
+
32
+ function truncate(text: string, maxLength: number): string {
33
+ if (text.length <= maxLength) return text;
34
+ return text.slice(0, maxLength - 3) + "...";
35
+ }
36
+
37
+ /**
38
+ * Check if a file path looks like an audio or video file
39
+ */
40
+ function isAudioOrVideoFile(path: string): boolean {
41
+ const mediaExtensions = [
42
+ ".mp3", ".wav", ".m4a", ".aac", ".ogg", ".flac",
43
+ ".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v",
44
+ ];
45
+ const ext = extname(path).toLowerCase();
46
+ return mediaExtensions.includes(ext);
47
+ }
48
+
49
+ /**
50
+ * Discover B-roll video files from paths or directory
51
+ */
52
+ async function discoverBrollFiles(
53
+ paths?: string,
54
+ directory?: string
55
+ ): Promise<string[]> {
56
+ const files: string[] = [];
57
+ const videoExtensions = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
58
+
59
+ if (paths) {
60
+ const pathList = paths.split(",").map((p) => resolve(process.cwd(), p.trim()));
61
+ for (const path of pathList) {
62
+ if (existsSync(path)) {
63
+ files.push(path);
64
+ }
65
+ }
66
+ }
67
+
68
+ if (directory) {
69
+ const dir = resolve(process.cwd(), directory);
70
+ if (existsSync(dir)) {
71
+ const entries = await readdir(dir);
72
+ for (const entry of entries) {
73
+ const ext = extname(entry).toLowerCase();
74
+ if (videoExtensions.includes(ext)) {
75
+ files.push(resolve(dir, entry));
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ return files;
82
+ }
83
+
84
+ /**
85
+ * Extract a key frame from video as base64 JPEG
86
+ */
87
+ async function extractKeyFrame(videoPath: string, timestamp: number): Promise<string> {
88
+ const tempPath = `/tmp/vibe_frame_${Date.now()}.jpg`;
89
+ await execSafe("ffmpeg", ["-ss", String(timestamp), "-i", videoPath, "-frames:v", "1", "-q:v", "2", tempPath, "-y"], { maxBuffer: 10 * 1024 * 1024 });
90
+ const buffer = await readFile(tempPath);
91
+ const { unlink } = await import("node:fs/promises");
92
+ await unlink(tempPath).catch(() => {});
93
+ return buffer.toString("base64");
94
+ }
95
+
96
+ // ── B-Roll Matcher command ──────────────────────────────────────────────────
97
+
98
+ export function registerBrollCommand(ai: Command): void {
99
+ ai
100
+ .command("b-roll")
101
+ .description("Match B-roll footage to narration content (deprecated)")
102
+ .argument("<narration>", "Narration audio file or script text")
103
+ .option("-b, --broll <paths>", "B-roll video files (comma-separated)")
104
+ .option("--broll-dir <dir>", "Directory containing B-roll files")
105
+ .option("-o, --output <path>", "Output project file", "broll-matched.vibe.json")
106
+ .option("-t, --threshold <value>", "Match confidence threshold (0-1)", "0.6")
107
+ .option("-l, --language <lang>", "Language code for transcription (e.g., en, ko)")
108
+ .option("-f, --file", "Treat narration as file path (script file)")
109
+ .option("--analyze-only", "Only analyze, don't create project")
110
+ .action(async (narration: string, options) => {
111
+ try {
112
+ console.warn(chalk.yellow("Warning: 'pipeline b-roll' is deprecated. Use individual commands instead:"));
113
+ console.warn(chalk.dim(" vibe analyze video <video> 'identify scenes needing b-roll' → vibe generate video '<prompt>'"));
114
+ console.warn();
115
+
116
+ // Validate B-roll input
117
+ if (!options.broll && !options.brollDir) {
118
+ console.error(chalk.red("B-roll files required. Use -b or --broll-dir"));
119
+ process.exit(1);
120
+ }
121
+
122
+ // Check API keys
123
+ const openaiApiKey = await getApiKey("OPENAI_API_KEY", "OpenAI");
124
+ if (!openaiApiKey) {
125
+ console.error(chalk.red("OpenAI API key required for Whisper transcription. Set OPENAI_API_KEY in .env or run: vibe setup"));
126
+ console.error(chalk.dim("Set OPENAI_API_KEY environment variable"));
127
+ process.exit(1);
128
+ }
129
+
130
+ const claudeApiKey = await getApiKey("ANTHROPIC_API_KEY", "Anthropic");
131
+ if (!claudeApiKey) {
132
+ console.error(chalk.red("Anthropic API key required for B-roll analysis. Set ANTHROPIC_API_KEY in .env or run: vibe setup"));
133
+ console.error(chalk.dim("Set ANTHROPIC_API_KEY environment variable"));
134
+ process.exit(1);
135
+ }
136
+
137
+ // Check FFmpeg availability
138
+ if (!commandExists("ffmpeg")) {
139
+ console.error(chalk.red("FFmpeg not found. Please install FFmpeg."));
140
+ process.exit(1);
141
+ }
142
+
143
+ console.log();
144
+ console.log(chalk.bold.cyan("🎬 B-Roll Matcher Pipeline"));
145
+ console.log(chalk.dim("─".repeat(60)));
146
+ console.log();
147
+
148
+ // Step 1: Discover B-roll files
149
+ const discoverSpinner = ora("🎥 Discovering B-roll files...").start();
150
+ const brollFiles = await discoverBrollFiles(options.broll, options.brollDir);
151
+
152
+ if (brollFiles.length === 0) {
153
+ discoverSpinner.fail(chalk.red("No B-roll video files found"));
154
+ process.exit(1);
155
+ }
156
+
157
+ discoverSpinner.succeed(chalk.green(`Found ${brollFiles.length} B-roll file(s)`));
158
+
159
+ // Step 2: Parse narration (audio file or script text)
160
+ const narrationSpinner = ora("📝 Processing narration...").start();
161
+
162
+ let narrationSegments: Array<{ startTime: number; endTime: number; text: string }> = [];
163
+ let totalDuration = 0;
164
+ let narrationFile = "";
165
+
166
+ const isScriptFile = options.file;
167
+ const isAudioFile = !isScriptFile && isAudioOrVideoFile(narration);
168
+
169
+ if (isAudioFile) {
170
+ // Transcribe audio with Whisper
171
+ narrationFile = resolve(process.cwd(), narration);
172
+ if (!existsSync(narrationFile)) {
173
+ narrationSpinner.fail(chalk.red(`Narration file not found: ${narrationFile}`));
174
+ process.exit(1);
175
+ }
176
+
177
+ narrationSpinner.text = "📝 Transcribing narration with Whisper...";
178
+
179
+ const whisper = new WhisperProvider();
180
+ await whisper.initialize({ apiKey: openaiApiKey });
181
+
182
+ // Extract audio if it's a video file
183
+ let audioPath = narrationFile;
184
+ let tempAudioPath: string | null = null;
185
+
186
+ const ext = extname(narrationFile).toLowerCase();
187
+ const videoExtensions = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"];
188
+ if (videoExtensions.includes(ext)) {
189
+ narrationSpinner.text = "📝 Extracting audio from video...";
190
+ tempAudioPath = `/tmp/vibe_broll_audio_${Date.now()}.wav`;
191
+ await execSafe("ffmpeg", ["-i", narrationFile, "-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1", tempAudioPath, "-y"], { maxBuffer: 50 * 1024 * 1024 });
192
+ audioPath = tempAudioPath;
193
+ }
194
+
195
+ const audioBuffer = await readFile(audioPath);
196
+ const audioBlob = new Blob([audioBuffer]);
197
+
198
+ narrationSpinner.text = "📝 Transcribing with Whisper...";
199
+ const transcriptResult = await whisper.transcribe(audioBlob, options.language);
200
+
201
+ // Cleanup temp file
202
+ if (tempAudioPath && existsSync(tempAudioPath)) {
203
+ const { unlink } = await import("node:fs/promises");
204
+ await unlink(tempAudioPath).catch(() => {});
205
+ }
206
+
207
+ if (transcriptResult.status === "failed" || !transcriptResult.segments) {
208
+ narrationSpinner.fail(chalk.red(`Transcription failed: ${transcriptResult.error}`));
209
+ process.exit(1);
210
+ }
211
+
212
+ narrationSegments = transcriptResult.segments.map((seg) => ({
213
+ startTime: seg.startTime,
214
+ endTime: seg.endTime,
215
+ text: seg.text,
216
+ }));
217
+
218
+ totalDuration = transcriptResult.segments.length > 0
219
+ ? transcriptResult.segments[transcriptResult.segments.length - 1].endTime
220
+ : 0;
221
+ } else {
222
+ // Use script text (direct or from file)
223
+ let scriptContent = narration;
224
+ if (isScriptFile) {
225
+ const scriptPath = resolve(process.cwd(), narration);
226
+ if (!existsSync(scriptPath)) {
227
+ narrationSpinner.fail(chalk.red(`Script file not found: ${scriptPath}`));
228
+ process.exit(1);
229
+ }
230
+ scriptContent = await readFile(scriptPath, "utf-8");
231
+ narrationFile = scriptPath;
232
+ } else {
233
+ narrationFile = "text-input";
234
+ }
235
+
236
+ // Split script into segments (by paragraph or sentence)
237
+ const paragraphs = scriptContent
238
+ .split(/\n\n+/)
239
+ .map((p) => p.trim())
240
+ .filter((p) => p.length > 0);
241
+
242
+ // Estimate timing (rough: ~150 words per minute)
243
+ let currentTime = 0;
244
+ narrationSegments = paragraphs.map((text) => {
245
+ const wordCount = text.split(/\s+/).length;
246
+ const duration = Math.max((wordCount / 150) * 60, 3); // Min 3 seconds per segment
247
+ const segment = {
248
+ startTime: currentTime,
249
+ endTime: currentTime + duration,
250
+ text,
251
+ };
252
+ currentTime += duration;
253
+ return segment;
254
+ });
255
+
256
+ totalDuration = currentTime;
257
+ }
258
+
259
+ narrationSpinner.succeed(chalk.green(`Processed ${narrationSegments.length} narration segments (${formatTime(totalDuration)} total)`));
260
+
261
+ // Step 3: Analyze B-roll clips with Claude Vision
262
+ const brollSpinner = ora("🎥 Analyzing B-roll content with Claude Vision...").start();
263
+
264
+ const claude = new ClaudeProvider();
265
+ await claude.initialize({ apiKey: claudeApiKey });
266
+
267
+ const brollClips: BrollClipInfo[] = [];
268
+
269
+ for (let i = 0; i < brollFiles.length; i++) {
270
+ const filePath = brollFiles[i];
271
+ const fileName = basename(filePath);
272
+ brollSpinner.text = `🎥 Analyzing B-roll ${i + 1}/${brollFiles.length}: ${fileName}`;
273
+
274
+ try {
275
+ // Get video duration
276
+ const duration = await ffprobeDuration(filePath);
277
+
278
+ // Extract a key frame (middle of video)
279
+ const frameTime = Math.min(duration / 2, 5);
280
+ const frameBase64 = await extractKeyFrame(filePath, frameTime);
281
+
282
+ // Analyze with Claude Vision
283
+ const analysis = await claude.analyzeBrollContent(frameBase64, fileName, "image/jpeg");
284
+
285
+ brollClips.push({
286
+ id: `broll-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
287
+ filePath,
288
+ duration,
289
+ description: analysis.description,
290
+ tags: analysis.tags,
291
+ });
292
+ } catch (error) {
293
+ console.log(chalk.yellow(`\n ⚠ Could not analyze ${fileName}: ${error}`));
294
+ }
295
+ }
296
+
297
+ brollSpinner.succeed(chalk.green(`Analyzed ${brollClips.length} B-roll clips`));
298
+
299
+ // Display analyzed B-roll
300
+ for (const clip of brollClips) {
301
+ console.log(chalk.dim(` → ${basename(clip.filePath)}: "${clip.description}"`));
302
+ console.log(chalk.dim(` [${clip.tags.join(", ")}]`));
303
+ }
304
+ console.log();
305
+
306
+ // Step 4: Analyze narration for visual requirements
307
+ const visualSpinner = ora("🔍 Analyzing narration for visual needs...").start();
308
+
309
+ const analyzedNarration = await claude.analyzeNarrationForVisuals(narrationSegments);
310
+
311
+ visualSpinner.succeed(chalk.green("Narration analysis complete"));
312
+
313
+ // Step 5: Match B-roll to narration
314
+ const matchSpinner = ora("🔗 Matching B-roll to narration...").start();
315
+
316
+ const matches = await claude.matchBrollToNarration(analyzedNarration, brollClips);
317
+
318
+ const threshold = parseFloat(options.threshold);
319
+ const filteredMatches = matches.filter((m) => m.confidence >= threshold);
320
+
321
+ // Remove duplicate assignments (keep highest confidence for each segment)
322
+ const uniqueMatches: BrollMatch[] = [];
323
+ const matchedSegments = new Set<number>();
324
+
325
+ // Sort by confidence descending
326
+ filteredMatches.sort((a, b) => b.confidence - a.confidence);
327
+
328
+ for (const match of filteredMatches) {
329
+ if (!matchedSegments.has(match.narrationSegmentIndex)) {
330
+ matchedSegments.add(match.narrationSegmentIndex);
331
+ uniqueMatches.push(match);
332
+ }
333
+ }
334
+
335
+ // Sort back by segment index
336
+ uniqueMatches.sort((a, b) => a.narrationSegmentIndex - b.narrationSegmentIndex);
337
+
338
+ const coverage = (uniqueMatches.length / narrationSegments.length) * 100;
339
+ matchSpinner.succeed(chalk.green(`Found ${uniqueMatches.length} matches (${coverage.toFixed(0)}% coverage)`));
340
+
341
+ // Find unmatched segments
342
+ const unmatchedSegments: number[] = [];
343
+ for (let i = 0; i < narrationSegments.length; i++) {
344
+ if (!matchedSegments.has(i)) {
345
+ unmatchedSegments.push(i);
346
+ }
347
+ }
348
+
349
+ // Display match summary
350
+ console.log();
351
+ console.log(chalk.bold.cyan("📊 Match Summary"));
352
+ console.log(chalk.dim("─".repeat(60)));
353
+
354
+ for (const match of uniqueMatches) {
355
+ const segment = analyzedNarration[match.narrationSegmentIndex];
356
+ const clip = brollClips.find((c) => c.id === match.brollClipId);
357
+ const startFormatted = formatTime(segment.startTime);
358
+ const endFormatted = formatTime(segment.endTime);
359
+ const confidencePercent = (match.confidence * 100).toFixed(0);
360
+
361
+ console.log();
362
+ console.log(` ${chalk.yellow(`Segment ${match.narrationSegmentIndex + 1}`)} [${startFormatted} - ${endFormatted}]`);
363
+ console.log(` ${chalk.dim(truncate(segment.text, 60))}`);
364
+ console.log(` ${chalk.green("→")} ${basename(clip?.filePath || "unknown")} ${chalk.dim(`(${confidencePercent}%)`)}`);
365
+ console.log(` ${chalk.dim(match.reason)}`);
366
+ }
367
+
368
+ if (unmatchedSegments.length > 0) {
369
+ console.log();
370
+ console.log(chalk.yellow(` ⚠ ${unmatchedSegments.length} unmatched segment(s): [${unmatchedSegments.map((i) => i + 1).join(", ")}]`));
371
+ }
372
+
373
+ console.log();
374
+ console.log(chalk.dim("─".repeat(60)));
375
+ console.log(`Total: ${chalk.bold(uniqueMatches.length)}/${narrationSegments.length} segments matched, ${chalk.bold(coverage.toFixed(0))}% coverage`);
376
+ console.log();
377
+
378
+ // Prepare result object
379
+ const result: BrollMatchResult = {
380
+ narrationFile,
381
+ totalDuration,
382
+ brollClips,
383
+ narrationSegments: analyzedNarration,
384
+ matches: uniqueMatches,
385
+ unmatchedSegments,
386
+ };
387
+
388
+ // Step 6: Create project (unless analyze-only)
389
+ if (!options.analyzeOnly) {
390
+ const projectSpinner = ora("📦 Creating project...").start();
391
+
392
+ const project = new Project("B-Roll Matched Project");
393
+
394
+ // Add B-roll sources
395
+ const sourceMap = new Map<string, string>();
396
+ for (const clip of brollClips) {
397
+ const source = project.addSource({
398
+ name: basename(clip.filePath),
399
+ url: clip.filePath,
400
+ type: "video",
401
+ duration: clip.duration,
402
+ });
403
+ sourceMap.set(clip.id, source.id);
404
+ }
405
+
406
+ // Add narration audio source if it's an audio file
407
+ let narrationSourceId: string | null = null;
408
+ if (isAudioFile && narrationFile && existsSync(narrationFile)) {
409
+ const narrationSource = project.addSource({
410
+ name: basename(narrationFile),
411
+ url: narrationFile,
412
+ type: "audio",
413
+ duration: totalDuration,
414
+ });
415
+ narrationSourceId = narrationSource.id;
416
+ }
417
+
418
+ // Get tracks
419
+ const videoTrack = project.getTracks().find((t) => t.type === "video");
420
+ const audioTrack = project.getTracks().find((t) => t.type === "audio");
421
+ if (!videoTrack) {
422
+ projectSpinner.fail(chalk.red("Failed to create project"));
423
+ process.exit(1);
424
+ }
425
+
426
+ // Add narration audio clip to audio track
427
+ if (narrationSourceId && audioTrack) {
428
+ project.addClip({
429
+ sourceId: narrationSourceId,
430
+ trackId: audioTrack.id,
431
+ startTime: 0,
432
+ duration: totalDuration,
433
+ sourceStartOffset: 0,
434
+ sourceEndOffset: totalDuration,
435
+ });
436
+ }
437
+
438
+ // Add clips for each match
439
+ for (const match of uniqueMatches) {
440
+ const segment = analyzedNarration[match.narrationSegmentIndex];
441
+ const sourceId = sourceMap.get(match.brollClipId);
442
+ const clip = brollClips.find((c) => c.id === match.brollClipId);
443
+
444
+ if (!sourceId || !clip) continue;
445
+
446
+ const clipDuration = Math.min(
447
+ match.suggestedDuration || segment.endTime - segment.startTime,
448
+ clip.duration - match.suggestedStartOffset
449
+ );
450
+
451
+ project.addClip({
452
+ sourceId,
453
+ trackId: videoTrack.id,
454
+ startTime: segment.startTime,
455
+ duration: clipDuration,
456
+ sourceStartOffset: match.suggestedStartOffset,
457
+ sourceEndOffset: match.suggestedStartOffset + clipDuration,
458
+ });
459
+ }
460
+
461
+ const outputPath = resolve(process.cwd(), options.output);
462
+ await writeFile(outputPath, JSON.stringify(project.toJSON(), null, 2), "utf-8");
463
+
464
+ projectSpinner.succeed(chalk.green(`Created project: ${outputPath}`));
465
+
466
+ // Save JSON result alongside project
467
+ const jsonOutputPath = outputPath.replace(/\.vibe\.json$/, "-analysis.json");
468
+ await writeFile(jsonOutputPath, JSON.stringify(result, null, 2), "utf-8");
469
+ console.log(chalk.dim(` → Analysis saved: ${jsonOutputPath}`));
470
+ }
471
+
472
+ console.log();
473
+ console.log(chalk.bold.green("✅ B-Roll matching complete!"));
474
+ console.log();
475
+ console.log(chalk.dim("Next steps:"));
476
+ if (!options.analyzeOnly) {
477
+ console.log(chalk.dim(` vibe project info ${options.output}`));
478
+ console.log(chalk.dim(` vibe export ${options.output} -o final.mp4`));
479
+ }
480
+ if (unmatchedSegments.length > 0) {
481
+ console.log(chalk.dim(" Consider adding more B-roll clips for unmatched segments"));
482
+ }
483
+ console.log();
484
+ } catch (error) {
485
+ console.error(chalk.red("B-Roll matching failed"));
486
+ console.error(error);
487
+ process.exit(1);
488
+ }
489
+ });
490
+ }