@conceptcraft/mindframes 0.1.38 → 0.1.39

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 (2) hide show
  1. package/dist/index.js +7 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -469,7 +469,7 @@ import { DEFAULT_FPS } from './audio/constants';
469
469
 
470
470
  ### 9c. Spawn Per-Scene Builder Agents
471
471
 
472
- **For EACH scene** from the parsed plan, spawn a \`video-scene-builder\` agent using the Task tool (model: sonnet).
472
+ **For EACH scene** from the parsed plan, spawn a \`video-scene-builder\` agent using the Task tool (model: opus).
473
473
 
474
474
  **Construct the Task prompt for each scene with these sections:**
475
475
 
@@ -2770,7 +2770,7 @@ ${i}`}function Io(n){return n.replace(/\bAskUserQuestion\b/g,"ask the user direc
2770
2770
  name: video-scene-reviewer
2771
2771
  description: Strict QA reviewer for a single Remotion scene. Compares code against the video plan checking ColorGrading, transitions, icons, logos, spring physics, idle states, VO-sync, and phase structure. Returns a pass/fail checklist.
2772
2772
  tools: Read, Glob
2773
- model: haiku
2773
+ model: sonnet
2774
2774
  permissionMode: plan
2775
2775
  ---
2776
2776
 
@@ -2867,7 +2867,7 @@ Priority fixes:
2867
2867
  name: video-scene-builder
2868
2868
  description: Builds a single Remotion video scene (.tsx) from a video plan section. Receives global context, scene spec, audio timing, and rhythm cues. Writes the scene file, spawns a reviewer, and iterates until 85%+ quality. One agent per scene.
2869
2869
  tools: Read, Write, Edit, Glob, Grep, Task, Bash
2870
- model: sonnet
2870
+ model: opus
2871
2871
  skills:
2872
2872
  - frontend-design
2873
2873
  - remotion-best-practices
@@ -3050,7 +3050,7 @@ function AnimatedCamera() {
3050
3050
  name: video-audio-director
3051
3051
  description: Generates all audio assets (voiceover TTS, background music, sound effects) and builds the timing manifest that reconciles planned vs actual durations. One agent per video, runs before scene building.
3052
3052
  tools: Read, Bash, Write
3053
- model: sonnet
3053
+ model: opus
3054
3054
  permissionMode: acceptEdits
3055
3055
  ---
3056
3056
 
@@ -3210,7 +3210,7 @@ This saves money and time \u2014 generation is expensive, downloading is free.
3210
3210
  name: video-continuity-editor
3211
3211
  description: Post-build cross-scene QA. Checks transition continuity, color grade flow, timing alignment between Main.tsx and manifest, import consistency, brand coherence, rhythm flow, and music cue alignment. Runs after all scenes are built.
3212
3212
  tools: Read, Glob, Grep
3213
- model: haiku
3213
+ model: sonnet
3214
3214
  permissionMode: plan
3215
3215
  ---
3216
3216
 
@@ -3321,7 +3321,7 @@ FIXES:
3321
3321
  name: video-branding-director
3322
3322
  description: Extracts brand identity (logos, colors, fonts) from a URL or existing profiles. Searches saved brands, extracts new ones via CLI, downloads assets locally, and writes branding.json. Returns success or failure. One agent per video, runs before brief/plan generation.
3323
3323
  tools: Read, Bash, Write
3324
- model: sonnet
3324
+ model: opus
3325
3325
  permissionMode: acceptEdits
3326
3326
  ---
3327
3327
 
@@ -3742,7 +3742,7 @@ execSync("${e} video generate plan --project .", { stdio: "inherit" });
3742
3742
  const args = process.argv.slice(2).join(" ");
3743
3743
  execSync("${e} video export --project . " + args, { stdio: "inherit" });
3744
3744
  `;await ae(j(r,"plan.ts"),a,"utf-8"),await ae(j(r,"export.ts"),s,"utf-8")}async function Zo(){return process.stdin.isTTY?null:new Promise(n=>{let t="",e=!1;process.stdin.setEncoding("utf-8"),process.stdin.on("data",o=>{t+=o}),process.stdin.on("end",()=>{e||(e=!0,n(t.trim()||null))}),process.stdin.on("error",()=>{e||(e=!0,n(null))}),setTimeout(()=>{!e&&!t&&(e=!0,n(null))},500)})}function fa(n){return n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ga(n){let t={shortTitle:"Video",elements:[],audio:[],text:[],scenes:[]},e=!0;return n.forEach(o=>{let r=o.startTime*1e3,i=o.endTime*1e3,a=o.durationInSeconds*1e3;t.scenes.push({name:o.name||`Scene ${o.id}`,text:o.text,startMs:r,endMs:i}),o.imagePath?(t.elements.push({startMs:r,endMs:i,imageUrl:o.imagePath,enterTransition:"blur",exitTransition:"blur",animations:[{type:"scale",from:e?1.3:1,to:e?1:1.3,startMs:0,endMs:a}]}),e=!e):o.videoPath&&t.elements.push({startMs:r,endMs:i,videoUrl:o.videoPath,enterTransition:"blur",exitTransition:"blur",animations:[]}),o.audioPath&&t.audio.push({startMs:r,endMs:i,audioUrl:o.audioPath}),o.timestamps&&t.text.push({startMs:r,endMs:i,text:o.text,position:"bottom",animations:[],timestamps:o.timestamps})}),t}async function It(n,t){if(n.startsWith("data:")){let r=n.match(/^data:[^;]+;base64,(.+)$/);if(!r)throw new Error("Invalid data URL format");let i=Buffer.from(r[1],"base64");await ae(t,i);return}let e=await fetch(n);if(!e.ok)throw new Error(`Failed to download: ${e.status}`);let o=await e.arrayBuffer();await ae(t,Buffer.from(o))}function ha(n){try{let o=new URL(n).pathname.split(".").pop()?.toLowerCase();if(o&&["jpg","jpeg","png","gif","webp"].includes(o))return o}catch{}return"jpg"}var ya=new se("assets").description("Generate assets (voiceover per scene, music, images, video)").option("-s, --script <text>","Narration script (legacy single-script mode)").option("--script-file <path>","Path to script file (legacy) or scenes JSON").option("--scenes <path>","Path to scenes.json (defaults to <project>/public/scenes.json)").option("-p, --project <dir>","Project directory (defaults to current directory; uses <dir>/public for output)").option("-t, --topic <text>","Topic for image search").option("-v, --voice <name>","TTS voice ID (ElevenLabs: Rachel, Josh, Adam; OpenAI: alloy, nova; Gemini: Kore, Puck)").option("-m, --music-prompt <text>","Music description").option("-n, --num-images <number>","Number of images to search/download","5").option("-o, --output <dir>","Output directory","./public").option("-f, --format <format>","Output format: human, json, quiet","human").action(async n=>{let t=n.format,e=t==="human"?Ze("Initializing...").start():null,o=n.project?Qe(process.cwd(),n.project):null;try{let r=await Zo(),i=null;if(n.scenes&&!n.scriptFile&&(n.scriptFile=n.scenes),o&&(n.output=j(o,"public"),n.scriptFile?n.scriptFile=Qe(o,n.scriptFile):!n.script&&!r&&(n.scriptFile=j(o,"public","scenes.json"))),r)try{let b=JSON.parse(r);b.scenes&&Array.isArray(b.scenes)&&(i=b)}catch{}if(!i&&n.scriptFile)try{let b=await Rt(n.scriptFile,"utf-8"),q=JSON.parse(b);q.scenes&&Array.isArray(q.scenes)&&(i=q)}catch{}let a=i?.voice||n.voice,s=i?.voiceId,c=i?.provider,d=i?.model,m=i?.voiceInstructions,C=i?.musicPrompt||n.musicPrompt||"uplifting background music, positive energy",h=i?.musicDuration,f=j(n.output,"audio"),w=j(n.output,"images"),$=j(n.output,"videos");e&&(e.text="Creating directories..."),await Be(f,{recursive:!0}),await Be(w,{recursive:!0}),await Be($,{recursive:!0});let k=0,y=[],R=0,T=[],g=[];if(i&&i.scenes.length>0){t==="human"&&(e?.stop(),p(`Processing ${i.scenes.length} scenes...`),e?.start());let b=i.scenes.map((X,L)=>{let re=X.script;return X.voiceStyle&&(re=`${X.voiceStyle} ${re}`),{text:re,id:`scene-${L}`}});e&&(e.text="Generating speech for all scenes...");let q=await Wn({texts:b,options:{provider:c,voice:a,voiceId:s,model:d,voiceSettings:i.voiceSettings,instructions:m}});k+=q.totalCost;let K=0;for(let X=0;X<i.scenes.length;X++){let L=i.scenes[X],re=fa(L.name),Z=q.results[X];e&&(e.text=`[${L.name}] Saving audio...`);let Et=j(f,`${re}.${Z.format}`);await ae(Et,Z.audioData);let we=Z.duration,cr=Math.round(we*_e),We={id:X+1,name:L.name,text:L.script,wordCount:L.script.split(/\s+/).length,startTime:K,endTime:K+we,durationInSeconds:we,durationInFrames:cr,audioPath:`audio/${re}.${Z.format}`,timestamps:Z.timestamps};if(L.imageQuery){e&&(e.text=`[${L.name}] Searching image...`);try{let W=await dt({query:L.imageQuery,options:{maxResults:1,size:"large",safeSearch:!0}}),Ye=W.data.results.flatMap(G=>G.results);if(k+=W.data.totalCost,Ye.length>0){let G=Ye[0],He=ha(G.url),me=`${re}.${He}`,Pt=j(w,me);await It(G.url,Pt),We.imagePath=`images/${me}`,T.push({path:`images/${me}`,url:G.url,width:G.width,height:G.height,query:L.imageQuery})}}catch(W){t==="human"&&(e?.stop(),I(`[${L.name}] Image search failed: ${W instanceof Error?W.message:"Unknown"}`),e?.start())}}if(L.videoQuery){e&&(e.text=`[${L.name}] Searching video...`);try{let W=await _t({query:L.videoQuery,options:{maxResults:1,license:"free"}}),Ye=W.data.results.flatMap(G=>G.results);if(k+=W.data.totalCost,Ye.length>0){let G=Ye[0],He=G.previewUrl||G.downloadUrl;if(He){let me=`${re}.mp4`,Pt=j($,me);await It(He,Pt),We.videoPath=`videos/${me}`,g.push({path:`videos/${me}`,url:He,width:G.width,height:G.height,duration:G.duration,query:L.videoQuery})}}}catch(W){t==="human"&&(e?.stop(),I(`[${L.name}] Video search failed: ${W instanceof Error?W.message:"Unknown"}`),e?.start())}}if(y.push(We),K+=we,R+=we,t==="human"){e?.stop();let W=[`audio: ${we.toFixed(1)}s`,We.imagePath?"image":null,We.videoPath?"video":null].filter(Boolean).join(", ");v(` ${L.name}: ${W}`),e?.start()}}}else{let b=n.script;if(n.scriptFile)try{b=await Rt(n.scriptFile,"utf-8")}catch(Z){e?.stop(),l(`Failed to read script file: ${Z instanceof Error?Z.message:"Unknown error"}`),process.exit(S.INVALID_INPUT)}(!b||b.trim().length===0)&&(e?.stop(),l("Provide scenes via stdin JSON, --scenes/--script-file with scenes JSON, or --script for legacy mode"),process.exit(S.INVALID_INPUT)),b=b.trim();let q=n.topic||b.split(".")[0].slice(0,50);e&&(e.text="Generating voiceover...");let K=await ct({text:b,options:{voice:a}}),X=j(f,`voiceover.${K.format}`);await ae(X,K.audioData),k+=K.cost,R=K.duration;let L=la(b);y=da(L,K.duration,_e,K.timestamps).map((Z,Et)=>({...Z,name:`Section${Et+1}`,audioPath:`audio/voiceover.${K.format}`})),t==="human"&&(e?.stop(),v(`Voiceover: ${X} (${K.duration.toFixed(1)}s)`),e?.start())}e&&(e.text="Creating timeline...");let A=ga(y),B=Math.max(A.audio.length>0?Math.max(...A.audio.map(b=>b.endMs)):0,A.text.length>0?Math.max(...A.text.map(b=>b.endMs)):0,A.elements.length>0?Math.max(...A.elements.map(b=>b.endMs)):0),z=B/1e3,oe=h?Math.min(300,Math.max(3,h)):Math.min(300,Math.ceil(z));console.log("[Music Generation] Requesting music:",{prompt:C,requestedDuration:oe,totalAudioDuration:R,actualVideoDuration:z,timelineDurationMs:B});let ve,Ge;e&&(e.text="Generating music and thumbnail...");let ir=`YouTube thumbnail style: BIG BOLD short headline text (like "$50K/MONTH"), dramatic engaging scene, arrows pointing to key elements, high contrast, vibrant colors, clean composition, 16:9. Video context: ${y.map(b=>b.text).join(" ")}`,At=[],ar=(async()=>{if(oe<3)return t==="human"&&(e?.stop(),I(`Video duration (${z.toFixed(1)}s) is too short for music generation (minimum 3s).`),e?.start()),null;try{let b=await lt({prompt:C,duration:oe});if(b.status!=="completed"&&b.status!=="failed"&&(b=await de(()=>ze(b.requestId),60,2e3)),b.status==="failed")throw new Error(b.error||"Unknown error");let q=j(f,"music.mp3");return b.audioUrl&&await It(b.audioUrl,q),{path:"audio/music.mp3",duration:b.duration||oe,prompt:C,cost:b.cost||0}}catch(b){return t==="human"&&(e?.stop(),I(`Music generation failed: ${b.message}`),e?.start()),null}})();At.push(ar);let sr=(async()=>{try{let b=await Jn({prompt:ir,options:{width:1280,height:720}});if(b.success&&b.data.url){let q=j(n.output,"thumbnail.jpg");return await It(b.data.url,q),k+=b.data.cost||0,q}return null}catch(b){return t==="human"&&(e?.stop(),I(`Thumbnail generation failed: ${b.message}`),e?.start()),null}})();At.push(sr);let[xe,hn]=await Promise.all(At);xe&&(ve=xe,k+=xe.cost||0,t==="human"&&(e?.stop(),v(`Music: ${j(f,"music.mp3")} (${xe.duration}s)`),xe.duration<z&&I(`Music duration (${xe.duration.toFixed(1)}s) is shorter than video duration (${z.toFixed(1)}s).`),e?.start())),hn&&(Ge=hn,t==="human"&&(e?.stop(),v(`Thumbnail: ${Ge}`),e?.start())),e&&(e.text="Writing manifest...");let yn=Math.round(z*_e),bn={music:ve,thumbnail:Ge?"thumbnail.jpg":void 0,images:T,videos:g,scenes:y,timeline:A,totalDurationInFrames:yn,fps:_e,totalCost:k,createdAt:new Date().toISOString()},Ot=j(n.output,"video-manifest.json");if(await ae(Ot,JSON.stringify(bn,null,2)),e?.stop(),t==="json"){let{totalCost:b,...q}=bn;V(q);return}if(t==="quiet"){console.log(Ot);return}console.log(),v("Video assets created successfully!"),console.log(),p(`Scenes: ${y.length} (${yn} frames at ${_e}fps)`);for(let b of y){let q=[b.audioPath?"audio":null,b.imagePath?"image":null,b.videoPath?"video":null].filter(Boolean).join(", ");p(` - ${b.name}: ${b.durationInSeconds.toFixed(1)}s [${q}]`)}p(`Music: ${ve?.path} (${ve?.duration}s)`),p(`Manifest: ${Ot}`),console.log(),p(`Total cost: $${k.toFixed(4)}`)}catch(r){e?.stop(),l(r instanceof Error?r.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),ba=new se("find").description("Find stock video clips (supporting)").argument("<query>","Search query").option("-n, --max-results <count>","Maximum number of results","10").option("-o, --orientation <type>","Video orientation: landscape, portrait, square, any","any").option("-l, --license <type>","License type: free, premium, any","free").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let{maxResults:e,orientation:o,license:r,format:i}=t,a=i==="human"?Ze("Finding videos...").start():null;try{let s=await _t({query:n,options:{maxResults:parseInt(e,10),orientation:o,license:r}});a?.stop();let c=s.data.results.flatMap(d=>d.results);if(i==="json"){let{totalCost:d,...m}=s.data;V({...s,data:m});return}if(i==="quiet"){c.forEach(d=>{console.log(d.previewUrl||d.thumbnailUrl)});return}if(c.length===0){p("No videos found");return}v(`Found ${c.length} videos for "${n}"`),console.log(),c.forEach((d,m)=>{console.log(`[${m+1}] ${d.title}`),console.log(` URL: ${d.previewUrl||d.thumbnailUrl}`),console.log(` Duration: ${d.duration}s | Size: ${d.width}x${d.height}`),console.log(` Provider: ${d.provider}`),console.log()}),p(`Total cost: $${s.data.totalCost.toFixed(4)}`)}catch(s){a?.stop(),l(s instanceof Error?s.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),va=new se("new").description("Create a new video project from a template").argument("<name>","Project directory name").option("-t, --template <repo>","GitHub repo (user/repo#commit)",aa).option("--type <type>","Video type: youtube (16:9), tiktok (9:16), or audiogram (1:1)","youtube").option("--no-install","Skip dependency install").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format,o=e==="human"?Ze("Initializing video project...").start():null;try{let r=je();await Be(r,{recursive:!0});let i=Ct(n),a=t.type,s=await dn();if(s.includes(n)){let f=Ho(n,s);o?.stop(),l(`Project "${n}" already exists at: ${i}`),p(`Try: ${u.commands[0]} video generate new ${f}`),process.exit(S.INVALID_INPUT)}/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+(#[a-zA-Z0-9_.\/-]+)?$/.test(t.template)||(o?.stop(),l(`Invalid template format: "${t.template}". Expected format: owner/repo or owner/repo#branch`),process.exit(S.INVALID_INPUT)),["youtube","tiktok","audiogram"].includes(a)||(o?.stop(),l(`Invalid type "${a}". Use youtube, tiktok, or audiogram.`),process.exit(S.INVALID_INPUT)),o&&(o.text=`Cloning template from ${t.template}...`);let[d,m]=t.template.split("#"),C=m?`-b ${m}`:"";try{qe(`git clone ${C} --depth 1 https://github.com/${d}.git "${i}"`,{stdio:"pipe"}),await un(j(i,".git"),{recursive:!0,force:!0})}catch(f){o?.stop(),l(`Failed to clone template: ${f instanceof Error?f.message:"Unknown error"}`),process.exit(S.GENERAL_ERROR)}e==="human"&&(o?.stop(),v(`Template downloaded to ${n}/`),o?.start());let h;try{h=await ca(i)}catch{o?.stop(),l("template.manifest.json not found or invalid in template repo"),process.exit(S.INVALID_INPUT)}if(h.propsSchema&&h.propsSchema!=="video-plan.v1"&&(o?.stop(),l(`Template propsSchema must be video-plan.v1 (found ${h.propsSchema})`),process.exit(S.INVALID_INPUT)),o&&(o.text="Writing scaffold files..."),await pa(i,a,u.commands[0]),t.install){let f=Qo()?"bun":"npm";o&&(o.text=`Installing dependencies with ${f}...`),await new Promise((w,$)=>{let k=oa(f,["install"],{cwd:i,stdio:"pipe",shell:!0});k.on("close",y=>{y===0?w():$(new Error(`${f} install failed with code ${y}`))}),k.on("error",$)}),e==="human"&&(o?.stop(),v("Dependencies installed"),o?.start())}if(o?.stop(),e==="json"){V({name:n,path:i,template:t.template,type:a,installed:t.install});return}if(e==="quiet"){console.log(i);return}console.log(),v(`Video project "${n}" created successfully!`),p(`Location: ${i}`),a==="tiktok"?p("Format: TikTok/Reels/Shorts (1080x1920 @ 30fps)"):a==="audiogram"?p("Format: Audiogram (1080x1080 @ 30fps)"):p("Format: YouTube (1920x1080 @ 30fps)"),console.log(),p("Next steps:"),p(` cd ${i}`),t.install||p(` ${Qo()?"bun":"npm"} install`),p(" bun run dev # Preview in Remotion Studio"),p(` ${u.commands[0]} video generate assets --scenes public/scenes.json`),p(` ${u.commands[0]} video generate plan`)}catch(r){o?.stop(),l(r instanceof Error?r.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),xa=new se("plan").description("Generate video plan from context (via stdin or --context)").option("-c, --context <text>","Context for plan generation").option("-o, --output <path>","Save plan to file").option("-b, --branding <path>","Branding JSON file to include").action(async n=>{let t=Ze("Generating video plan...").start();try{let e=n.context;if(!e){let a=await Zo();a&&(e=a)}e||(t.stop(),l("Provide context via stdin or --context flag"),process.exit(S.INVALID_INPUT));let o;if(n.branding)try{o=await Rt(n.branding,"utf-8")}catch{t.stop(),l(`Could not read branding file: ${n.branding}`),process.exit(S.INVALID_INPUT)}let r=await Xn({context:e,branding:o});t.stop(),r.success||(l(r.error||"Failed to generate plan"),process.exit(S.GENERAL_ERROR));let i=r.data;n.output?(await ae(n.output,i),v(`Plan saved to ${n.output}`)):console.log(i)}catch(e){t.stop(),l(e instanceof Error?e.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),wa=new se("generate").description("Generate parts of a video project").addCommand(va).addCommand(ya).addCommand(xa),Sa=new se("projects").description("List all video projects").option("-f, --format <format>","Output format: human, json","human").action(async n=>{let t=await dn();if(n.format==="json"){V({projectsDir:je(),projects:t.map(e=>({name:e,path:Ct(e)}))});return}if(t.length===0){p("No video projects found"),p(`Projects are stored in: ${je()}`);return}console.log(`
3745
- Video Projects:`),console.log("-".repeat(50));for(let e of t)console.log(` ${e}`),console.log(` ${Ct(e)}`);console.log()}),Ca=new se("inject").description("Inject thumbnail into video (first frame + file icon)").requiredOption("-i, --video <path>","Input video file").requiredOption("-t, --thumbnail <path>","Thumbnail image").option("-o, --output <path>","Output video (default: video-with-thumb.mp4)").option("-f, --format <format>","Output format: human, json, quiet","human").action(async n=>{let t=n.format,e=t==="human"?Ze("Injecting thumbnail...").start():null;try{let o=Qe(n.video),r=Qe(n.thumbnail),i=n.output?Qe(n.output):o.replace(/\.mp4$/,"-with-thumb.mp4");await mn(o),await mn(r);let a=ia;if(a)try{await mn(a)}catch{a=null}if(!a)try{a=qe("which ffmpeg",{encoding:"utf8"}).trim()}catch{throw new Error("ffmpeg not found. Install ffmpeg or ffmpeg-static.")}let s=ra(a,["-i",o],{encoding:"utf8"});if(s.error)throw new Error(`Failed to probe video: ${s.error.message}`);let c=(s.stderr??"").match(/(\d{2,5})x(\d{2,5})/);if(!c)throw new Error("Could not detect video dimensions");let[,d,m]=c;e&&(e.text="Creating thumbnail frame...");let C="/tmp/cc-thumb-frame.mp4";qe(`${a} -y -loop 1 -i "${r}" -vf "scale=${d}:${m}:force_original_aspect_ratio=decrease,pad=${d}:${m}:(ow-iw)/2:(oh-ih)/2,setsar=1" -t 0.033 -r 30 -c:v libx264 -pix_fmt yuv420p "${C}"`,{stdio:"pipe"}),e&&(e.text="Concatenating with video...");let h="/tmp/cc-concat-video.mp4";if(qe(`${a} -y -i "${C}" -i "${o}" -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" -map "[v]" -map 1:a -c:v libx264 -preset ultrafast -crf 18 -c:a copy "${h}"`,{stdio:"pipe"}),e&&(e.text="Adding file icon thumbnail..."),qe(`${a} -y -i "${h}" -i "${r}" -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic "${i}"`,{stdio:"pipe"}),await un(C,{force:!0}),await un(h,{force:!0}),e?.stop(),t==="json"){V({success:!0,output:i});return}if(t==="quiet"){console.log(i);return}v(`Thumbnail injected: ${i}`)}catch(o){e?.stop(),l(o instanceof Error?o.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),ka=new se("thumbnail").description("Thumbnail operations").addCommand(Ca),er=new se("video").description("Video generation commands").addCommand(wa).addCommand(ba).addCommand(Sa).addCommand(ka).addCommand(Xo);import{Command as Ta}from"commander";import $a from"ora";import{writeFile as Ia}from"fs/promises";var tr=new Ta("scrape").description("Extract content from a URL").argument("<url>","URL to scrape").option("-o, --output <path>","Save content to file").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format,o=e==="human"?$a("Scraping URL...").start():null;try{let r=await zn(n);o?.stop(),r.success||(l(r.error||"Failed to scrape URL"),process.exit(S.GENERAL_ERROR));let i=r.data;if(t.output&&(await Ia(t.output,i.content,"utf-8"),e==="human"&&v(`Content saved to: ${t.output}`)),e==="json"){let{cost:a,...s}=i;V(s);return}if(e==="quiet"){console.log(i.content);return}i.title&&(console.log(),console.log(`Title: ${i.title}`)),i.metadata?.description&&console.log(`Description: ${i.metadata.description}`),console.log(`URL: ${i.url}`),console.log(`Tokens: ~${i.metadata?.tokenUsage?.toLocaleString()||"unknown"}`),i.warning&&I(i.warning),t.output||(console.log(),console.log("--- Content ---"),console.log(i.content)),i.cost&&i.cost>0&&p(`Cost: $${i.cost.toFixed(6)}`)}catch(r){o?.stop(),l(r instanceof Error?r.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}});import{Command as gn}from"commander";import pn from"ora";import{writeFile as nr,mkdir as Ra}from"fs/promises";import{dirname as Aa}from"path";function fn(n,t){if(t==="json"){let{cost:e,...o}=n;V(o);return}if(t==="quiet"){n.audioUrl?console.log(n.audioUrl):n.requestId?console.log(n.requestId):console.log(n.id);return}p(`Request ID: ${n.requestId||n.id}`),p(`Status: ${n.status}`),n.duration&&p(`Duration: ${n.duration}s`),n.audioUrl&&v(`Audio URL: ${n.audioUrl}`),n.cost!==void 0&&p(`Cost: $${n.cost.toFixed(4)}`),n.error&&l(`Error: ${n.error}`)}async function Oa(n,t){if(await Ra(Aa(t),{recursive:!0}),n.startsWith("data:")){let r=n.match(/^data:[^;]+;base64,(.+)$/);if(!r)throw new Error("Invalid data URL format");let i=Buffer.from(r[1],"base64");await nr(t,i);return}let e=await fetch(n);if(!e.ok)throw new Error(`Failed to download: ${e.status}`);let o=await e.arrayBuffer();await nr(t,Buffer.from(o))}var Ea=new gn("generate").description("Generate sound effects from a text prompt").requiredOption("-t, --text <text>","Sound effect description (e.g., 'thunder rumbling, rain on window')").option("-d, --duration <seconds>","Duration in seconds (0.5-30)").option("-p, --provider <provider>","Provider (elevenlabs)","elevenlabs").option("--prompt-influence <value>","How closely to follow prompt (0-1)","0.3").option("-l, --loop","Generate seamless looping audio",!1).option("--format <format>","Audio format: mp3, wav, ogg","mp3").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --output-format <format>","Output format: human, json, quiet","human").action(async n=>{let t;n.duration&&(t=parseFloat(n.duration),(isNaN(t)||t<.5||t>30)&&(l("Duration must be between 0.5 and 30 seconds"),process.exit(S.INVALID_INPUT)));let e;n.promptInfluence&&(e=parseFloat(n.promptInfluence),(isNaN(e)||e<0||e>1)&&(l("Prompt influence must be between 0 and 1"),process.exit(S.INVALID_INPUT)));let o=n.outputFormat,r=o==="human"?pn("Generating sound effects...").start():null;try{let i=await Kn({text:n.text,duration:t,options:{provider:n.provider,promptInfluence:e,loop:n.loop,format:n.format}});if(!n.wait){r?.stop(),fn(i,o);return}let a=i;if(i.status!=="completed"&&i.status!=="failed"){let s=i.requestId||i.id;r&&(r.text=`Processing (ID: ${s})...`),a=await de(()=>qt(s),60,2e3)}if(r?.stop(),a.status==="failed"&&(l(a.error||"Sound effects generation failed"),process.exit(S.GENERAL_ERROR)),fn(a,o),n.output&&a.audioUrl){let s=o==="human"?pn("Downloading...").start():null;try{await Oa(a.audioUrl,n.output),s?.stop(),o==="human"&&v(`Saved to: ${n.output}`)}catch(c){s?.stop(),I(`Failed to download: ${c instanceof Error?c.message:"Unknown error"}`)}}}catch(i){r?.stop(),l(i instanceof Error?i.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),Pa=new gn("status").description("Check status of a sound effects generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format==="human"?pn("Checking status...").start():null;try{let o=await qt(n);e?.stop(),fn(o,t.format)}catch(o){e?.stop(),l(o instanceof Error?o.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),or=new gn("sfx").description("Sound effects generation commands").addCommand(Ea).addCommand(Pa);var Da="0.1.38",N=new Fa,ce=u.commands[0];N.name(ce).description(u.description).version(Da,"-v, --version","Show version number").option("--debug","Enable debug logging").option("--no-color","Disable colored output").configureOutput({outputError:(n,t)=>{t(Q.red(n))}});N.addCommand(io);N.addCommand(ao);N.addCommand(co);N.addCommand(po);N.addCommand(fo);N.addCommand(go);N.addCommand(ho);N.addCommand(bo);N.addCommand(xo);N.addCommand(So);N.addCommand(To);N.addCommand($o);N.addCommand(Me);N.addCommand(Vo);N.addCommand(qo);N.addCommand(Bo);N.addCommand(Wo);N.addCommand(er);N.addCommand(tr);N.addCommand(or);var rr=ko();rr.commands.length>0&&N.addCommand(rr);N.on("command:*",n=>{console.error(Q.red(`Error: Unknown command '${n[0]}'`)),console.error(),console.error(`Run '${ce} --help' to see available commands.`),process.exit(1)});N.addHelpText("after",`
3745
+ Video Projects:`),console.log("-".repeat(50));for(let e of t)console.log(` ${e}`),console.log(` ${Ct(e)}`);console.log()}),Ca=new se("inject").description("Inject thumbnail into video (first frame + file icon)").requiredOption("-i, --video <path>","Input video file").requiredOption("-t, --thumbnail <path>","Thumbnail image").option("-o, --output <path>","Output video (default: video-with-thumb.mp4)").option("-f, --format <format>","Output format: human, json, quiet","human").action(async n=>{let t=n.format,e=t==="human"?Ze("Injecting thumbnail...").start():null;try{let o=Qe(n.video),r=Qe(n.thumbnail),i=n.output?Qe(n.output):o.replace(/\.mp4$/,"-with-thumb.mp4");await mn(o),await mn(r);let a=ia;if(a)try{await mn(a)}catch{a=null}if(!a)try{a=qe("which ffmpeg",{encoding:"utf8"}).trim()}catch{throw new Error("ffmpeg not found. Install ffmpeg or ffmpeg-static.")}let s=ra(a,["-i",o],{encoding:"utf8"});if(s.error)throw new Error(`Failed to probe video: ${s.error.message}`);let c=(s.stderr??"").match(/(\d{2,5})x(\d{2,5})/);if(!c)throw new Error("Could not detect video dimensions");let[,d,m]=c;e&&(e.text="Creating thumbnail frame...");let C="/tmp/cc-thumb-frame.mp4";qe(`${a} -y -loop 1 -i "${r}" -vf "scale=${d}:${m}:force_original_aspect_ratio=decrease,pad=${d}:${m}:(ow-iw)/2:(oh-ih)/2,setsar=1" -t 0.033 -r 30 -c:v libx264 -pix_fmt yuv420p "${C}"`,{stdio:"pipe"}),e&&(e.text="Concatenating with video...");let h="/tmp/cc-concat-video.mp4";if(qe(`${a} -y -i "${C}" -i "${o}" -filter_complex "[0:v][1:v]concat=n=2:v=1:a=0[v]" -map "[v]" -map 1:a -c:v libx264 -preset ultrafast -crf 18 -c:a copy "${h}"`,{stdio:"pipe"}),e&&(e.text="Adding file icon thumbnail..."),qe(`${a} -y -i "${h}" -i "${r}" -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic "${i}"`,{stdio:"pipe"}),await un(C,{force:!0}),await un(h,{force:!0}),e?.stop(),t==="json"){V({success:!0,output:i});return}if(t==="quiet"){console.log(i);return}v(`Thumbnail injected: ${i}`)}catch(o){e?.stop(),l(o instanceof Error?o.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),ka=new se("thumbnail").description("Thumbnail operations").addCommand(Ca),er=new se("video").description("Video generation commands").addCommand(wa).addCommand(ba).addCommand(Sa).addCommand(ka).addCommand(Xo);import{Command as Ta}from"commander";import $a from"ora";import{writeFile as Ia}from"fs/promises";var tr=new Ta("scrape").description("Extract content from a URL").argument("<url>","URL to scrape").option("-o, --output <path>","Save content to file").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format,o=e==="human"?$a("Scraping URL...").start():null;try{let r=await zn(n);o?.stop(),r.success||(l(r.error||"Failed to scrape URL"),process.exit(S.GENERAL_ERROR));let i=r.data;if(t.output&&(await Ia(t.output,i.content,"utf-8"),e==="human"&&v(`Content saved to: ${t.output}`)),e==="json"){let{cost:a,...s}=i;V(s);return}if(e==="quiet"){console.log(i.content);return}i.title&&(console.log(),console.log(`Title: ${i.title}`)),i.metadata?.description&&console.log(`Description: ${i.metadata.description}`),console.log(`URL: ${i.url}`),console.log(`Tokens: ~${i.metadata?.tokenUsage?.toLocaleString()||"unknown"}`),i.warning&&I(i.warning),t.output||(console.log(),console.log("--- Content ---"),console.log(i.content)),i.cost&&i.cost>0&&p(`Cost: $${i.cost.toFixed(6)}`)}catch(r){o?.stop(),l(r instanceof Error?r.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}});import{Command as gn}from"commander";import pn from"ora";import{writeFile as nr,mkdir as Ra}from"fs/promises";import{dirname as Aa}from"path";function fn(n,t){if(t==="json"){let{cost:e,...o}=n;V(o);return}if(t==="quiet"){n.audioUrl?console.log(n.audioUrl):n.requestId?console.log(n.requestId):console.log(n.id);return}p(`Request ID: ${n.requestId||n.id}`),p(`Status: ${n.status}`),n.duration&&p(`Duration: ${n.duration}s`),n.audioUrl&&v(`Audio URL: ${n.audioUrl}`),n.cost!==void 0&&p(`Cost: $${n.cost.toFixed(4)}`),n.error&&l(`Error: ${n.error}`)}async function Oa(n,t){if(await Ra(Aa(t),{recursive:!0}),n.startsWith("data:")){let r=n.match(/^data:[^;]+;base64,(.+)$/);if(!r)throw new Error("Invalid data URL format");let i=Buffer.from(r[1],"base64");await nr(t,i);return}let e=await fetch(n);if(!e.ok)throw new Error(`Failed to download: ${e.status}`);let o=await e.arrayBuffer();await nr(t,Buffer.from(o))}var Ea=new gn("generate").description("Generate sound effects from a text prompt").requiredOption("-t, --text <text>","Sound effect description (e.g., 'thunder rumbling, rain on window')").option("-d, --duration <seconds>","Duration in seconds (0.5-30)").option("-p, --provider <provider>","Provider (elevenlabs)","elevenlabs").option("--prompt-influence <value>","How closely to follow prompt (0-1)","0.3").option("-l, --loop","Generate seamless looping audio",!1).option("--format <format>","Audio format: mp3, wav, ogg","mp3").option("-o, --output <path>","Output file path").option("--no-wait","Do not wait for completion").option("-f, --output-format <format>","Output format: human, json, quiet","human").action(async n=>{let t;n.duration&&(t=parseFloat(n.duration),(isNaN(t)||t<.5||t>30)&&(l("Duration must be between 0.5 and 30 seconds"),process.exit(S.INVALID_INPUT)));let e;n.promptInfluence&&(e=parseFloat(n.promptInfluence),(isNaN(e)||e<0||e>1)&&(l("Prompt influence must be between 0 and 1"),process.exit(S.INVALID_INPUT)));let o=n.outputFormat,r=o==="human"?pn("Generating sound effects...").start():null;try{let i=await Kn({text:n.text,duration:t,options:{provider:n.provider,promptInfluence:e,loop:n.loop,format:n.format}});if(!n.wait){r?.stop(),fn(i,o);return}let a=i;if(i.status!=="completed"&&i.status!=="failed"){let s=i.requestId||i.id;r&&(r.text=`Processing (ID: ${s})...`),a=await de(()=>qt(s),60,2e3)}if(r?.stop(),a.status==="failed"&&(l(a.error||"Sound effects generation failed"),process.exit(S.GENERAL_ERROR)),fn(a,o),n.output&&a.audioUrl){let s=o==="human"?pn("Downloading...").start():null;try{await Oa(a.audioUrl,n.output),s?.stop(),o==="human"&&v(`Saved to: ${n.output}`)}catch(c){s?.stop(),I(`Failed to download: ${c instanceof Error?c.message:"Unknown error"}`)}}}catch(i){r?.stop(),l(i instanceof Error?i.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),Pa=new gn("status").description("Check status of a sound effects generation request").argument("<id>","Request ID").option("-f, --format <format>","Output format: human, json, quiet","human").action(async(n,t)=>{let e=t.format==="human"?pn("Checking status...").start():null;try{let o=await qt(n);e?.stop(),fn(o,t.format)}catch(o){e?.stop(),l(o instanceof Error?o.message:"Unknown error"),process.exit(S.GENERAL_ERROR)}}),or=new gn("sfx").description("Sound effects generation commands").addCommand(Ea).addCommand(Pa);var Da="0.1.39",N=new Fa,ce=u.commands[0];N.name(ce).description(u.description).version(Da,"-v, --version","Show version number").option("--debug","Enable debug logging").option("--no-color","Disable colored output").configureOutput({outputError:(n,t)=>{t(Q.red(n))}});N.addCommand(io);N.addCommand(ao);N.addCommand(co);N.addCommand(po);N.addCommand(fo);N.addCommand(go);N.addCommand(ho);N.addCommand(bo);N.addCommand(xo);N.addCommand(So);N.addCommand(To);N.addCommand($o);N.addCommand(Me);N.addCommand(Vo);N.addCommand(qo);N.addCommand(Bo);N.addCommand(Wo);N.addCommand(er);N.addCommand(tr);N.addCommand(or);var rr=ko();rr.commands.length>0&&N.addCommand(rr);N.on("command:*",n=>{console.error(Q.red(`Error: Unknown command '${n[0]}'`)),console.error(),console.error(`Run '${ce} --help' to see available commands.`),process.exit(1)});N.addHelpText("after",`
3746
3746
  ${Q.bold("Examples:")}
3747
3747
  ${Q.gray("# Log in (opens browser)")}
3748
3748
  $ ${ce} login
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conceptcraft/mindframes",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "description": "CLI tool for creating AI-powered presentations with Mindframes",
5
5
  "type": "module",
6
6
  "bin": {