@kenkaiiii/gg-editor 0.7.0 → 0.7.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 (446) hide show
  1. package/dist/cli.js +64 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/core/audio-mix.d.ts.map +1 -1
  4. package/dist/core/audio-mix.js +8 -1
  5. package/dist/core/audio-mix.js.map +1 -1
  6. package/dist/core/audio-mix.test.js +1 -1
  7. package/dist/core/audio-mix.test.js.map +1 -1
  8. package/dist/core/auth/api-keys.d.ts +1 -1
  9. package/dist/core/auth/api-keys.d.ts.map +1 -1
  10. package/dist/core/auth/api-keys.js +2 -1
  11. package/dist/core/auth/api-keys.js.map +1 -1
  12. package/dist/core/auth/login.d.ts.map +1 -1
  13. package/dist/core/auth/login.js.map +1 -1
  14. package/dist/core/beats.d.ts +59 -0
  15. package/dist/core/beats.d.ts.map +1 -0
  16. package/dist/core/beats.js +122 -0
  17. package/dist/core/beats.js.map +1 -0
  18. package/dist/core/beats.test.d.ts +2 -0
  19. package/dist/core/beats.test.d.ts.map +1 -0
  20. package/dist/core/beats.test.js +86 -0
  21. package/dist/core/beats.test.js.map +1 -0
  22. package/dist/core/brand-kit.d.ts +80 -0
  23. package/dist/core/brand-kit.d.ts.map +1 -0
  24. package/dist/core/brand-kit.js +96 -0
  25. package/dist/core/brand-kit.js.map +1 -0
  26. package/dist/core/brand-kit.test.d.ts +2 -0
  27. package/dist/core/brand-kit.test.d.ts.map +1 -0
  28. package/dist/core/brand-kit.test.js +76 -0
  29. package/dist/core/brand-kit.test.js.map +1 -0
  30. package/dist/core/bundled-sfx.d.ts +64 -0
  31. package/dist/core/bundled-sfx.d.ts.map +1 -0
  32. package/dist/core/bundled-sfx.js +218 -0
  33. package/dist/core/bundled-sfx.js.map +1 -0
  34. package/dist/core/bundled-sfx.test.d.ts +2 -0
  35. package/dist/core/bundled-sfx.test.d.ts.map +1 -0
  36. package/dist/core/bundled-sfx.test.js +81 -0
  37. package/dist/core/bundled-sfx.test.js.map +1 -0
  38. package/dist/core/child-abort.d.ts +57 -0
  39. package/dist/core/child-abort.d.ts.map +1 -0
  40. package/dist/core/child-abort.js +95 -0
  41. package/dist/core/child-abort.js.map +1 -0
  42. package/dist/core/child-abort.test.d.ts +2 -0
  43. package/dist/core/child-abort.test.d.ts.map +1 -0
  44. package/dist/core/child-abort.test.js +88 -0
  45. package/dist/core/child-abort.test.js.map +1 -0
  46. package/dist/core/clip-scoring.d.ts +44 -0
  47. package/dist/core/clip-scoring.d.ts.map +1 -0
  48. package/dist/core/clip-scoring.js +165 -0
  49. package/dist/core/clip-scoring.js.map +1 -0
  50. package/dist/core/clip-scoring.test.d.ts +2 -0
  51. package/dist/core/clip-scoring.test.d.ts.map +1 -0
  52. package/dist/core/clip-scoring.test.js +113 -0
  53. package/dist/core/clip-scoring.test.js.map +1 -0
  54. package/dist/core/emoji-captions.d.ts +45 -0
  55. package/dist/core/emoji-captions.d.ts.map +1 -0
  56. package/dist/core/emoji-captions.js +121 -0
  57. package/dist/core/emoji-captions.js.map +1 -0
  58. package/dist/core/face-reframe.d.ts +91 -0
  59. package/dist/core/face-reframe.d.ts.map +1 -0
  60. package/dist/core/face-reframe.js +141 -0
  61. package/dist/core/face-reframe.js.map +1 -0
  62. package/dist/core/face-reframe.test.d.ts +2 -0
  63. package/dist/core/face-reframe.test.d.ts.map +1 -0
  64. package/dist/core/face-reframe.test.js +171 -0
  65. package/dist/core/face-reframe.test.js.map +1 -0
  66. package/dist/core/filler-words.d.ts +57 -9
  67. package/dist/core/filler-words.d.ts.map +1 -1
  68. package/dist/core/filler-words.js +61 -9
  69. package/dist/core/filler-words.js.map +1 -1
  70. package/dist/core/filler-words.test.js +91 -17
  71. package/dist/core/filler-words.test.js.map +1 -1
  72. package/dist/core/hook-rewrite.d.ts +48 -0
  73. package/dist/core/hook-rewrite.d.ts.map +1 -0
  74. package/dist/core/hook-rewrite.js +151 -0
  75. package/dist/core/hook-rewrite.js.map +1 -0
  76. package/dist/core/hook-rewrite.test.d.ts +2 -0
  77. package/dist/core/hook-rewrite.test.d.ts.map +1 -0
  78. package/dist/core/hook-rewrite.test.js +58 -0
  79. package/dist/core/hook-rewrite.test.js.map +1 -0
  80. package/dist/core/hosts/lazy.d.ts.map +1 -1
  81. package/dist/core/hosts/lazy.js +2 -0
  82. package/dist/core/hosts/lazy.js.map +1 -1
  83. package/dist/core/hosts/premiere/adapter.d.ts +1 -0
  84. package/dist/core/hosts/premiere/adapter.d.ts.map +1 -1
  85. package/dist/core/hosts/premiere/adapter.js.map +1 -1
  86. package/dist/core/hosts/premiere/bridge-source.d.ts.map +1 -1
  87. package/dist/core/hosts/premiere/bridge-source.js +6 -3
  88. package/dist/core/hosts/premiere/bridge-source.js.map +1 -1
  89. package/dist/core/hosts/resolve/adapter.d.ts +1 -0
  90. package/dist/core/hosts/resolve/adapter.d.ts.map +1 -1
  91. package/dist/core/hosts/resolve/adapter.js.map +1 -1
  92. package/dist/core/hosts/resolve/bridge-source.d.ts.map +1 -1
  93. package/dist/core/hosts/resolve/bridge-source.js +31 -4
  94. package/dist/core/hosts/resolve/bridge-source.js.map +1 -1
  95. package/dist/core/hosts/resolve/bridge.d.ts +2 -19
  96. package/dist/core/hosts/resolve/bridge.d.ts.map +1 -1
  97. package/dist/core/hosts/resolve/bridge.js +70 -41
  98. package/dist/core/hosts/resolve/bridge.js.map +1 -1
  99. package/dist/core/hosts/resolve/bridge.test.js +130 -0
  100. package/dist/core/hosts/resolve/bridge.test.js.map +1 -1
  101. package/dist/core/hosts/types.d.ts +6 -0
  102. package/dist/core/hosts/types.d.ts.map +1 -1
  103. package/dist/core/hosts/types.js.map +1 -1
  104. package/dist/core/logger.d.ts +32 -0
  105. package/dist/core/logger.d.ts.map +1 -0
  106. package/dist/core/logger.js +188 -0
  107. package/dist/core/logger.js.map +1 -0
  108. package/dist/core/loop-match.d.ts +57 -0
  109. package/dist/core/loop-match.d.ts.map +1 -0
  110. package/dist/core/loop-match.js +91 -0
  111. package/dist/core/loop-match.js.map +1 -0
  112. package/dist/core/media/ffmpeg.d.ts.map +1 -1
  113. package/dist/core/media/ffmpeg.js +14 -3
  114. package/dist/core/media/ffmpeg.js.map +1 -1
  115. package/dist/core/multi-format.d.ts +67 -0
  116. package/dist/core/multi-format.d.ts.map +1 -0
  117. package/dist/core/multi-format.js +127 -0
  118. package/dist/core/multi-format.js.map +1 -0
  119. package/dist/core/multi-format.test.d.ts +2 -0
  120. package/dist/core/multi-format.test.d.ts.map +1 -0
  121. package/dist/core/multi-format.test.js +151 -0
  122. package/dist/core/multi-format.test.js.map +1 -0
  123. package/dist/core/python/beats.py +61 -0
  124. package/dist/core/python/face_reframe.py +163 -0
  125. package/dist/core/python/sidecar-path.d.ts +13 -0
  126. package/dist/core/python/sidecar-path.d.ts.map +1 -0
  127. package/dist/core/python/sidecar-path.js +24 -0
  128. package/dist/core/python/sidecar-path.js.map +1 -0
  129. package/dist/core/python.d.ts +57 -0
  130. package/dist/core/python.d.ts.map +1 -0
  131. package/dist/core/python.js +107 -0
  132. package/dist/core/python.js.map +1 -0
  133. package/dist/core/python.test.d.ts +2 -0
  134. package/dist/core/python.test.d.ts.map +1 -0
  135. package/dist/core/python.test.js +129 -0
  136. package/dist/core/python.test.js.map +1 -0
  137. package/dist/core/retention-structure.d.ts +81 -0
  138. package/dist/core/retention-structure.d.ts.map +1 -0
  139. package/dist/core/retention-structure.js +206 -0
  140. package/dist/core/retention-structure.js.map +1 -0
  141. package/dist/core/retention-structure.test.d.ts +2 -0
  142. package/dist/core/retention-structure.test.d.ts.map +1 -0
  143. package/dist/core/retention-structure.test.js +88 -0
  144. package/dist/core/retention-structure.test.js.map +1 -0
  145. package/dist/core/review.d.ts +17 -0
  146. package/dist/core/review.d.ts.map +1 -1
  147. package/dist/core/review.js +20 -24
  148. package/dist/core/review.js.map +1 -1
  149. package/dist/core/safe-paths.d.ts +11 -0
  150. package/dist/core/safe-paths.d.ts.map +1 -1
  151. package/dist/core/safe-paths.js +26 -10
  152. package/dist/core/safe-paths.js.map +1 -1
  153. package/dist/core/safe-paths.test.js +16 -0
  154. package/dist/core/safe-paths.test.js.map +1 -1
  155. package/dist/core/skills-loader.d.ts +48 -2
  156. package/dist/core/skills-loader.d.ts.map +1 -1
  157. package/dist/core/skills-loader.js +97 -19
  158. package/dist/core/skills-loader.js.map +1 -1
  159. package/dist/core/skills-loader.test.js +63 -1
  160. package/dist/core/skills-loader.test.js.map +1 -1
  161. package/dist/core/srt.d.ts +42 -7
  162. package/dist/core/srt.d.ts.map +1 -1
  163. package/dist/core/srt.js +101 -32
  164. package/dist/core/srt.js.map +1 -1
  165. package/dist/core/srt.test.js +54 -1
  166. package/dist/core/srt.test.js.map +1 -1
  167. package/dist/core/thumbnail-compose.d.ts +58 -0
  168. package/dist/core/thumbnail-compose.d.ts.map +1 -0
  169. package/dist/core/thumbnail-compose.js +101 -0
  170. package/dist/core/thumbnail-compose.js.map +1 -0
  171. package/dist/core/thumbnail-promise.d.ts +46 -0
  172. package/dist/core/thumbnail-promise.d.ts.map +1 -0
  173. package/dist/core/thumbnail-promise.js +133 -0
  174. package/dist/core/thumbnail-promise.js.map +1 -0
  175. package/dist/core/thumbnail-promise.test.d.ts +2 -0
  176. package/dist/core/thumbnail-promise.test.d.ts.map +1 -0
  177. package/dist/core/thumbnail-promise.test.js +52 -0
  178. package/dist/core/thumbnail-promise.test.js.map +1 -0
  179. package/dist/core/viral-moments.d.ts +70 -0
  180. package/dist/core/viral-moments.d.ts.map +1 -0
  181. package/dist/core/viral-moments.js +192 -0
  182. package/dist/core/viral-moments.js.map +1 -0
  183. package/dist/core/viral-moments.test.d.ts +2 -0
  184. package/dist/core/viral-moments.test.d.ts.map +1 -0
  185. package/dist/core/viral-moments.test.js +153 -0
  186. package/dist/core/viral-moments.test.js.map +1 -0
  187. package/dist/core/whisper.d.ts +16 -0
  188. package/dist/core/whisper.d.ts.map +1 -1
  189. package/dist/core/whisper.js +72 -5
  190. package/dist/core/whisper.js.map +1 -1
  191. package/dist/core/whisper.test.js +111 -1
  192. package/dist/core/whisper.test.js.map +1 -1
  193. package/dist/core/youtube-metadata.d.ts +44 -0
  194. package/dist/core/youtube-metadata.d.ts.map +1 -0
  195. package/dist/core/youtube-metadata.js +168 -0
  196. package/dist/core/youtube-metadata.js.map +1 -0
  197. package/dist/core/youtube-metadata.test.d.ts +2 -0
  198. package/dist/core/youtube-metadata.test.d.ts.map +1 -0
  199. package/dist/core/youtube-metadata.test.js +132 -0
  200. package/dist/core/youtube-metadata.test.js.map +1 -0
  201. package/dist/prompt-commands.d.ts +24 -0
  202. package/dist/prompt-commands.d.ts.map +1 -0
  203. package/dist/prompt-commands.js +243 -0
  204. package/dist/prompt-commands.js.map +1 -0
  205. package/dist/prompt-commands.test.d.ts +2 -0
  206. package/dist/prompt-commands.test.d.ts.map +1 -0
  207. package/dist/prompt-commands.test.js +46 -0
  208. package/dist/prompt-commands.test.js.map +1 -0
  209. package/dist/skills.d.ts +6 -6
  210. package/dist/skills.d.ts.map +1 -1
  211. package/dist/skills.js +1426 -445
  212. package/dist/skills.js.map +1 -1
  213. package/dist/system-prompt.d.ts.map +1 -1
  214. package/dist/system-prompt.js +108 -0
  215. package/dist/system-prompt.js.map +1 -1
  216. package/dist/tools/add-fades.d.ts.map +1 -1
  217. package/dist/tools/add-fades.js +2 -1
  218. package/dist/tools/add-fades.js.map +1 -1
  219. package/dist/tools/add-sfx-at-cuts.d.ts.map +1 -1
  220. package/dist/tools/add-sfx-at-cuts.js +36 -11
  221. package/dist/tools/add-sfx-at-cuts.js.map +1 -1
  222. package/dist/tools/add-sfx-to-timeline.d.ts +34 -0
  223. package/dist/tools/add-sfx-to-timeline.d.ts.map +1 -0
  224. package/dist/tools/add-sfx-to-timeline.js +169 -0
  225. package/dist/tools/add-sfx-to-timeline.js.map +1 -0
  226. package/dist/tools/add-sfx-to-timeline.test.d.ts +2 -0
  227. package/dist/tools/add-sfx-to-timeline.test.d.ts.map +1 -0
  228. package/dist/tools/add-sfx-to-timeline.test.js +181 -0
  229. package/dist/tools/add-sfx-to-timeline.test.js.map +1 -0
  230. package/dist/tools/audit-first-frame.d.ts +36 -0
  231. package/dist/tools/audit-first-frame.d.ts.map +1 -0
  232. package/dist/tools/audit-first-frame.js +181 -0
  233. package/dist/tools/audit-first-frame.js.map +1 -0
  234. package/dist/tools/audit-retention-structure.d.ts +20 -0
  235. package/dist/tools/audit-retention-structure.d.ts.map +1 -0
  236. package/dist/tools/audit-retention-structure.js +95 -0
  237. package/dist/tools/audit-retention-structure.js.map +1 -0
  238. package/dist/tools/audit-retention-structure.test.d.ts +2 -0
  239. package/dist/tools/audit-retention-structure.test.d.ts.map +1 -0
  240. package/dist/tools/audit-retention-structure.test.js +93 -0
  241. package/dist/tools/audit-retention-structure.test.js.map +1 -0
  242. package/dist/tools/bleep-words.d.ts +59 -0
  243. package/dist/tools/bleep-words.d.ts.map +1 -0
  244. package/dist/tools/bleep-words.js +211 -0
  245. package/dist/tools/bleep-words.js.map +1 -0
  246. package/dist/tools/bleep-words.test.d.ts +2 -0
  247. package/dist/tools/bleep-words.test.d.ts.map +1 -0
  248. package/dist/tools/bleep-words.test.js +96 -0
  249. package/dist/tools/bleep-words.test.js.map +1 -0
  250. package/dist/tools/burn-subtitles.d.ts.map +1 -1
  251. package/dist/tools/burn-subtitles.js +10 -5
  252. package/dist/tools/burn-subtitles.js.map +1 -1
  253. package/dist/tools/clean-audio.d.ts.map +1 -1
  254. package/dist/tools/clean-audio.js +2 -1
  255. package/dist/tools/clean-audio.js.map +1 -1
  256. package/dist/tools/cluster-takes.js +2 -1
  257. package/dist/tools/cluster-takes.js.map +1 -1
  258. package/dist/tools/compose-thumbnail-variants.d.ts +70 -0
  259. package/dist/tools/compose-thumbnail-variants.d.ts.map +1 -0
  260. package/dist/tools/compose-thumbnail-variants.js +274 -0
  261. package/dist/tools/compose-thumbnail-variants.js.map +1 -0
  262. package/dist/tools/compose-thumbnail.d.ts +6 -13
  263. package/dist/tools/compose-thumbnail.d.ts.map +1 -1
  264. package/dist/tools/compose-thumbnail.js +44 -81
  265. package/dist/tools/compose-thumbnail.js.map +1 -1
  266. package/dist/tools/concat-videos.d.ts.map +1 -1
  267. package/dist/tools/concat-videos.js +12 -5
  268. package/dist/tools/concat-videos.js.map +1 -1
  269. package/dist/tools/concat-videos.test.d.ts +2 -0
  270. package/dist/tools/concat-videos.test.d.ts.map +1 -0
  271. package/dist/tools/concat-videos.test.js +103 -0
  272. package/dist/tools/concat-videos.test.js.map +1 -0
  273. package/dist/tools/crossfade-videos.d.ts.map +1 -1
  274. package/dist/tools/crossfade-videos.js +2 -1
  275. package/dist/tools/crossfade-videos.js.map +1 -1
  276. package/dist/tools/cut-filler-words.d.ts.map +1 -1
  277. package/dist/tools/cut-filler-words.js +24 -8
  278. package/dist/tools/cut-filler-words.js.map +1 -1
  279. package/dist/tools/detect-speaker-changes.js +2 -1
  280. package/dist/tools/detect-speaker-changes.js.map +1 -1
  281. package/dist/tools/extract-audio.d.ts.map +1 -1
  282. package/dist/tools/extract-audio.js +13 -7
  283. package/dist/tools/extract-audio.js.map +1 -1
  284. package/dist/tools/face-reframe.d.ts +30 -0
  285. package/dist/tools/face-reframe.d.ts.map +1 -0
  286. package/dist/tools/face-reframe.js +143 -0
  287. package/dist/tools/face-reframe.js.map +1 -0
  288. package/dist/tools/face-reframe.test.d.ts +2 -0
  289. package/dist/tools/face-reframe.test.d.ts.map +1 -0
  290. package/dist/tools/face-reframe.test.js +139 -0
  291. package/dist/tools/face-reframe.test.js.map +1 -0
  292. package/dist/tools/find-viral-moments.d.ts +23 -0
  293. package/dist/tools/find-viral-moments.d.ts.map +1 -0
  294. package/dist/tools/find-viral-moments.js +176 -0
  295. package/dist/tools/find-viral-moments.js.map +1 -0
  296. package/dist/tools/find-viral-moments.test.d.ts +2 -0
  297. package/dist/tools/find-viral-moments.test.d.ts.map +1 -0
  298. package/dist/tools/find-viral-moments.test.js +144 -0
  299. package/dist/tools/find-viral-moments.test.js.map +1 -0
  300. package/dist/tools/generate-gif.d.ts.map +1 -1
  301. package/dist/tools/generate-gif.js +47 -40
  302. package/dist/tools/generate-gif.js.map +1 -1
  303. package/dist/tools/generate-gif.test.d.ts +2 -0
  304. package/dist/tools/generate-gif.test.d.ts.map +1 -0
  305. package/dist/tools/generate-gif.test.js +115 -0
  306. package/dist/tools/generate-gif.test.js.map +1 -0
  307. package/dist/tools/generate-outro.d.ts +18 -0
  308. package/dist/tools/generate-outro.d.ts.map +1 -0
  309. package/dist/tools/generate-outro.js +175 -0
  310. package/dist/tools/generate-outro.js.map +1 -0
  311. package/dist/tools/generate-youtube-metadata.d.ts +23 -0
  312. package/dist/tools/generate-youtube-metadata.d.ts.map +1 -0
  313. package/dist/tools/generate-youtube-metadata.js +103 -0
  314. package/dist/tools/generate-youtube-metadata.js.map +1 -0
  315. package/dist/tools/generate-youtube-metadata.test.d.ts +2 -0
  316. package/dist/tools/generate-youtube-metadata.test.d.ts.map +1 -0
  317. package/dist/tools/generate-youtube-metadata.test.js +118 -0
  318. package/dist/tools/generate-youtube-metadata.test.js.map +1 -0
  319. package/dist/tools/index.d.ts +14 -0
  320. package/dist/tools/index.d.ts.map +1 -1
  321. package/dist/tools/index.js +130 -1
  322. package/dist/tools/index.js.map +1 -1
  323. package/dist/tools/index.test.js +27 -1
  324. package/dist/tools/index.test.js.map +1 -1
  325. package/dist/tools/ken-burns.d.ts.map +1 -1
  326. package/dist/tools/ken-burns.js +2 -1
  327. package/dist/tools/ken-burns.js.map +1 -1
  328. package/dist/tools/loop-match-short.d.ts +22 -0
  329. package/dist/tools/loop-match-short.d.ts.map +1 -0
  330. package/dist/tools/loop-match-short.js +107 -0
  331. package/dist/tools/loop-match-short.js.map +1 -0
  332. package/dist/tools/mix-audio.d.ts.map +1 -1
  333. package/dist/tools/mix-audio.js +2 -1
  334. package/dist/tools/mix-audio.js.map +1 -1
  335. package/dist/tools/normalize-loudness.d.ts.map +1 -1
  336. package/dist/tools/normalize-loudness.js +2 -1
  337. package/dist/tools/normalize-loudness.js.map +1 -1
  338. package/dist/tools/path-traversal.test.d.ts +15 -0
  339. package/dist/tools/path-traversal.test.d.ts.map +1 -0
  340. package/dist/tools/path-traversal.test.js +223 -0
  341. package/dist/tools/path-traversal.test.js.map +1 -0
  342. package/dist/tools/pick-best-takes.js +2 -1
  343. package/dist/tools/pick-best-takes.js.map +1 -1
  344. package/dist/tools/punch-in.d.ts.map +1 -1
  345. package/dist/tools/punch-in.js +2 -1
  346. package/dist/tools/punch-in.js.map +1 -1
  347. package/dist/tools/read-transcript.js +2 -1
  348. package/dist/tools/read-transcript.js.map +1 -1
  349. package/dist/tools/render-multi-format.d.ts +35 -0
  350. package/dist/tools/render-multi-format.d.ts.map +1 -0
  351. package/dist/tools/render-multi-format.js +206 -0
  352. package/dist/tools/render-multi-format.js.map +1 -0
  353. package/dist/tools/render-multi-format.test.d.ts +2 -0
  354. package/dist/tools/render-multi-format.test.d.ts.map +1 -0
  355. package/dist/tools/render-multi-format.test.js +312 -0
  356. package/dist/tools/render-multi-format.test.js.map +1 -0
  357. package/dist/tools/render.d.ts.map +1 -1
  358. package/dist/tools/render.js +2 -2
  359. package/dist/tools/render.js.map +1 -1
  360. package/dist/tools/rewrite-hook.d.ts +32 -0
  361. package/dist/tools/rewrite-hook.d.ts.map +1 -0
  362. package/dist/tools/rewrite-hook.js +65 -0
  363. package/dist/tools/rewrite-hook.js.map +1 -0
  364. package/dist/tools/score-clip.d.ts +30 -0
  365. package/dist/tools/score-clip.d.ts.map +1 -0
  366. package/dist/tools/score-clip.js +109 -0
  367. package/dist/tools/score-clip.js.map +1 -0
  368. package/dist/tools/score-clip.test.d.ts +2 -0
  369. package/dist/tools/score-clip.test.d.ts.map +1 -0
  370. package/dist/tools/score-clip.test.js +110 -0
  371. package/dist/tools/score-clip.test.js.map +1 -0
  372. package/dist/tools/search-tools.d.ts +34 -0
  373. package/dist/tools/search-tools.d.ts.map +1 -0
  374. package/dist/tools/search-tools.js +86 -0
  375. package/dist/tools/search-tools.js.map +1 -0
  376. package/dist/tools/search-tools.test.d.ts +2 -0
  377. package/dist/tools/search-tools.test.d.ts.map +1 -0
  378. package/dist/tools/search-tools.test.js +60 -0
  379. package/dist/tools/search-tools.test.js.map +1 -0
  380. package/dist/tools/snap-cuts-to-beats.d.ts +18 -0
  381. package/dist/tools/snap-cuts-to-beats.d.ts.map +1 -0
  382. package/dist/tools/snap-cuts-to-beats.js +110 -0
  383. package/dist/tools/snap-cuts-to-beats.js.map +1 -0
  384. package/dist/tools/snap-cuts-to-beats.test.d.ts +2 -0
  385. package/dist/tools/snap-cuts-to-beats.test.d.ts.map +1 -0
  386. package/dist/tools/snap-cuts-to-beats.test.js +99 -0
  387. package/dist/tools/snap-cuts-to-beats.test.js.map +1 -0
  388. package/dist/tools/speed-ramp.d.ts.map +1 -1
  389. package/dist/tools/speed-ramp.js +2 -1
  390. package/dist/tools/speed-ramp.js.map +1 -1
  391. package/dist/tools/stabilize-video.d.ts.map +1 -1
  392. package/dist/tools/stabilize-video.js +2 -1
  393. package/dist/tools/stabilize-video.js.map +1 -1
  394. package/dist/tools/suggest-broll.d.ts +34 -0
  395. package/dist/tools/suggest-broll.d.ts.map +1 -0
  396. package/dist/tools/suggest-broll.js +367 -0
  397. package/dist/tools/suggest-broll.js.map +1 -0
  398. package/dist/tools/suggest-broll.test.d.ts +2 -0
  399. package/dist/tools/suggest-broll.test.d.ts.map +1 -0
  400. package/dist/tools/suggest-broll.test.js +217 -0
  401. package/dist/tools/suggest-broll.test.js.map +1 -0
  402. package/dist/tools/text-based-cut.d.ts +33 -0
  403. package/dist/tools/text-based-cut.d.ts.map +1 -0
  404. package/dist/tools/text-based-cut.js +172 -0
  405. package/dist/tools/text-based-cut.js.map +1 -0
  406. package/dist/tools/text-based-cut.test.d.ts +2 -0
  407. package/dist/tools/text-based-cut.test.d.ts.map +1 -0
  408. package/dist/tools/text-based-cut.test.js +32 -0
  409. package/dist/tools/text-based-cut.test.js.map +1 -0
  410. package/dist/tools/transcribe.d.ts +1 -1
  411. package/dist/tools/transition-videos.d.ts +1 -1
  412. package/dist/tools/transition-videos.d.ts.map +1 -1
  413. package/dist/tools/transition-videos.js +2 -1
  414. package/dist/tools/transition-videos.js.map +1 -1
  415. package/dist/tools/trim-dead-air.d.ts +59 -0
  416. package/dist/tools/trim-dead-air.d.ts.map +1 -0
  417. package/dist/tools/trim-dead-air.js +215 -0
  418. package/dist/tools/trim-dead-air.js.map +1 -0
  419. package/dist/tools/trim-dead-air.test.d.ts +2 -0
  420. package/dist/tools/trim-dead-air.test.d.ts.map +1 -0
  421. package/dist/tools/trim-dead-air.test.js +75 -0
  422. package/dist/tools/trim-dead-air.test.js.map +1 -0
  423. package/dist/tools/verify-thumbnail-promise.d.ts +33 -0
  424. package/dist/tools/verify-thumbnail-promise.d.ts.map +1 -0
  425. package/dist/tools/verify-thumbnail-promise.js +112 -0
  426. package/dist/tools/verify-thumbnail-promise.js.map +1 -0
  427. package/dist/tools/verify-thumbnail-promise.test.d.ts +2 -0
  428. package/dist/tools/verify-thumbnail-promise.test.d.ts.map +1 -0
  429. package/dist/tools/verify-thumbnail-promise.test.js +38 -0
  430. package/dist/tools/verify-thumbnail-promise.test.js.map +1 -0
  431. package/dist/tools/write-keyword-captions.d.ts +7 -0
  432. package/dist/tools/write-keyword-captions.d.ts.map +1 -1
  433. package/dist/tools/write-keyword-captions.js +35 -4
  434. package/dist/tools/write-keyword-captions.js.map +1 -1
  435. package/dist/ui/App.d.ts.map +1 -1
  436. package/dist/ui/App.js +75 -11
  437. package/dist/ui/App.js.map +1 -1
  438. package/dist/ui/tool-formatters.d.ts +30 -0
  439. package/dist/ui/tool-formatters.d.ts.map +1 -0
  440. package/dist/ui/tool-formatters.js +461 -0
  441. package/dist/ui/tool-formatters.js.map +1 -0
  442. package/dist/ui/tool-formatters.test.d.ts +2 -0
  443. package/dist/ui/tool-formatters.test.d.ts.map +1 -0
  444. package/dist/ui/tool-formatters.test.js +143 -0
  445. package/dist/ui/tool-formatters.test.js.map +1 -0
  446. package/package.json +10 -9
@@ -0,0 +1,129 @@
1
+ import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
2
+ function ok(stdout) {
3
+ return { pid: 0, status: 0, signal: null, output: [], stdout, stderr: "" };
4
+ }
5
+ function fail() {
6
+ return { pid: 0, status: 1, signal: null, output: [], stdout: "", stderr: "" };
7
+ }
8
+ describe("findPython caching", () => {
9
+ beforeEach(() => {
10
+ vi.resetModules();
11
+ });
12
+ afterEach(() => {
13
+ vi.doUnmock("node:child_process");
14
+ vi.resetModules();
15
+ });
16
+ it("returns the same PythonCmd on every call and only spawns once", async () => {
17
+ const calls = [];
18
+ vi.doMock("node:child_process", () => ({
19
+ spawnSync: (cmd, args) => {
20
+ calls.push({ cmd, args });
21
+ if (cmd === "python3")
22
+ return ok("Python 3.11.4\n");
23
+ return fail();
24
+ },
25
+ spawn: () => {
26
+ throw new Error("spawn should not be called by findPython");
27
+ },
28
+ }));
29
+ const mod = await import("./python.js");
30
+ mod.__resetPythonCacheForTests();
31
+ const first = mod.findPython();
32
+ expect(first?.cmd).toBe("python3");
33
+ // Two probes on first call: --version, then sys.prefix.
34
+ expect(calls.length).toBe(2);
35
+ for (let i = 0; i < 25; i++) {
36
+ const next = mod.findPython();
37
+ expect(next).toBe(first);
38
+ }
39
+ // Cache hit — no further spawns.
40
+ expect(calls.length).toBe(2);
41
+ });
42
+ it("does not cache a negative result", async () => {
43
+ let probeCount = 0;
44
+ vi.doMock("node:child_process", () => ({
45
+ spawnSync: () => {
46
+ probeCount++;
47
+ return fail();
48
+ },
49
+ spawn: () => {
50
+ throw new Error("spawn should not be called");
51
+ },
52
+ }));
53
+ const mod = await import("./python.js");
54
+ mod.__resetPythonCacheForTests();
55
+ expect(mod.findPython()).toBeUndefined();
56
+ const probesAfterFirst = probeCount;
57
+ expect(probesAfterFirst).toBeGreaterThanOrEqual(3); // tries python3, python, py
58
+ // Second call must re-probe — Python may have been installed since.
59
+ expect(mod.findPython()).toBeUndefined();
60
+ expect(probeCount).toBeGreaterThan(probesAfterFirst);
61
+ });
62
+ it("falls back to `python` when `python3` is absent and caches it", async () => {
63
+ const calls = [];
64
+ vi.doMock("node:child_process", () => ({
65
+ spawnSync: (cmd) => {
66
+ calls.push(cmd);
67
+ if (cmd === "python")
68
+ return ok("Python 3.12.1\n");
69
+ return fail();
70
+ },
71
+ spawn: () => {
72
+ throw new Error("spawn should not be called");
73
+ },
74
+ }));
75
+ const mod = await import("./python.js");
76
+ mod.__resetPythonCacheForTests();
77
+ const first = mod.findPython();
78
+ expect(first?.cmd).toBe("python");
79
+ const before = calls.length;
80
+ const second = mod.findPython();
81
+ expect(second).toBe(first);
82
+ expect(calls.length).toBe(before);
83
+ });
84
+ it("rejects a Python 2 candidate and keeps probing", async () => {
85
+ vi.doMock("node:child_process", () => ({
86
+ spawnSync: (cmd) => {
87
+ if (cmd === "python3")
88
+ return fail();
89
+ if (cmd === "python")
90
+ return ok("Python 2.7.18\n");
91
+ if (cmd === "py")
92
+ return ok("Python 3.10.6\n");
93
+ return fail();
94
+ },
95
+ spawn: () => {
96
+ throw new Error("spawn should not be called");
97
+ },
98
+ }));
99
+ const mod = await import("./python.js");
100
+ mod.__resetPythonCacheForTests();
101
+ const py = mod.findPython();
102
+ expect(py?.cmd).toBe("py");
103
+ expect(py?.args).toEqual(["-3"]);
104
+ });
105
+ it("__resetPythonCacheForTests forces a fresh probe", async () => {
106
+ let probes = 0;
107
+ vi.doMock("node:child_process", () => ({
108
+ spawnSync: (cmd) => {
109
+ probes++;
110
+ if (cmd === "python3")
111
+ return ok("Python 3.11.4\n");
112
+ return fail();
113
+ },
114
+ spawn: () => {
115
+ throw new Error("spawn should not be called");
116
+ },
117
+ }));
118
+ const mod = await import("./python.js");
119
+ mod.__resetPythonCacheForTests();
120
+ mod.findPython();
121
+ const afterFirst = probes;
122
+ mod.findPython();
123
+ expect(probes).toBe(afterFirst); // cached
124
+ mod.__resetPythonCacheForTests();
125
+ mod.findPython();
126
+ expect(probes).toBeGreaterThan(afterFirst); // re-probed
127
+ });
128
+ });
129
+ //# sourceMappingURL=python.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python.test.js","sourceRoot":"","sources":["../../src/core/python.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Retention-structure auditor.
3
+ *
4
+ * Long-form videos live or die at the 3-min and 6-min marks. Per the
5
+ * MrBeast manual: every ~3 minutes a "mini-hook" / re-engagement is
6
+ * required — a twist, a reveal, a visual spectacle, an escalation. Flat
7
+ * stretches between mini-hooks bleed retention; the YouTube graph
8
+ * shows the dip almost every time.
9
+ *
10
+ * This module:
11
+ * 1. Builds a 60s window centred on each requested checkpoint.
12
+ * 2. Bundles all windows + a compact transcript outline into ONE LLM call.
13
+ * 3. Parses the response into a structured report with per-checkpoint
14
+ * scores + an overall escalation pattern score.
15
+ *
16
+ * Pure parser + window-builder for unit tests; the network call lives
17
+ * here too but is exported as `runRetentionAudit` so the wrapper tool
18
+ * stays thin.
19
+ */
20
+ import type { Transcript } from "./whisper.js";
21
+ export interface RetentionWindow {
22
+ /** Centre of the window. */
23
+ atSec: number;
24
+ /** Inclusive [startSec, endSec]. */
25
+ startSec: number;
26
+ endSec: number;
27
+ /** Concatenated transcript text falling inside the window. */
28
+ text: string;
29
+ }
30
+ export interface CheckpointScore {
31
+ atSec: number;
32
+ /** 0-1. ≥0.5 = re-engagement detected. <0.5 = flat / weak. */
33
+ score: number;
34
+ /** ≤120 char description of what's happening at this checkpoint. */
35
+ summary: string;
36
+ /** ≤200 char actionable suggestion when score < 0.5. Empty otherwise. */
37
+ suggestion: string;
38
+ }
39
+ export interface RetentionAuditResult {
40
+ checkpoints: CheckpointScore[];
41
+ /** 0-1. Does the video build progressively (1) or stay flat (0)? */
42
+ escalationScore: number;
43
+ /** ≤200 char overall verdict. */
44
+ overallSummary: string;
45
+ /** atSec of the lowest-scoring checkpoint (-1 when checkpoints is empty). */
46
+ weakestCheckpoint: number;
47
+ }
48
+ /**
49
+ * Build a 60s window centred on `atSec`, clamped to [0, totalSec].
50
+ * Returns the concatenated transcript text inside the window for LLM
51
+ * context. Pure — exported for testing.
52
+ */
53
+ export declare function buildWindow(t: Transcript, atSec: number, totalSec: number, windowSec?: number): RetentionWindow;
54
+ /**
55
+ * Compact transcript outline — every Nth segment trimmed to ~60 chars
56
+ * with a leading [t]s tag. Keeps the LLM grounded in overall pacing
57
+ * without blowing the context budget on a long transcript.
58
+ */
59
+ export declare function buildOutline(t: Transcript, maxLines?: number): string;
60
+ export interface RetentionAuditOptions {
61
+ apiKey?: string;
62
+ model?: string;
63
+ /** Default [180, 360] (3min, 6min). */
64
+ checkpoints?: number[];
65
+ /** Override transcript.durationSec (e.g. when you trust ffprobe more). */
66
+ durationSec?: number;
67
+ signal?: AbortSignal;
68
+ }
69
+ /**
70
+ * Run ONE LLM audit pass and return the structured result. Throws on
71
+ * network / parse error so the wrapper can convert to err().
72
+ */
73
+ export declare function runRetentionAudit(t: Transcript, opts?: RetentionAuditOptions): Promise<RetentionAuditResult>;
74
+ /**
75
+ * Parse the model's JSON. Robust to missing keys; pads checkpoints to
76
+ * match the requested order so the caller can correlate by index.
77
+ *
78
+ * Pure — exported for testing.
79
+ */
80
+ export declare function parseAuditResponse(content: string, requested: number[]): RetentionAuditResult;
81
+ //# sourceMappingURL=retention-structure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-structure.d.ts","sourceRoot":"","sources":["../../src/core/retention-structure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,oEAAoE;IACpE,eAAe,EAAE,MAAM,CAAC;IACxB,iCAAiC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,UAAU,EACb,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,SAAK,GACb,eAAe,CAWjB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,SAAK,GAAG,MAAM,CAWjE;AAqCD,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,CAAC,EAAE,UAAU,EACb,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAuD/B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,oBAAoB,CAiD7F"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Retention-structure auditor.
3
+ *
4
+ * Long-form videos live or die at the 3-min and 6-min marks. Per the
5
+ * MrBeast manual: every ~3 minutes a "mini-hook" / re-engagement is
6
+ * required — a twist, a reveal, a visual spectacle, an escalation. Flat
7
+ * stretches between mini-hooks bleed retention; the YouTube graph
8
+ * shows the dip almost every time.
9
+ *
10
+ * This module:
11
+ * 1. Builds a 60s window centred on each requested checkpoint.
12
+ * 2. Bundles all windows + a compact transcript outline into ONE LLM call.
13
+ * 3. Parses the response into a structured report with per-checkpoint
14
+ * scores + an overall escalation pattern score.
15
+ *
16
+ * Pure parser + window-builder for unit tests; the network call lives
17
+ * here too but is exported as `runRetentionAudit` so the wrapper tool
18
+ * stays thin.
19
+ */
20
+ import { resolveApiKey } from "./auth/api-keys.js";
21
+ /**
22
+ * Build a 60s window centred on `atSec`, clamped to [0, totalSec].
23
+ * Returns the concatenated transcript text inside the window for LLM
24
+ * context. Pure — exported for testing.
25
+ */
26
+ export function buildWindow(t, atSec, totalSec, windowSec = 60) {
27
+ const half = windowSec / 2;
28
+ const startSec = Math.max(0, atSec - half);
29
+ const endSec = Math.min(totalSec, atSec + half);
30
+ const parts = [];
31
+ for (const seg of t.segments) {
32
+ if (seg.end <= startSec || seg.start >= endSec)
33
+ continue;
34
+ const txt = seg.text.trim();
35
+ if (txt)
36
+ parts.push(txt);
37
+ }
38
+ return { atSec, startSec, endSec, text: parts.join(" ").trim() };
39
+ }
40
+ /**
41
+ * Compact transcript outline — every Nth segment trimmed to ~60 chars
42
+ * with a leading [t]s tag. Keeps the LLM grounded in overall pacing
43
+ * without blowing the context budget on a long transcript.
44
+ */
45
+ export function buildOutline(t, maxLines = 40) {
46
+ const segs = t.segments.filter((s) => s.text.trim().length > 0);
47
+ if (segs.length === 0)
48
+ return "";
49
+ const stride = Math.max(1, Math.floor(segs.length / maxLines));
50
+ const lines = [];
51
+ for (let i = 0; i < segs.length; i += stride) {
52
+ const s = segs[i];
53
+ const txt = s.text.trim().slice(0, 80);
54
+ lines.push(`[${s.start.toFixed(1)}s] ${txt}`);
55
+ }
56
+ return lines.join("\n");
57
+ }
58
+ const SYSTEM = `You audit long-form videos for retention structure. Per MrBeast's documented manual, every ~3 minutes a "mini-hook" / re-engagement is required: a twist, reveal, escalation, visual spectacle, or stakes shift. Flat stretches without re-engagement bleed retention.
59
+
60
+ You receive:
61
+ - The CHECKPOINT WINDOWS (one 60s block of transcript text per requested checkpoint).
62
+ - An OUTLINE of the whole transcript for global context.
63
+
64
+ Output a JSON object with these EXACT keys:
65
+ {
66
+ "checkpoints": [
67
+ { "atSec": <number>, "score": <0..1>, "summary": "<≤120 char>", "suggestion": "<≤200 char or empty>" }
68
+ ...one per input checkpoint, in input order...
69
+ ],
70
+ "escalationScore": <0..1>,
71
+ "overallSummary": "<≤200 char>"
72
+ }
73
+
74
+ Scoring rubric:
75
+
76
+ score (per checkpoint):
77
+ 1.0 = clear re-engagement — twist / reveal / shift in stakes / new visual element introduced.
78
+ 0.6 = something shifts but it's understated.
79
+ 0.3 = mostly flat — same energy as the surrounding minutes.
80
+ 0.0 = dead zone — no shift, no escalation, no surprise.
81
+
82
+ escalationScore (whole video):
83
+ 1.0 = each act bigger than the last (stakes, spectacle, payoff escalate).
84
+ 0.5 = some progression but inconsistent.
85
+ 0.0 = flat from start to finish (no build).
86
+
87
+ suggestion (only when score < 0.5):
88
+ Concrete and short, e.g. "Insert a b-roll cutaway here" or "Add a stakes-raising line: 'but then…'" or
89
+ "Punch in + sound effect on the reveal." Empty string when score ≥ 0.5.
90
+
91
+ Be honest — flat stretches must score low even when the content is competent. Sparse-but-real beats dense-but-fake.`;
92
+ /**
93
+ * Run ONE LLM audit pass and return the structured result. Throws on
94
+ * network / parse error so the wrapper can convert to err().
95
+ */
96
+ export async function runRetentionAudit(t, opts = {}) {
97
+ const apiKey = opts.apiKey ?? resolveApiKey("OPENAI_API_KEY", "openai");
98
+ if (!apiKey)
99
+ throw new Error("OPENAI_API_KEY required for audit_retention_structure.");
100
+ const model = opts.model ?? "gpt-4o-mini";
101
+ const totalSec = opts.durationSec ?? t.durationSec;
102
+ const checkpoints = (opts.checkpoints ?? [180, 360]).filter((c) => c > 0 && c < totalSec);
103
+ if (checkpoints.length === 0) {
104
+ return {
105
+ checkpoints: [],
106
+ escalationScore: 0,
107
+ overallSummary: "video too short for any requested checkpoint",
108
+ weakestCheckpoint: -1,
109
+ };
110
+ }
111
+ const windows = checkpoints.map((c) => buildWindow(t, c, totalSec));
112
+ const outline = buildOutline(t);
113
+ const userParts = [];
114
+ userParts.push(`DURATION: ${totalSec.toFixed(1)}s`);
115
+ userParts.push(`OUTLINE:\n${outline}`);
116
+ for (const w of windows) {
117
+ userParts.push(`CHECKPOINT @${w.atSec}s (window ${w.startSec.toFixed(1)}s\u2013${w.endSec.toFixed(1)}s):\n${w.text || "[no speech in window]"}`);
118
+ }
119
+ const body = {
120
+ model,
121
+ messages: [
122
+ { role: "system", content: SYSTEM },
123
+ { role: "user", content: userParts.join("\n\n") },
124
+ ],
125
+ response_format: { type: "json_object" },
126
+ temperature: 0,
127
+ };
128
+ const res = await fetch("https://api.openai.com/v1/chat/completions", {
129
+ method: "POST",
130
+ headers: {
131
+ Authorization: `Bearer ${apiKey}`,
132
+ "Content-Type": "application/json",
133
+ },
134
+ body: JSON.stringify(body),
135
+ signal: opts.signal,
136
+ });
137
+ if (!res.ok) {
138
+ const text = await res.text().catch(() => "");
139
+ throw new Error(`OpenAI HTTP ${res.status}: ${text.slice(0, 200)}`);
140
+ }
141
+ const data = (await res.json());
142
+ const content = data.choices[0]?.message?.content;
143
+ if (!content)
144
+ throw new Error("audit_retention_structure: empty model response");
145
+ return parseAuditResponse(content, checkpoints);
146
+ }
147
+ /**
148
+ * Parse the model's JSON. Robust to missing keys; pads checkpoints to
149
+ * match the requested order so the caller can correlate by index.
150
+ *
151
+ * Pure — exported for testing.
152
+ */
153
+ export function parseAuditResponse(content, requested) {
154
+ const start = content.indexOf("{");
155
+ const end = content.lastIndexOf("}");
156
+ if (start < 0 || end < start) {
157
+ throw new Error("audit_retention_structure: no JSON object in response");
158
+ }
159
+ const parsed = JSON.parse(content.slice(start, end + 1));
160
+ const cpRaw = Array.isArray(parsed.checkpoints) ? parsed.checkpoints : [];
161
+ const byAt = new Map();
162
+ for (const c of cpRaw) {
163
+ if (typeof c !== "object" || c === null)
164
+ continue;
165
+ const r = c;
166
+ const atSec = Number(r.atSec);
167
+ if (!Number.isFinite(atSec))
168
+ continue;
169
+ byAt.set(Math.round(atSec), {
170
+ atSec: Math.round(atSec),
171
+ score: clamp01(Number(r.score)),
172
+ summary: String(r.summary ?? "").slice(0, 200),
173
+ suggestion: String(r.suggestion ?? "").slice(0, 240),
174
+ });
175
+ }
176
+ // Re-order to match the requested checkpoint list. If the LLM dropped
177
+ // one, fill in a zero-score placeholder so the caller still gets N.
178
+ const checkpoints = requested.map((at) => {
179
+ const hit = byAt.get(at);
180
+ if (hit)
181
+ return { ...hit, atSec: at };
182
+ return {
183
+ atSec: at,
184
+ score: 0,
185
+ summary: "no response from model for this checkpoint",
186
+ suggestion: "",
187
+ };
188
+ });
189
+ const escalationScore = clamp01(Number(parsed.escalationScore));
190
+ const overallSummary = String(parsed.overallSummary ?? "").slice(0, 240);
191
+ let weakestCheckpoint = -1;
192
+ let lowest = Infinity;
193
+ for (const c of checkpoints) {
194
+ if (c.score < lowest) {
195
+ lowest = c.score;
196
+ weakestCheckpoint = c.atSec;
197
+ }
198
+ }
199
+ return { checkpoints, escalationScore, overallSummary, weakestCheckpoint };
200
+ }
201
+ function clamp01(n) {
202
+ if (!Number.isFinite(n))
203
+ return 0;
204
+ return Math.max(0, Math.min(1, n));
205
+ }
206
+ //# sourceMappingURL=retention-structure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-structure.js","sourceRoot":"","sources":["../../src/core/retention-structure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=retention-structure.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-structure.test.d.ts","sourceRoot":"","sources":["../../src/core/retention-structure.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,88 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildOutline, buildWindow, parseAuditResponse } from "./retention-structure.js";
3
+ const FIXTURE = {
4
+ language: "en",
5
+ durationSec: 600,
6
+ segments: Array.from({ length: 30 }, (_, i) => ({
7
+ start: i * 20,
8
+ end: (i + 1) * 20,
9
+ text: `segment ${i} content here some words`,
10
+ })),
11
+ };
12
+ describe("buildWindow", () => {
13
+ it("centres a 60s window on the requested timestamp", () => {
14
+ const w = buildWindow(FIXTURE, 180, 600);
15
+ expect(w.atSec).toBe(180);
16
+ expect(w.startSec).toBe(150);
17
+ expect(w.endSec).toBe(210);
18
+ expect(w.text).toContain("segment");
19
+ });
20
+ it("clamps to [0, totalSec]", () => {
21
+ const start = buildWindow(FIXTURE, 10, 600);
22
+ expect(start.startSec).toBe(0);
23
+ const end = buildWindow(FIXTURE, 590, 600);
24
+ expect(end.endSec).toBe(600);
25
+ });
26
+ it("returns empty text when no segments fall inside the window", () => {
27
+ const t = { language: "en", durationSec: 600, segments: [] };
28
+ const w = buildWindow(t, 180, 600);
29
+ expect(w.text).toBe("");
30
+ });
31
+ it("respects custom windowSec", () => {
32
+ const w = buildWindow(FIXTURE, 180, 600, 30);
33
+ expect(w.startSec).toBe(165);
34
+ expect(w.endSec).toBe(195);
35
+ });
36
+ });
37
+ describe("buildOutline", () => {
38
+ it("returns a stride-sampled outline with timestamps", () => {
39
+ const out = buildOutline(FIXTURE, 10);
40
+ expect(out.split("\n").length).toBeGreaterThan(0);
41
+ expect(out.split("\n").length).toBeLessThanOrEqual(15);
42
+ expect(out).toMatch(/\[\d+\.\d+s\]/);
43
+ });
44
+ it("returns empty string when no segments exist", () => {
45
+ const t = { language: "en", durationSec: 0, segments: [] };
46
+ expect(buildOutline(t)).toBe("");
47
+ });
48
+ });
49
+ describe("parseAuditResponse", () => {
50
+ it("matches checkpoints to the requested order", () => {
51
+ const content = JSON.stringify({
52
+ checkpoints: [
53
+ { atSec: 360, score: 0.9, summary: "twist", suggestion: "" },
54
+ { atSec: 180, score: 0.4, summary: "flat", suggestion: "add b-roll" },
55
+ ],
56
+ escalationScore: 0.7,
57
+ overallSummary: "ok",
58
+ });
59
+ const r = parseAuditResponse(content, [180, 360]);
60
+ expect(r.checkpoints[0].atSec).toBe(180);
61
+ expect(r.checkpoints[0].score).toBeCloseTo(0.4);
62
+ expect(r.checkpoints[1].atSec).toBe(360);
63
+ expect(r.weakestCheckpoint).toBe(180);
64
+ });
65
+ it("fills in placeholders when the model drops a checkpoint", () => {
66
+ const content = JSON.stringify({
67
+ checkpoints: [{ atSec: 180, score: 0.5, summary: "ok" }],
68
+ escalationScore: 0.5,
69
+ overallSummary: "",
70
+ });
71
+ const r = parseAuditResponse(content, [180, 360]);
72
+ expect(r.checkpoints).toHaveLength(2);
73
+ expect(r.checkpoints[1].score).toBe(0);
74
+ expect(r.checkpoints[1].summary).toMatch(/no response/);
75
+ });
76
+ it("clamps escalationScore to [0,1]", () => {
77
+ const content = JSON.stringify({
78
+ checkpoints: [],
79
+ escalationScore: 5,
80
+ overallSummary: "",
81
+ });
82
+ expect(parseAuditResponse(content, []).escalationScore).toBe(1);
83
+ });
84
+ it("throws on non-JSON content", () => {
85
+ expect(() => parseAuditResponse("not json", [180])).toThrow();
86
+ });
87
+ });
88
+ //# sourceMappingURL=retention-structure.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-structure.test.js","sourceRoot":"","sources":["../../src/core/retention-structure.test.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,17 @@
1
+ import { type AgentTool } from "@kenkaiiii/gg-agent";
1
2
  import type { Usage } from "@kenkaiiii/gg-ai";
2
3
  import type { VideoHost } from "./hosts/types.js";
4
+ import { type SkillSource } from "./skills-loader.js";
5
+ /**
6
+ * Self-critique pass over the timeline + transcripts.
7
+ *
8
+ * Runs a fresh Agent with a READ-ONLY subset of editor tools and a critique
9
+ * prompt. The reviewer inspects the current state against the user's stated
10
+ * intent and returns a one-paragraph critique + a structured flags array.
11
+ *
12
+ * Token cost: a sub-agent over many turns can burn context; we cap at
13
+ * maxTurns=10 by default.
14
+ */
3
15
  export interface ReviewFlag {
4
16
  severity: "ok" | "warn" | "block";
5
17
  note: string;
@@ -25,6 +37,11 @@ export interface ReviewResult {
25
37
  turns: number;
26
38
  usage: Usage;
27
39
  }
40
+ /**
41
+ * Build only the 6 read-only tools needed by the reviewer, without instantiating
42
+ * the full ~100-tool set. Exported so callers can compose a reviewer independently.
43
+ */
44
+ export declare function createReadOnlyEditorTools(host: VideoHost, cwd: string, skills?: SkillSource[]): AgentTool[];
28
45
  export declare function runReview(opts: ReviewOptions): Promise<ReviewResult>;
29
46
  /**
30
47
  * Parse the trailing ```json {...} ``` block out of the final assistant message.
@@ -1 +1 @@
1
- {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/core/review.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAyBlD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,KAAK,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAC5E,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE;QACN,QAAQ,EAAE,WAAW,GAAG,QAAQ,GAAG,KAAK,GAAG,UAAU,CAAC;QACtD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;CACd;AA6BD,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAmD1E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CA2B1F"}
1
+ {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/core/review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAUlD,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGtE;;;;;;;;;GASG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,KAAK,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAC5E,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE;QACN,QAAQ,EAAE,WAAW,GAAG,QAAQ,GAAG,KAAK,GAAG,UAAU,CAAC;QACtD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;CACd;AA6BD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,WAAW,EAAE,GACrB,SAAS,EAAE,CAWb;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAiD1E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CA2B1F"}
@@ -1,25 +1,7 @@
1
1
  import { Agent } from "@kenkaiiii/gg-agent";
2
- import { createEditorTools } from "../tools/index.js";
3
- /**
4
- * Self-critique pass over the timeline + transcripts.
5
- *
6
- * Runs a fresh Agent with a READ-ONLY subset of editor tools and a critique
7
- * prompt. The reviewer inspects the current state against the user's stated
8
- * intent and returns a one-paragraph critique + a structured flags array.
9
- *
10
- * Token cost: a sub-agent over many turns can burn context; we cap at
11
- * maxTurns=10 by default.
12
- */
13
- /** Tools the reviewer is allowed to call. Strictly read-only. */
14
- const READ_ONLY_TOOL_NAMES = new Set([
15
- "host_info",
16
- "get_timeline",
17
- "get_markers",
18
- "read_transcript",
19
- "score_shot",
20
- "probe_media",
21
- "read_skill",
22
- ]);
2
+ import { createGetMarkersTool, createGetTimelineTool, createHostInfoTool, createProbeMediaTool, createReadSkillTool, createReadTranscriptTool, createScoreShotTool, } from "../tools/index.js";
3
+ import { discoverSkills } from "./skills-loader.js";
4
+ import { SKILLS } from "../skills.js";
23
5
  const REVIEW_SYSTEM = `You are the GG Editor REVIEWER — a read-only critic of an edit-in-progress.
24
6
 
25
7
  Your job: read the timeline + relevant transcripts/media, measure them against the stated INTENT, and write a tight critique.
@@ -46,11 +28,25 @@ severity:
46
28
  - block: ship-stopper (wrong take kept, missing captions on long-form, hook missing)
47
29
 
48
30
  Be direct. Don't praise the work just to be nice. If it's good, say "ok". If it's not, say what's wrong.`;
31
+ /**
32
+ * Build only the 6 read-only tools needed by the reviewer, without instantiating
33
+ * the full ~100-tool set. Exported so callers can compose a reviewer independently.
34
+ */
35
+ export function createReadOnlyEditorTools(host, cwd, skills) {
36
+ const resolvedSkills = skills ?? discoverSkills({ cwd, bundled: Object.values(SKILLS) });
37
+ return [
38
+ createHostInfoTool(host),
39
+ createGetTimelineTool(host),
40
+ createGetMarkersTool(host),
41
+ createReadTranscriptTool(cwd),
42
+ createScoreShotTool(cwd),
43
+ createProbeMediaTool(cwd),
44
+ createReadSkillTool(resolvedSkills),
45
+ ];
46
+ }
49
47
  export async function runReview(opts) {
50
48
  const { intent, focus, host, cwd, config, signal } = opts;
51
- // Build the read-only tool subset by name-allowlist filter.
52
- const allTools = createEditorTools({ host, cwd });
53
- const tools = allTools.filter((t) => READ_ONLY_TOOL_NAMES.has(t.name));
49
+ const tools = createReadOnlyEditorTools(host, cwd);
54
50
  const userPrompt = [
55
51
  `INTENT: ${intent}`,
56
52
  focus && focus.length > 0 ? `FOCUS: ${focus.join(", ")}` : null,