@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,763 @@
1
+ /**
2
+ * @module ai-editing
3
+ * @description Agent tools for post-production editing (text overlay, review,
4
+ * silence cut, jump cut, captions, noise reduction, fade, thumbnail,
5
+ * SRT translation). FFmpeg-based and AI-assisted editing tools for agent use.
6
+ * Most tools work without API keys (FFmpeg-only), some use Gemini or OpenAI.
7
+ *
8
+ * ## Tools: edit_text_overlay, analyze_review, edit_silence_cut, edit_jump_cut, edit_caption,
9
+ * edit_noise_reduce, edit_fade, generate_thumbnail, edit_translate_srt
10
+ * ## Dependencies: FFmpeg, Gemini (optional), OpenAI/Whisper (optional)
11
+ * @see MODELS.md for the Single Source of Truth (SSOT) on supported providers/models
12
+ */
13
+ import { resolve } from "node:path";
14
+ import { executeTextOverlay, executeSilenceCut, executeJumpCut, executeCaption, executeNoiseReduce, executeFade, executeTranslateSrt, } from "../../commands/ai-edit.js";
15
+ import { executeReview } from "../../commands/ai-review.js";
16
+ import { executeThumbnailBestFrame } from "../../commands/ai-image.js";
17
+ import { sanitizeAIResult } from "../../commands/sanitize.js";
18
+ // ============================================================================
19
+ // Tool Definitions
20
+ // ============================================================================
21
+ const textOverlayDef = {
22
+ name: "edit_text_overlay",
23
+ description: "Apply text overlays to a video using FFmpeg drawtext. Supports 4 style presets: lower-third, center-bold, subtitle, minimal. Auto-detects font and scales based on video resolution.",
24
+ parameters: {
25
+ type: "object",
26
+ properties: {
27
+ videoPath: {
28
+ type: "string",
29
+ description: "Path to input video file",
30
+ },
31
+ texts: {
32
+ type: "array",
33
+ items: { type: "string", description: "Text line to overlay" },
34
+ description: "Text lines to overlay (multiple lines stack vertically)",
35
+ },
36
+ outputPath: {
37
+ type: "string",
38
+ description: "Output video file path",
39
+ },
40
+ style: {
41
+ type: "string",
42
+ description: "Overlay style preset",
43
+ enum: ["lower-third", "center-bold", "subtitle", "minimal"],
44
+ },
45
+ fontSize: {
46
+ type: "number",
47
+ description: "Font size in pixels (auto-calculated if omitted)",
48
+ },
49
+ fontColor: {
50
+ type: "string",
51
+ description: "Font color (default: white)",
52
+ },
53
+ fadeDuration: {
54
+ type: "number",
55
+ description: "Fade in/out duration in seconds (default: 0.3)",
56
+ },
57
+ startTime: {
58
+ type: "number",
59
+ description: "Start time for overlay in seconds (default: 0)",
60
+ },
61
+ endTime: {
62
+ type: "number",
63
+ description: "End time for overlay in seconds (default: video duration)",
64
+ },
65
+ },
66
+ required: ["videoPath", "texts", "outputPath"],
67
+ },
68
+ };
69
+ const reviewDef = {
70
+ name: "analyze_review",
71
+ description: "Review video quality using Gemini AI. Analyzes pacing, color, text readability, audio-visual sync, and composition. Can auto-apply fixable corrections (color grading). Returns structured feedback with scores and recommendations.",
72
+ parameters: {
73
+ type: "object",
74
+ properties: {
75
+ videoPath: {
76
+ type: "string",
77
+ description: "Path to video file to review",
78
+ },
79
+ storyboardPath: {
80
+ type: "string",
81
+ description: "Optional path to storyboard JSON for context",
82
+ },
83
+ autoApply: {
84
+ type: "boolean",
85
+ description: "Automatically apply fixable corrections (default: false)",
86
+ },
87
+ verify: {
88
+ type: "boolean",
89
+ description: "Run verification pass after applying fixes (default: false)",
90
+ },
91
+ model: {
92
+ type: "string",
93
+ description: "Gemini model: flash (default), flash-2.5, pro",
94
+ enum: ["flash", "flash-2.5", "pro"],
95
+ },
96
+ outputPath: {
97
+ type: "string",
98
+ description: "Output path for corrected video (when autoApply is true)",
99
+ },
100
+ },
101
+ required: ["videoPath"],
102
+ },
103
+ };
104
+ const silenceCutDef = {
105
+ name: "edit_silence_cut",
106
+ description: "Remove silent segments from a video. Default uses FFmpeg silencedetect (free, no API key). Use useGemini=true for smart context-aware detection via Gemini Video Understanding — distinguishes dead air from intentional pauses using visual+audio analysis.",
107
+ parameters: {
108
+ type: "object",
109
+ properties: {
110
+ videoPath: {
111
+ type: "string",
112
+ description: "Path to input video file",
113
+ },
114
+ outputPath: {
115
+ type: "string",
116
+ description: "Output file path (default: <name>-cut.<ext>)",
117
+ },
118
+ noiseThreshold: {
119
+ type: "number",
120
+ description: "Silence threshold in dB (default: -30). Lower = more sensitive. FFmpeg mode only.",
121
+ },
122
+ minDuration: {
123
+ type: "number",
124
+ description: "Minimum silence duration in seconds to cut (default: 0.5)",
125
+ },
126
+ padding: {
127
+ type: "number",
128
+ description: "Padding around non-silent segments in seconds (default: 0.1)",
129
+ },
130
+ analyzeOnly: {
131
+ type: "boolean",
132
+ description: "Only detect silence without cutting (default: false)",
133
+ },
134
+ useGemini: {
135
+ type: "boolean",
136
+ description: "Use Gemini Video Understanding for context-aware silence detection (default: false). Requires GOOGLE_API_KEY.",
137
+ },
138
+ model: {
139
+ type: "string",
140
+ description: "Gemini model to use (default: flash). Options: flash, flash-2.5, pro",
141
+ },
142
+ lowRes: {
143
+ type: "boolean",
144
+ description: "Low resolution mode for longer videos (Gemini only)",
145
+ },
146
+ },
147
+ required: ["videoPath"],
148
+ },
149
+ };
150
+ const jumpCutDef = {
151
+ name: "edit_jump_cut",
152
+ description: "Remove filler words (um, uh, like, etc.) from video using Whisper word-level timestamps + FFmpeg concat. Requires OpenAI API key. Detects filler words, cuts them out, and stitches remaining segments with stream copy (fast, no re-encode).",
153
+ parameters: {
154
+ type: "object",
155
+ properties: {
156
+ videoPath: {
157
+ type: "string",
158
+ description: "Path to input video file",
159
+ },
160
+ outputPath: {
161
+ type: "string",
162
+ description: "Output file path (default: <name>-jumpcut.<ext>)",
163
+ },
164
+ fillers: {
165
+ type: "array",
166
+ items: { type: "string", description: "A filler word to detect" },
167
+ description: "Custom filler words to detect (default: um, uh, like, you know, etc.)",
168
+ },
169
+ padding: {
170
+ type: "number",
171
+ description: "Padding around cuts in seconds (default: 0.05)",
172
+ },
173
+ language: {
174
+ type: "string",
175
+ description: "Language code for transcription (e.g., en, ko)",
176
+ },
177
+ analyzeOnly: {
178
+ type: "boolean",
179
+ description: "Only detect fillers without cutting (default: false)",
180
+ },
181
+ },
182
+ required: ["videoPath"],
183
+ },
184
+ };
185
+ const captionDef = {
186
+ name: "edit_caption",
187
+ description: "Transcribe video with Whisper and burn styled captions using FFmpeg. Requires OpenAI API key. 4 style presets: minimal, bold (default), outline, karaoke. Auto-sizes font based on video resolution.",
188
+ parameters: {
189
+ type: "object",
190
+ properties: {
191
+ videoPath: {
192
+ type: "string",
193
+ description: "Path to input video file",
194
+ },
195
+ outputPath: {
196
+ type: "string",
197
+ description: "Output file path (default: <name>-captioned.<ext>)",
198
+ },
199
+ style: {
200
+ type: "string",
201
+ description: "Caption style preset",
202
+ enum: ["minimal", "bold", "outline", "karaoke"],
203
+ },
204
+ fontSize: {
205
+ type: "number",
206
+ description: "Font size in pixels (auto-calculated based on resolution if omitted)",
207
+ },
208
+ fontColor: {
209
+ type: "string",
210
+ description: "Font color (default: white)",
211
+ },
212
+ language: {
213
+ type: "string",
214
+ description: "Language code for transcription (e.g., en, ko)",
215
+ },
216
+ position: {
217
+ type: "string",
218
+ description: "Caption position",
219
+ enum: ["top", "center", "bottom"],
220
+ },
221
+ },
222
+ required: ["videoPath"],
223
+ },
224
+ };
225
+ const noiseReduceDef = {
226
+ name: "edit_noise_reduce",
227
+ description: "Remove background noise from audio/video using FFmpeg afftdn filter. No API key needed. Three strength presets: low, medium (default), high. High adds bandpass filtering.",
228
+ parameters: {
229
+ type: "object",
230
+ properties: {
231
+ inputPath: {
232
+ type: "string",
233
+ description: "Path to input audio or video file",
234
+ },
235
+ outputPath: {
236
+ type: "string",
237
+ description: "Output file path (default: <name>-denoised.<ext>)",
238
+ },
239
+ strength: {
240
+ type: "string",
241
+ description: "Noise reduction strength",
242
+ enum: ["low", "medium", "high"],
243
+ },
244
+ noiseFloor: {
245
+ type: "number",
246
+ description: "Custom noise floor in dB (overrides strength preset)",
247
+ },
248
+ },
249
+ required: ["inputPath"],
250
+ },
251
+ };
252
+ const fadeDef = {
253
+ name: "edit_fade",
254
+ description: "Apply fade in/out effects to video using FFmpeg. No API key needed. Supports video-only, audio-only, or both. Configurable fade durations.",
255
+ parameters: {
256
+ type: "object",
257
+ properties: {
258
+ videoPath: {
259
+ type: "string",
260
+ description: "Path to input video file",
261
+ },
262
+ outputPath: {
263
+ type: "string",
264
+ description: "Output file path (default: <name>-faded.<ext>)",
265
+ },
266
+ fadeIn: {
267
+ type: "number",
268
+ description: "Fade-in duration in seconds (default: 1)",
269
+ },
270
+ fadeOut: {
271
+ type: "number",
272
+ description: "Fade-out duration in seconds (default: 1)",
273
+ },
274
+ audioOnly: {
275
+ type: "boolean",
276
+ description: "Apply fade to audio only (default: false)",
277
+ },
278
+ videoOnly: {
279
+ type: "boolean",
280
+ description: "Apply fade to video only (default: false)",
281
+ },
282
+ },
283
+ required: ["videoPath"],
284
+ },
285
+ };
286
+ const thumbnailBestFrameDef = {
287
+ name: "generate_thumbnail",
288
+ description: "Extract the best thumbnail frame from a video using Gemini AI analysis + FFmpeg frame extraction. Requires GOOGLE_API_KEY. Finds visually striking, well-composed frames.",
289
+ parameters: {
290
+ type: "object",
291
+ properties: {
292
+ videoPath: {
293
+ type: "string",
294
+ description: "Path to input video file",
295
+ },
296
+ outputPath: {
297
+ type: "string",
298
+ description: "Output image path (default: <name>-thumbnail.png)",
299
+ },
300
+ prompt: {
301
+ type: "string",
302
+ description: "Custom prompt for frame selection analysis",
303
+ },
304
+ model: {
305
+ type: "string",
306
+ description: "Gemini model to use",
307
+ enum: ["flash", "flash-2.5", "pro"],
308
+ },
309
+ },
310
+ required: ["videoPath"],
311
+ },
312
+ };
313
+ const translateSrtDef = {
314
+ name: "edit_translate_srt",
315
+ description: "Translate SRT subtitle file to another language using Claude or OpenAI. Preserves timestamps. Batches segments for efficiency.",
316
+ parameters: {
317
+ type: "object",
318
+ properties: {
319
+ srtPath: {
320
+ type: "string",
321
+ description: "Path to input SRT file",
322
+ },
323
+ outputPath: {
324
+ type: "string",
325
+ description: "Output file path (default: <name>-<target>.srt)",
326
+ },
327
+ targetLanguage: {
328
+ type: "string",
329
+ description: "Target language (e.g., ko, es, fr, ja, zh)",
330
+ },
331
+ provider: {
332
+ type: "string",
333
+ description: "Translation provider",
334
+ enum: ["claude", "openai"],
335
+ },
336
+ sourceLanguage: {
337
+ type: "string",
338
+ description: "Source language (auto-detected if omitted)",
339
+ },
340
+ },
341
+ required: ["srtPath", "targetLanguage"],
342
+ },
343
+ };
344
+ // ============================================================================
345
+ // Tool Handlers
346
+ // ============================================================================
347
+ const textOverlayHandler = async (args) => {
348
+ const { videoPath, texts, outputPath, style, fontSize, fontColor, fadeDuration, startTime, endTime } = args;
349
+ if (!videoPath || !texts || texts.length === 0 || !outputPath) {
350
+ return {
351
+ toolCallId: "",
352
+ success: false,
353
+ output: "",
354
+ error: "videoPath, texts (non-empty array), and outputPath are required",
355
+ };
356
+ }
357
+ const result = await executeTextOverlay({
358
+ videoPath,
359
+ texts,
360
+ outputPath,
361
+ style,
362
+ fontSize,
363
+ fontColor,
364
+ fadeDuration,
365
+ startTime,
366
+ endTime,
367
+ });
368
+ if (!result.success) {
369
+ return {
370
+ toolCallId: "",
371
+ success: false,
372
+ output: "",
373
+ error: result.error || "Text overlay failed",
374
+ };
375
+ }
376
+ return {
377
+ toolCallId: "",
378
+ success: true,
379
+ output: `Text overlay applied: ${result.outputPath}`,
380
+ };
381
+ };
382
+ const reviewHandler = async (args) => {
383
+ const { videoPath, storyboardPath, autoApply, verify, model, outputPath } = args;
384
+ if (!videoPath) {
385
+ return {
386
+ toolCallId: "",
387
+ success: false,
388
+ output: "",
389
+ error: "videoPath is required",
390
+ };
391
+ }
392
+ const result = await executeReview({
393
+ videoPath,
394
+ storyboardPath,
395
+ autoApply,
396
+ verify,
397
+ model,
398
+ outputPath,
399
+ });
400
+ if (!result.success) {
401
+ return {
402
+ toolCallId: "",
403
+ success: false,
404
+ output: "",
405
+ error: result.error || "Video review failed",
406
+ };
407
+ }
408
+ const fb = sanitizeAIResult(result.feedback);
409
+ let output = `Video Review: ${fb.overallScore}/10\n`;
410
+ output += `Pacing: ${fb.categories.pacing.score}/10, Color: ${fb.categories.color.score}/10, `;
411
+ output += `Text: ${fb.categories.textReadability.score}/10, AV Sync: ${fb.categories.audioVisualSync.score}/10, `;
412
+ output += `Composition: ${fb.categories.composition.score}/10\n`;
413
+ if (result.appliedFixes && result.appliedFixes.length > 0) {
414
+ output += `Applied fixes: ${sanitizeAIResult(result.appliedFixes).join("; ")}\n`;
415
+ }
416
+ if (result.verificationScore !== undefined) {
417
+ output += `Verification score: ${result.verificationScore}/10\n`;
418
+ }
419
+ if (fb.recommendations.length > 0) {
420
+ output += `Recommendations: ${fb.recommendations.join("; ")}`;
421
+ }
422
+ return {
423
+ toolCallId: "",
424
+ success: true,
425
+ output,
426
+ };
427
+ };
428
+ const silenceCutHandler = async (args, context) => {
429
+ const videoPath = resolve(context.workingDirectory, args.videoPath);
430
+ const ext = videoPath.split(".").pop() || "mp4";
431
+ const name = videoPath.replace(/\.[^.]+$/, "");
432
+ const outputPath = args.outputPath
433
+ ? resolve(context.workingDirectory, args.outputPath)
434
+ : `${name}-cut.${ext}`;
435
+ try {
436
+ const result = await executeSilenceCut({
437
+ videoPath,
438
+ outputPath,
439
+ noiseThreshold: args.noiseThreshold,
440
+ minDuration: args.minDuration,
441
+ padding: args.padding,
442
+ analyzeOnly: args.analyzeOnly,
443
+ useGemini: args.useGemini,
444
+ model: args.model,
445
+ lowRes: args.lowRes,
446
+ });
447
+ if (!result.success) {
448
+ return {
449
+ toolCallId: "",
450
+ success: false,
451
+ output: "",
452
+ error: result.error || "Silence cut failed",
453
+ };
454
+ }
455
+ const lines = [];
456
+ lines.push(`Detection method: ${result.method === "gemini" ? "Gemini Video Understanding" : "FFmpeg silencedetect"}`);
457
+ lines.push(`Total duration: ${result.totalDuration.toFixed(1)}s`);
458
+ lines.push(`Silent periods: ${result.silentPeriods.length}`);
459
+ lines.push(`Silent duration: ${result.silentDuration.toFixed(1)}s`);
460
+ lines.push(`Non-silent duration: ${(result.totalDuration - result.silentDuration).toFixed(1)}s`);
461
+ if (result.outputPath) {
462
+ lines.push(`Output: ${result.outputPath}`);
463
+ }
464
+ return {
465
+ toolCallId: "",
466
+ success: true,
467
+ output: lines.join("\n"),
468
+ };
469
+ }
470
+ catch (error) {
471
+ return {
472
+ toolCallId: "",
473
+ success: false,
474
+ output: "",
475
+ error: `Silence cut failed: ${error instanceof Error ? error.message : String(error)}`,
476
+ };
477
+ }
478
+ };
479
+ const jumpCutHandler = async (args, context) => {
480
+ const videoPath = resolve(context.workingDirectory, args.videoPath);
481
+ const ext = videoPath.split(".").pop() || "mp4";
482
+ const name = videoPath.replace(/\.[^.]+$/, "");
483
+ const outputPath = args.outputPath
484
+ ? resolve(context.workingDirectory, args.outputPath)
485
+ : `${name}-jumpcut.${ext}`;
486
+ try {
487
+ const result = await executeJumpCut({
488
+ videoPath,
489
+ outputPath,
490
+ fillers: args.fillers,
491
+ padding: args.padding,
492
+ language: args.language,
493
+ analyzeOnly: args.analyzeOnly,
494
+ });
495
+ if (!result.success) {
496
+ return {
497
+ toolCallId: "",
498
+ success: false,
499
+ output: "",
500
+ error: result.error || "Jump cut failed",
501
+ };
502
+ }
503
+ const lines = [];
504
+ lines.push(`Total duration: ${result.totalDuration.toFixed(1)}s`);
505
+ lines.push(`Filler words found: ${result.fillerCount}`);
506
+ lines.push(`Filler duration: ${result.fillerDuration.toFixed(1)}s`);
507
+ lines.push(`Clean duration: ${(result.totalDuration - result.fillerDuration).toFixed(1)}s`);
508
+ if (result.fillers && result.fillers.length > 0) {
509
+ lines.push("");
510
+ lines.push("Detected fillers:");
511
+ for (const filler of result.fillers) {
512
+ lines.push(` "${filler.word}" at ${filler.start.toFixed(2)}s - ${filler.end.toFixed(2)}s`);
513
+ }
514
+ }
515
+ if (result.outputPath) {
516
+ lines.push(`Output: ${result.outputPath}`);
517
+ }
518
+ return {
519
+ toolCallId: "",
520
+ success: true,
521
+ output: lines.join("\n"),
522
+ };
523
+ }
524
+ catch (error) {
525
+ return {
526
+ toolCallId: "",
527
+ success: false,
528
+ output: "",
529
+ error: `Jump cut failed: ${error instanceof Error ? error.message : String(error)}`,
530
+ };
531
+ }
532
+ };
533
+ const captionHandler = async (args, context) => {
534
+ const videoPath = resolve(context.workingDirectory, args.videoPath);
535
+ const ext = videoPath.split(".").pop() || "mp4";
536
+ const name = videoPath.replace(/\.[^.]+$/, "");
537
+ const outputPath = args.outputPath
538
+ ? resolve(context.workingDirectory, args.outputPath)
539
+ : `${name}-captioned.${ext}`;
540
+ try {
541
+ const result = await executeCaption({
542
+ videoPath,
543
+ outputPath,
544
+ style: args.style,
545
+ fontSize: args.fontSize,
546
+ fontColor: args.fontColor,
547
+ language: args.language,
548
+ position: args.position,
549
+ });
550
+ if (!result.success) {
551
+ return {
552
+ toolCallId: "",
553
+ success: false,
554
+ output: "",
555
+ error: result.error || "Caption failed",
556
+ };
557
+ }
558
+ const lines = [];
559
+ lines.push(`Captions applied: ${result.outputPath}`);
560
+ lines.push(`Segments transcribed: ${result.segmentCount}`);
561
+ if (result.srtPath) {
562
+ lines.push(`SRT file: ${result.srtPath}`);
563
+ }
564
+ return {
565
+ toolCallId: "",
566
+ success: true,
567
+ output: lines.join("\n"),
568
+ };
569
+ }
570
+ catch (error) {
571
+ return {
572
+ toolCallId: "",
573
+ success: false,
574
+ output: "",
575
+ error: `Caption failed: ${error instanceof Error ? error.message : String(error)}`,
576
+ };
577
+ }
578
+ };
579
+ const noiseReduceHandler = async (args, context) => {
580
+ const inputPath = resolve(context.workingDirectory, args.inputPath);
581
+ const ext = inputPath.split(".").pop() || "mp4";
582
+ const name = inputPath.replace(/\.[^.]+$/, "");
583
+ const outputPath = args.outputPath
584
+ ? resolve(context.workingDirectory, args.outputPath)
585
+ : `${name}-denoised.${ext}`;
586
+ try {
587
+ const result = await executeNoiseReduce({
588
+ inputPath,
589
+ outputPath,
590
+ strength: args.strength,
591
+ noiseFloor: args.noiseFloor,
592
+ });
593
+ if (!result.success) {
594
+ return {
595
+ toolCallId: "",
596
+ success: false,
597
+ output: "",
598
+ error: result.error || "Noise reduction failed",
599
+ };
600
+ }
601
+ const lines = [];
602
+ lines.push(`Noise reduction applied: ${result.outputPath}`);
603
+ lines.push(`Input duration: ${result.inputDuration.toFixed(1)}s`);
604
+ return {
605
+ toolCallId: "",
606
+ success: true,
607
+ output: lines.join("\n"),
608
+ };
609
+ }
610
+ catch (error) {
611
+ return {
612
+ toolCallId: "",
613
+ success: false,
614
+ output: "",
615
+ error: `Noise reduction failed: ${error instanceof Error ? error.message : String(error)}`,
616
+ };
617
+ }
618
+ };
619
+ const fadeHandler = async (args, context) => {
620
+ const videoPath = resolve(context.workingDirectory, args.videoPath);
621
+ const ext = videoPath.split(".").pop() || "mp4";
622
+ const name = videoPath.replace(/\.[^.]+$/, "");
623
+ const outputPath = args.outputPath
624
+ ? resolve(context.workingDirectory, args.outputPath)
625
+ : `${name}-faded.${ext}`;
626
+ try {
627
+ const result = await executeFade({
628
+ videoPath,
629
+ outputPath,
630
+ fadeIn: args.fadeIn,
631
+ fadeOut: args.fadeOut,
632
+ audioOnly: args.audioOnly,
633
+ videoOnly: args.videoOnly,
634
+ });
635
+ if (!result.success) {
636
+ return {
637
+ toolCallId: "",
638
+ success: false,
639
+ output: "",
640
+ error: result.error || "Fade failed",
641
+ };
642
+ }
643
+ const lines = [];
644
+ lines.push(`Fade effects applied: ${result.outputPath}`);
645
+ lines.push(`Total duration: ${result.totalDuration.toFixed(1)}s`);
646
+ if (result.fadeInApplied)
647
+ lines.push(`Fade-in applied`);
648
+ if (result.fadeOutApplied)
649
+ lines.push(`Fade-out applied`);
650
+ return {
651
+ toolCallId: "",
652
+ success: true,
653
+ output: lines.join("\n"),
654
+ };
655
+ }
656
+ catch (error) {
657
+ return {
658
+ toolCallId: "",
659
+ success: false,
660
+ output: "",
661
+ error: `Fade failed: ${error instanceof Error ? error.message : String(error)}`,
662
+ };
663
+ }
664
+ };
665
+ const thumbnailBestFrameHandler = async (args, context) => {
666
+ const videoPath = resolve(context.workingDirectory, args.videoPath);
667
+ const name = videoPath.replace(/\.[^.]+$/, "");
668
+ const outputPath = args.outputPath
669
+ ? resolve(context.workingDirectory, args.outputPath)
670
+ : `${name}-thumbnail.png`;
671
+ try {
672
+ const result = await executeThumbnailBestFrame({
673
+ videoPath,
674
+ outputPath,
675
+ prompt: args.prompt,
676
+ model: args.model,
677
+ });
678
+ if (!result.success) {
679
+ return {
680
+ toolCallId: "",
681
+ success: false,
682
+ output: "",
683
+ error: result.error || "Best frame extraction failed",
684
+ };
685
+ }
686
+ const lines = [];
687
+ lines.push(`Best frame extracted: ${result.outputPath}`);
688
+ lines.push(`Timestamp: ${result.timestamp.toFixed(2)}s`);
689
+ if (result.reason)
690
+ lines.push(`Reason: ${sanitizeAIResult(result.reason)}`);
691
+ return {
692
+ toolCallId: "",
693
+ success: true,
694
+ output: lines.join("\n"),
695
+ };
696
+ }
697
+ catch (error) {
698
+ return {
699
+ toolCallId: "",
700
+ success: false,
701
+ output: "",
702
+ error: `Best frame extraction failed: ${error instanceof Error ? error.message : String(error)}`,
703
+ };
704
+ }
705
+ };
706
+ const translateSrtHandler = async (args, context) => {
707
+ const srtPath = resolve(context.workingDirectory, args.srtPath);
708
+ const target = args.targetLanguage;
709
+ const ext = srtPath.split(".").pop() || "srt";
710
+ const name = srtPath.replace(/\.[^.]+$/, "");
711
+ const outputPath = args.outputPath
712
+ ? resolve(context.workingDirectory, args.outputPath)
713
+ : `${name}-${target}.${ext}`;
714
+ try {
715
+ const result = await executeTranslateSrt({
716
+ srtPath,
717
+ outputPath,
718
+ targetLanguage: target,
719
+ provider: args.provider,
720
+ sourceLanguage: args.sourceLanguage,
721
+ });
722
+ if (!result.success) {
723
+ return {
724
+ toolCallId: "",
725
+ success: false,
726
+ output: "",
727
+ error: result.error || "Translation failed",
728
+ };
729
+ }
730
+ const lines = [];
731
+ lines.push(`Translation complete: ${result.outputPath}`);
732
+ lines.push(`Segments translated: ${result.segmentCount}`);
733
+ lines.push(`Target language: ${result.targetLanguage}`);
734
+ return {
735
+ toolCallId: "",
736
+ success: true,
737
+ output: lines.join("\n"),
738
+ };
739
+ }
740
+ catch (error) {
741
+ return {
742
+ toolCallId: "",
743
+ success: false,
744
+ output: "",
745
+ error: `Translation failed: ${error instanceof Error ? error.message : String(error)}`,
746
+ };
747
+ }
748
+ };
749
+ // ============================================================================
750
+ // Registration
751
+ // ============================================================================
752
+ export function registerEditingTools(registry) {
753
+ registry.register(textOverlayDef, textOverlayHandler);
754
+ registry.register(reviewDef, reviewHandler);
755
+ registry.register(silenceCutDef, silenceCutHandler);
756
+ registry.register(jumpCutDef, jumpCutHandler);
757
+ registry.register(captionDef, captionHandler);
758
+ registry.register(noiseReduceDef, noiseReduceHandler);
759
+ registry.register(fadeDef, fadeHandler);
760
+ registry.register(thumbnailBestFrameDef, thumbnailBestFrameHandler);
761
+ registry.register(translateSrtDef, translateSrtHandler);
762
+ }
763
+ //# sourceMappingURL=ai-editing.js.map