@conceptcraft/mindframes 0.3.0 → 0.3.1

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 +2 -2
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -57,7 +57,7 @@ ${e}`:e:""}var La=new Set(["minimal","low","medium","high","xhigh"]);function Ua
57
57
  `),usage:a}}import{randomUUID as qa}from"crypto";import{mkdir as Ba}from"fs/promises";import{hostname as fn,platform as Ga}from"os";import{join as Wa}from"path";var Ee=class extends Error{constructor(e,o,r=!1){super(e);this.statusCode=o;this.reRegister=r;this.name="RunnerApiError"}statusCode;reRegister};async function Oe(n,t={}){let e=await ge();t.body!==void 0&&(e["Content-Type"]="application/json");let o=new AbortController,r=setTimeout(()=>o.abort(),t.timeoutMs??3e4),i=()=>o.abort();t.signal?.addEventListener("abort",i,{once:!0});try{let a=await fetch(`${j()}${n}`,{body:t.body!==void 0?JSON.stringify(t.body):void 0,headers:e,method:t.method??"GET",signal:o.signal}),s=await a.text(),l={};try{l=s?JSON.parse(s):{}}catch{l={error:s.slice(0,300)}}if(!a.ok)throw new Ee(typeof l.error=="string"?l.error:`Runner API returned ${a.status}`,a.status,l.reRegister===!0);return l}finally{clearTimeout(r),t.signal?.removeEventListener("abort",i)}}async function or(n){await Oe("/api/runner/register",{body:n,method:"POST",timeoutMs:15e3})}async function rr(n,t){return(await Oe(`/api/runner/work?runnerId=${encodeURIComponent(n)}`,{signal:t,timeoutMs:28e3})).job??null}async function ir(n){await Oe("/api/runner/heartbeat",{body:{runnerId:n},method:"POST",timeoutMs:1e4})}async function ar(n,t){return{cancelRequested:(await Oe(`/api/runner/jobs/${encodeURIComponent(n)}/events`,{body:{events:t},method:"POST",timeoutMs:15e3})).cancelRequested===!0}}async function mn(n,t,e){await Oe(`/api/runner/jobs/${encodeURIComponent(n)}/complete`,{body:{result:e,runnerId:t},method:"POST",timeoutMs:15e3})}async function wt(n,t,e,o=!0){await Oe(`/api/runner/jobs/${encodeURIComponent(n)}/fail`,{body:{message:e,retryable:o,runnerId:t},method:"POST",timeoutMs:15e3})}import{mkdirSync as _a,readFileSync as Ma,writeFileSync as sr}from"fs";import{dirname as ja,join as Va}from"path";function un(n){return Va(n,"sessions.json")}function pn(n){try{let t=Ma(un(n),"utf8"),e=JSON.parse(t);if(e&&typeof e=="object"&&!Array.isArray(e))return e}catch{}return{}}function cr(n,t,e){return t?pn(n)[t]?.[e]??null:null}function lr(n,t,e,o){if(!(!t||!o))try{let r=un(n);_a(ja(r),{recursive:!0});let i=pn(n);i[t]={...i[t],[e]:o},sr(r,`${JSON.stringify(i,null," ")}
58
58
  `,"utf8")}catch{}}function dr(n,t,e){if(t)try{let o=pn(n);if(!o[t])return;delete o[t][e],sr(un(n),`${JSON.stringify(o,null," ")}
59
59
  `,"utf8")}catch{}}var Ja=300,Ha=3e4;async function mr(n){let[t,e]=await Promise.all([vt(n.claudeBin),xt(n.codexBin)]);if(!t&&!e)throw new Error("Neither Claude Code nor Codex was found on this machine. Install one and log in, then retry.");t?n.log(`Claude Code detected: ${t.version} (${t.bin})`):n.log("Claude Code not found \u2014 Claude turns will be declined."),e?n.log(`Codex detected: ${e.version} (${e.bin})`):n.log("Codex not found \u2014 Codex turns will be declined.");let o=[...t?["claude-code"]:[],...e?["codex"]:[]],r=`${fn().split(".")[0]}-${qa().slice(0,8)}`,i=()=>or({capabilities:["chat_turn","proof"],claudeCodeVersion:t?.version??null,cliVersion:n.version,codexVersion:e?.version??null,hostname:fn(),platform:Ga(),runnerId:r,runtimes:o});for(await i(),n.log(`Registered as ${r} (runtimes: ${o.join(", ")}). Waiting for work\u2026`);!n.signal?.aborted;){let a=null;try{a=await rr(r,n.signal)}catch(s){if(n.signal?.aborted)break;if(s instanceof Ee&&s.reRegister){n.log("Server asked for re-registration."),await i().catch(()=>{});continue}n.log(`Work poll failed (${s instanceof Error?s.message:"unknown"}); retrying\u2026`),await Ka(n.pollBackoffMs??5e3,n.signal);continue}if(!a){if(n.once)continue;continue}if(n.log(`Claimed ${a.jobType} job ${a.runId}.`),await za({claude:t,codex:e,job:a,options:n,runnerId:r}),n.once)break}}async function za({claude:n,codex:t,job:e,options:o,runnerId:r}){let i=new AbortController,a=()=>i.abort();o.signal?.addEventListener("abort",a,{once:!0});let s=setInterval(()=>{ir(r).catch(()=>{})},Ha),l=[],d=!1,f=async()=>{if(d||l.length===0)return;d=!0;let g=l.splice(0,100);try{let{cancelRequested:y}=await ar(e.runId,g);y&&i.abort()}catch(y){y instanceof Ee&&y.statusCode===409&&i.abort()}finally{d=!1}},b=setInterval(()=>{f()},Ja),v=g=>{l.push(g)};try{if(e.jobType==="runner.proof"){let g=typeof e.input.message=="string"?e.input.message:"proof";v({eventType:"proof.echo",payload:{message:g}}),await gn(f,l),await mn(e.runId,r,{echo:g}),o.log(`Proof job ${e.runId} completed.`);return}if(e.jobType==="runner.chat_turn"){let g=e.input.runtime==="codex"?"codex":"claude-code",y=g==="codex"?t&&{detection:t,run:nr}:n&&{detection:n,run:er};if(!y){await wt(e.runId,r,g==="codex"?"Codex is not available on this machine. Install the Codex app or CLI and log in, then retry.":"Claude Code is not available on this machine. Install it and log in, then retry.",!1);return}let k=typeof e.input.chatId=="string"&&e.input.chatId?e.input.chatId:null,w=Wa(o.workspaceRoot,"chats",k??e.runId);await Ba(w,{recursive:!0});let $=Ya(e.input),B=S=>v({eventType:S.eventType,message:S.message??null,payload:S.payload??{}}),L=cr(o.workspaceRoot,k,g),H;try{H=await y.run({bin:y.detection.bin,input:$,onEvent:B,resumeSessionId:L,signal:i.signal,workspace:w})}catch(S){if(!L||i.signal.aborted)throw S;o.log(`Resume of ${g} session failed (${S instanceof Error?S.message:"unknown"}); retrying fresh.`),dr(o.workspaceRoot,k,g),H=await y.run({bin:y.detection.bin,input:$,onEvent:B,resumeSessionId:null,signal:i.signal,workspace:w})}lr(o.workspaceRoot,k,g,H.sessionId),v({eventType:"chat.session",payload:{host:fn(),runtime:g,runtimeVersion:y.detection.version,sessionId:H.sessionId,sessionPath:H.sessionPath}}),await gn(f,l),await mn(e.runId,r,{modelId:H.modelId,runtime:g,runtimeVersion:y.detection.version,sessionId:H.sessionId,sessionPath:H.sessionPath,text:H.text,usage:H.usage??{}}),o.log(`Chat turn ${e.runId} (${g}) completed.`);return}await wt(e.runId,r,`This runner does not support ${e.jobType} jobs. Update the desktop app / CLI.`,!1)}catch(g){let y=g instanceof Error?g.message:"Local runner job crashed.";o.log(`Job ${e.runId} failed: ${y}`),await gn(f,l).catch(()=>{}),await wt(e.runId,r,y).catch(()=>{})}finally{clearInterval(b),clearInterval(s),o.signal?.removeEventListener("abort",a)}}function Ya(n){let t=Array.isArray(n.messages)?n.messages.map(e=>{if(!e||typeof e!="object")return null;let o=e;return{role:typeof o.role=="string"?o.role:"user",text:typeof o.text=="string"?o.text:""}}).filter(e=>!!e):[];return{effort:typeof n.effort=="string"?n.effort:null,messages:t,modelId:typeof n.modelId=="string"?n.modelId:null,system:typeof n.system=="string"?n.system:null,thinking:typeof n.thinking=="string"?n.thinking:null}}async function gn(n,t){for(;t.length>0;)await n(),t.length>0&&await new Promise(e=>setTimeout(e,25))}function Ka(n,t){return n<=0||t?.aborted?Promise.resolve():new Promise(e=>{let o=setTimeout(r,n);function r(){clearTimeout(o),t?.removeEventListener("abort",r),e()}t?.addEventListener("abort",r,{once:!0})})}function es(){return Qa(Xa(),`.${u.commands[0]}-runner`,"runs")}var St=new Za("runner").description("Run agent work locally with your own Claude Code / Codex login (desktop lane)");St.command("status").description("Check local Claude Code / Codex detection and account auth").action(async()=>{let[n,t]=await Promise.all([vt(),xt()]);if(console.log(n?re.green(`\u2713 Claude Code ${n.version}`):re.red("\u2717 Claude Code not found on PATH")),console.log(t?re.green(`\u2713 Codex ${t.version} (${t.bin})`):re.red("\u2717 Codex not found (PATH or Codex.app)")),!oe()){console.log(re.red(`\u2717 Not logged in. Run '${u.commands[0]} login' first.`)),process.exitCode=2;return}try{let e=await me();console.log(re.green(`\u2713 Logged in as ${e.user.email}`))}catch(e){console.log(re.red(`\u2717 Auth check failed: ${e instanceof Error?e.message:"unknown"}`)),process.exitCode=2}});St.command("start").description("Start the local runner (register + wait for work)").option("--once","Execute a single job, then exit").option("--workspace <dir>","Workspace root for job scratch directories").option("--claude-bin <path>","Path to the claude binary").option("--codex-bin <path>","Path to the codex binary").action(async n=>{oe()||(console.error(re.red(`Not authenticated. Run '${u.commands[0]} login' first.`)),process.exit(2));let t=new AbortController,e=()=>{console.log(re.gray(`
60
- Stopping runner\u2026`)),t.abort()};process.on("SIGINT",e),process.on("SIGTERM",e);try{await mr({claudeBin:n.claudeBin,codexBin:n.codexBin,log:o=>console.log(re.gray(`[runner] ${o}`)),once:n.once,signal:t.signal,version:"0.3.0",workspaceRoot:n.workspace??es()})}catch(o){console.error(re.red(o instanceof Error?o.message:String(o))),process.exit(1)}});import{Command as as}from"commander";import N from"chalk";function Ct(n){let{name:t,cmd:e}=n,o=t.toUpperCase().replace(/[^A-Z0-9]/g,"_");return`---
60
+ Stopping runner\u2026`)),t.abort()};process.on("SIGINT",e),process.on("SIGTERM",e);try{await mr({claudeBin:n.claudeBin,codexBin:n.codexBin,log:o=>console.log(re.gray(`[runner] ${o}`)),once:n.once,signal:t.signal,version:"0.3.1",workspaceRoot:n.workspace??es()})}catch(o){console.error(re.red(o instanceof Error?o.message:String(o))),process.exit(1)}});import{Command as as}from"commander";import N from"chalk";function Ct(n){let{name:t,cmd:e}=n,o=t.toUpperCase().replace(/[^A-Z0-9]/g,"_");return`---
61
61
  name: ${t}
62
62
  description: CLI with TTS (text-to-speech), AI music generation, sound effects, stock image/video search, audio mixing, URL scraping, and branding. Use when user asks for voiceover, narration, background music, sound effects, stock footage, or needs to scrape a website.
63
63
  ---
@@ -3760,7 +3760,7 @@ execSync("${e} video generate plan --project .", { stdio: "inherit" });
3760
3760
  const args = process.argv.slice(2).join(" ");
3761
3761
  execSync("${e} video export --project . " + args, { stdio: "inherit" });
3762
3762
  `;await ue(V(r,"plan.ts"),a,"utf-8"),await ue(V(r,"export.ts"),s,"utf-8")}async function si(){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 $c(n){return n.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function Ac(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 Vt(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 ue(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 ue(t,Buffer.from(o))}function Ec(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 Oc=new Q("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"?it("Initializing...").start():null,o=n.project?ot(process.cwd(),n.project):null;try{let r=await si(),i=null;if(n.scenes&&!n.scriptFile&&(n.scriptFile=n.scenes),o&&(n.output=V(o,"public"),n.scriptFile?n.scriptFile=ot(o,n.scriptFile):!n.script&&!r&&(n.scriptFile=V(o,"public","scenes.json"))),r)try{let T=JSON.parse(r);T.scenes&&Array.isArray(T.scenes)&&(i=T)}catch{}if(!i&&n.scriptFile)try{let T=await rt(n.scriptFile,"utf-8"),W=JSON.parse(T);W.scenes&&Array.isArray(W.scenes)&&(i=W)}catch{}let a=i?.voice||n.voice,s=i?.voiceId,l=i?.provider,d=i?.model,f=i?.voiceInstructions,b=i?.musicPrompt||n.musicPrompt||"uplifting background music, positive energy",v=i?.musicDuration,g=V(n.output,"audio"),y=V(n.output,"images"),k=V(n.output,"videos");e&&(e.text="Creating directories..."),await He(g,{recursive:!0}),await He(y,{recursive:!0}),await He(k,{recursive:!0});let w=0,$=[],B=0,L=[],H=[];if(i&&i.scenes.length>0){t==="human"&&(e?.stop(),m(`Processing ${i.scenes.length} scenes...`),e?.start());let T=i.scenes.map((te,M)=>{let le=te.script;return te.voiceStyle&&(le=`${te.voiceStyle} ${le}`),{text:le,id:`scene-${M}`}});e&&(e.text="Generating speech for all scenes...");let W=await vo({texts:T,options:{provider:l,voice:a,voiceId:s,model:d,voiceSettings:i.voiceSettings,instructions:f}});w+=W.totalCost;let ee=0;for(let te=0;te<i.scenes.length;te++){let M=i.scenes[te],le=$c(M.name),ae=W.results[te];e&&(e.text=`[${M.name}] Saving audio...`);let Wt=V(g,`${le}.${ae.format}`);await ue(Wt,ae.audioData);let Te=ae.duration,fi=Math.round(Te*We),Ye={id:te+1,name:M.name,text:M.script,wordCount:M.script.split(/\s+/).length,startTime:ee,endTime:ee+Te,durationInSeconds:Te,durationInFrames:fi,audioPath:`audio/${le}.${ae.format}`,timestamps:ae.timestamps};if(M.imageQuery){e&&(e.text=`[${M.name}] Searching image...`);try{let K=await ht({query:M.imageQuery,options:{maxResults:1,size:"large",safeSearch:!0}}),Ke=K.data.results.flatMap(z=>z.results);if(w+=K.data.totalCost,Ke.length>0){let z=Ke[0],Xe=Ec(z.url),ye=`${le}.${Xe}`,Jt=V(y,ye);await Vt(z.url,Jt),Ye.imagePath=`images/${ye}`,L.push({path:`images/${ye}`,url:z.url,width:z.width,height:z.height,query:M.imageQuery})}}catch(K){t==="human"&&(e?.stop(),A(`[${M.name}] Image search failed: ${K instanceof Error?K.message:"Unknown"}`),e?.start())}}if(M.videoQuery){e&&(e.text=`[${M.name}] Searching video...`);try{let K=await en({query:M.videoQuery,options:{maxResults:1,license:"free"}}),Ke=K.data.results.flatMap(z=>z.results);if(w+=K.data.totalCost,Ke.length>0){let z=Ke[0],Xe=z.previewUrl||z.downloadUrl;if(Xe){let ye=`${le}.mp4`,Jt=V(k,ye);await Vt(Xe,Jt),Ye.videoPath=`videos/${ye}`,H.push({path:`videos/${ye}`,url:Xe,width:z.width,height:z.height,duration:z.duration,query:M.videoQuery})}}}catch(K){t==="human"&&(e?.stop(),A(`[${M.name}] Video search failed: ${K instanceof Error?K.message:"Unknown"}`),e?.start())}}if($.push(Ye),ee+=Te,B+=Te,t==="human"){e?.stop();let K=[`audio: ${Te.toFixed(1)}s`,Ye.imagePath?"image":null,Ye.videoPath?"video":null].filter(Boolean).join(", ");C(` ${M.name}: ${K}`),e?.start()}}}else{let T=n.script;if(n.scriptFile)try{T=await rt(n.scriptFile,"utf-8")}catch(ae){e?.stop(),c(`Failed to read script file: ${ae instanceof Error?ae.message:"Unknown error"}`),process.exit(p.INVALID_INPUT)}(!T||T.trim().length===0)&&(e?.stop(),c("Provide scenes via stdin JSON, --scenes/--script-file with scenes JSON, or --script for legacy mode"),process.exit(p.INVALID_INPUT)),T=T.trim();let W=n.topic||T.split(".")[0].slice(0,50);e&&(e.text="Generating voiceover...");let ee=await gt({text:T,options:{voice:a}}),te=V(g,`voiceover.${ee.format}`);await ue(te,ee.audioData),w+=ee.cost,B=ee.duration;let M=Cc(T);$=Ic(M,ee.duration,We,ee.timestamps).map((ae,Wt)=>({...ae,name:`Section${Wt+1}`,audioPath:`audio/voiceover.${ee.format}`})),t==="human"&&(e?.stop(),C(`Voiceover: ${te} (${ee.duration.toFixed(1)}s)`),e?.start())}e&&(e.text="Creating timeline...");let S=Ac($),U=Math.max(S.audio.length>0?Math.max(...S.audio.map(T=>T.endMs)):0,S.text.length>0?Math.max(...S.text.map(T=>T.endMs)):0,S.elements.length>0?Math.max(...S.elements.map(T=>T.endMs)):0),_=U/1e3,Z=v?Math.min(300,Math.max(3,v)):Math.min(300,Math.ceil(_));console.log("[Music Generation] Requesting music:",{prompt:b,requestedDuration:Z,totalAudioDuration:B,actualVideoDuration:_,timelineDurationMs:U});let ze,qt;e&&(e.text="Generating music and thumbnail...");let ui=`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: ${$.map(T=>T.text).join(" ")}`,Bt=[],pi=(async()=>{if(Z<3)return t==="human"&&(e?.stop(),A(`Video duration (${_.toFixed(1)}s) is too short for music generation (minimum 3s).`),e?.start()),null;try{let T=await ft({prompt:b,duration:Z});if(T.status!=="completed"&&T.status!=="failed"&&(T=await he(()=>Qe(T.requestId),60,2e3)),T.status==="failed")throw new Error(T.error||"Unknown error");let W=V(g,"music.mp3");return T.audioUrl&&await Vt(T.audioUrl,W),{path:"audio/music.mp3",duration:T.duration||Z,prompt:b,cost:T.cost||0}}catch(T){return t==="human"&&(e?.stop(),A(`Music generation failed: ${T.message}`),e?.start()),null}})();Bt.push(pi);let gi=(async()=>{try{let T=await So({prompt:ui,options:{width:1280,height:720}});if(T.success&&T.data.url){let W=V(n.output,"thumbnail.jpg");return await Vt(T.data.url,W),w+=T.data.cost||0,W}return null}catch(T){return t==="human"&&(e?.stop(),A(`Thumbnail generation failed: ${T.message}`),e?.start()),null}})();Bt.push(gi);let[Ie,Gn]=await Promise.all(Bt);Ie&&(ze=Ie,w+=Ie.cost||0,t==="human"&&(e?.stop(),C(`Music: ${V(g,"music.mp3")} (${Ie.duration}s)`),Ie.duration<_&&A(`Music duration (${Ie.duration.toFixed(1)}s) is shorter than video duration (${_.toFixed(1)}s).`),e?.start())),Gn&&(qt=Gn,t==="human"&&(e?.stop(),C(`Thumbnail: ${qt}`),e?.start())),e&&(e.text="Writing manifest...");let Wn=Math.round(_*We),Jn={music:ze,thumbnail:qt?"thumbnail.jpg":void 0,images:L,videos:H,scenes:$,timeline:S,totalDurationInFrames:Wn,fps:We,totalCost:w,createdAt:new Date().toISOString()},Gt=V(n.output,"video-manifest.json");if(await ue(Gt,JSON.stringify(Jn,null,2)),e?.stop(),t==="json"){let{totalCost:T,...W}=Jn;x(W);return}if(t==="quiet"){console.log(Gt);return}console.log(),C("Video assets created successfully!"),console.log(),m(`Scenes: ${$.length} (${Wn} frames at ${We}fps)`);for(let T of $){let W=[T.audioPath?"audio":null,T.imagePath?"image":null,T.videoPath?"video":null].filter(Boolean).join(", ");m(` - ${T.name}: ${T.durationInSeconds.toFixed(1)}s [${W}]`)}m(`Music: ${ze?.path} (${ze?.duration}s)`),m(`Manifest: ${Gt}`),console.log(),m(`Total cost: $${w.toFixed(4)}`)}catch(r){e?.stop(),c(r instanceof Error?r.message:"Unknown error"),process.exit(p.GENERAL_ERROR)}}),Pc=new Q("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"?it("Finding videos...").start():null;try{let s=await en({query:n,options:{maxResults:parseInt(e,10),orientation:o,license:r}});a?.stop();let l=s.data.results.flatMap(d=>d.results);if(i==="json"){let{totalCost:d,...f}=s.data;x({...s,data:f});return}if(i==="quiet"){l.forEach(d=>{console.log(d.previewUrl||d.thumbnailUrl)});return}if(l.length===0){m("No videos found");return}C(`Found ${l.length} videos for "${n}"`),console.log(),l.forEach((d,f)=>{console.log(`[${f+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()}),m(`Total cost: $${s.data.totalCost.toFixed(4)}`)}catch(s){a?.stop(),c(s instanceof Error?s.message:"Unknown error"),process.exit(p.GENERAL_ERROR)}}),Dc=new Q("new").description("Create a new video project from a template").argument("<name>","Project directory name").option("-t, --template <repo>","GitHub repo (user/repo#commit)",xc).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"?it("Initializing video project...").start():null;try{let r=Be();await He(r,{recursive:!0});let i=Ut(n),a=t.type,s=await Mn();if(s.includes(n)){let g=ti(n,s);o?.stop(),c(`Project "${n}" already exists at: ${i}`),m(`Try: ${u.commands[0]} video generate new ${g}`),process.exit(p.INVALID_INPUT)}/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+(#[a-zA-Z0-9_./-]+)?$/.test(t.template)||(o?.stop(),c(`Invalid template format: "${t.template}". Expected format: owner/repo or owner/repo#branch`),process.exit(p.INVALID_INPUT)),["youtube","tiktok","audiogram"].includes(a)||(o?.stop(),c(`Invalid type "${a}". Use youtube, tiktok, or audiogram.`),process.exit(p.INVALID_INPUT)),o&&(o.text=`Cloning template from ${t.template}...`);let[d,f]=t.template.split("#"),b=f?`-b ${f}`:"";try{Je(`git clone ${b} --depth 1 https://github.com/${d}.git "${i}"`,{stdio:"pipe"}),await Vn(V(i,".git"),{recursive:!0,force:!0})}catch(g){o?.stop(),c(`Failed to clone template: ${g instanceof Error?g.message:"Unknown error"}`),process.exit(p.GENERAL_ERROR)}e==="human"&&(o?.stop(),C(`Template downloaded to ${n}/`),o?.start());let v;try{v=await Sc(i)}catch{o?.stop(),c("template.manifest.json not found or invalid in template repo"),process.exit(p.INVALID_INPUT)}if(v.propsSchema&&v.propsSchema!=="video-plan.v1"&&(o?.stop(),c(`Template propsSchema must be video-plan.v1 (found ${v.propsSchema})`),process.exit(p.INVALID_INPUT)),o&&(o.text="Writing scaffold files..."),await Rc(i,a,u.commands[0]),t.install){let g=ai()?"bun":"npm";o&&(o.text=`Installing dependencies with ${g}...`),await new Promise((y,k)=>{let w=yc(g,["install"],{cwd:i,stdio:"pipe",shell:!0});w.on("close",$=>{$===0?y():k(new Error(`${g} install failed with code ${$}`))}),w.on("error",k)}),e==="human"&&(o?.stop(),C("Dependencies installed"),o?.start())}if(o?.stop(),e==="json"){x({name:n,path:i,template:t.template,type:a,installed:t.install});return}if(e==="quiet"){console.log(i);return}console.log(),C(`Video project "${n}" created successfully!`),m(`Location: ${i}`),a==="tiktok"?m("Format: TikTok/Reels/Shorts (1080x1920 @ 30fps)"):a==="audiogram"?m("Format: Audiogram (1080x1080 @ 30fps)"):m("Format: YouTube (1920x1080 @ 30fps)"),console.log(),m("Next steps:"),m(` cd ${i}`),t.install||m(` ${ai()?"bun":"npm"} install`),m(" bun run dev # Preview in Remotion Studio"),m(` ${u.commands[0]} video generate assets --scenes public/scenes.json`),m(` ${u.commands[0]} video generate plan`)}catch(r){o?.stop(),c(r instanceof Error?r.message:"Unknown error"),process.exit(p.GENERAL_ERROR)}}),Nc=new Q("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").option("--render-target <target>","Plan target: hyperframes or remotion","hyperframes").option("--no-wait","Return the background run ID immediately").action(async n=>{let t=it("Generating video plan...").start();try{let e=n.context;if(!e){let l=await si();l&&(e=l)}e||(t.stop(),c("Provide context via stdin or --context flag"),process.exit(p.INVALID_INPUT));let o;if(n.branding)try{o=await rt(n.branding,"utf-8")}catch{t.stop(),c(`Could not read branding file: ${n.branding}`),process.exit(p.INVALID_INPUT)}let r=n.renderTarget==="remotion"?"remotion":"hyperframes",i={context:e,branding:o,renderTarget:r};if(n.wait===!1){t.stop(),x(await nn(i));return}let a=await ko(i);t.stop(),a.success||(c(a.error||"Failed to generate plan"),process.exit(p.GENERAL_ERROR));let s=a.data;n.output?(await ue(n.output,s),C(`Plan saved to ${n.output}`)):console.log(s)}catch(e){t.stop(),c(e instanceof Error?e.message:"Unknown error"),process.exit(p.GENERAL_ERROR)}}),Fc=new Q("generate").description("Generate parts of a video project").addCommand(Dc).addCommand(Oc).addCommand(Nc),Lc=new Q("create").description("Start background standalone video generation").argument("<title>","Video title").requiredOption("--brief <brief>","Video brief").option("--topic <topic>","Video topic").option("--duration <seconds>","Duration in seconds","30").option("--format <format>","tiktok, instagram_reel, or youtube_short","tiktok").option("--team-id <id>","Workspace ID").option("--yes","Approve generation").action(async(n,t)=>{if(!t.yes)return c("Pass --yes to approve video generation");try{x(await To({brief:t.brief,durationSec:Number(t.duration),format:t.format,title:n,topic:t.topic}))}catch(e){c(e instanceof Error?e.message:"Generation failed"),process.exit(p.GENERAL_ERROR)}}),ci=new Q("plan").description("Start background video-plan generation").requiredOption("--context <brief>","Video brief (minimum 100 characters)").option("--branding <path>","Branding JSON file").option("--render-target <target>","hyperframes or remotion","hyperframes").option("--yes","Approve generation").action(async n=>{if(!n.yes)return c("Pass --yes to approve plan generation");try{let t=n.branding?await rt(n.branding,"utf-8"):void 0;x(await nn({branding:t,context:n.context,renderTarget:n.renderTarget==="remotion"?"remotion":"hyperframes"}))}catch(t){c(t instanceof Error?t.message:"Plan generation failed"),process.exit(p.GENERAL_ERROR)}}),Uc=new Q("status").description("Read video or video-plan background status").argument("<run-id>","Run ID").option("--plan","Read a video-plan run").option("--team-id <id>","Workspace ID").action(async(n,t)=>{try{x(await F(t.plan?"generateVideoPlan":"createVideo",n,t.teamId))}catch(e){c(e instanceof Error?e.message:"Status failed"),process.exit(p.GENERAL_ERROR)}}),_c=new Q("status").description("Read video-plan background status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(n,t)=>{try{x(await F("generateVideoPlan",n,t.teamId))}catch(e){c(e instanceof Error?e.message:"Status failed"),process.exit(p.GENERAL_ERROR)}});ci.addCommand(_c);var Mc=new Q("projects").description("List all video projects").option("-f, --format <format>","Output format: human, json","human").action(async n=>{let t=await Mn();if(n.format==="json"){x({projectsDir:Be(),projects:t.map(e=>({name:e,path:Ut(e)}))});return}if(t.length===0){m("No video projects found"),m(`Projects are stored in: ${Be()}`);return}console.log(`
3763
- Video Projects:`),console.log("-".repeat(50));for(let e of t)console.log(` ${e}`),console.log(` ${Ut(e)}`);console.log()}),jc=new Q("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"?it("Injecting thumbnail...").start():null;try{let o=ot(n.video),r=ot(n.thumbnail),i=n.output?ot(n.output):o.replace(/\.mp4$/,"-with-thumb.mp4");await jn(o),await jn(r);let a=vc;if(a)try{await jn(a)}catch{a=null}if(!a)try{a=Je("which ffmpeg",{encoding:"utf8"}).trim()}catch{throw new Error("ffmpeg not found. Install ffmpeg or ffmpeg-static.")}let s=bc(a,["-i",o],{encoding:"utf8"});if(s.error)throw new Error(`Failed to probe video: ${s.error.message}`);let l=(s.stderr??"").match(/(\d{2,5})x(\d{2,5})/);if(!l)throw new Error("Could not detect video dimensions");let[,d,f]=l;e&&(e.text="Creating thumbnail frame...");let b="/tmp/cc-thumb-frame.mp4";Je(`${a} -y -loop 1 -i "${r}" -vf "scale=${d}:${f}:force_original_aspect_ratio=decrease,pad=${d}:${f}:(ow-iw)/2:(oh-ih)/2,setsar=1" -t 0.033 -r 30 -c:v libx264 -pix_fmt yuv420p "${b}"`,{stdio:"pipe"}),e&&(e.text="Concatenating with video...");let v="/tmp/cc-concat-video.mp4";if(Je(`${a} -y -i "${b}" -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 "${v}"`,{stdio:"pipe"}),e&&(e.text="Adding file icon thumbnail..."),Je(`${a} -y -i "${v}" -i "${r}" -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic "${i}"`,{stdio:"pipe"}),await Vn(b,{force:!0}),await Vn(v,{force:!0}),e?.stop(),t==="json"){x({success:!0,output:i});return}if(t==="quiet"){console.log(i);return}C(`Thumbnail injected: ${i}`)}catch(o){e?.stop(),c(o instanceof Error?o.message:"Unknown error"),process.exit(p.GENERAL_ERROR)}}),Vc=new Q("thumbnail").description("Thumbnail operations").addCommand(jc),qn=new Q("video").description("Video generation commands").addCommand(Fc).addCommand(Pc).addCommand(Mc).addCommand(Vc).addCommand(ii);qn.addCommand(Lc).addCommand(ci).addCommand(Uc);import{Command as Bn}from"commander";var qc=new Bn("create").description("Start background visual-abstract generation").argument("<brief>","Dense source brief").option("--title <title>","Artifact title").option("--aspect <ratio>","1:1, 16:9, 9:16, 4:3, or 3:4","1:1").option("--quality <tier>","fast, balanced, or best","best").option("--preset <preset>","Output preset","executive-one-pager").option("--branding-id <id>","Branding ID").option("--team-id <id>","Workspace ID").option("--yes","Approve generation").action(async(n,t)=>{if(!t.yes)return c("Pass --yes to approve visual-abstract generation");try{x(await R("createVisualAbstract",{approvalReceipt:"cli:--yes",aspectRatio:t.aspect,brandingId:t.brandingId,brief:n,outputPreset:t.preset,qualityTier:t.quality,teamId:t.teamId,title:t.title}))}catch(e){c(e instanceof Error?e.message:"Generation failed"),process.exit(p.GENERAL_ERROR)}}),Bc=new Bn("status").description("Read visual-abstract generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(n,t)=>{try{x(await F("createVisualAbstract",n,t.teamId))}catch(e){c(e instanceof Error?e.message:"Status failed"),process.exit(p.GENERAL_ERROR)}}),li=new Bn("visual-abstract").description("Visual abstract commands").addCommand(qc).addCommand(Bc);var Gc=[Dr,Nr,Ar,Zr,jr,_r,Fr,qn,Wr,zr,Gr,Jr,Or,Lr,qr,Rr,li,Ur,Ir];function di(n){for(let t of Gc)n.addCommand(t)}var Jc="0.3.0",q=new Wc,pe=u.commands[0];q.name(pe).description(u.description).version(Jc,"-v, --version","Show version number").option("--debug","Enable debug logging").option("--no-color","Disable colored output").configureOutput({outputError:(n,t)=>{t(ie.red(n))}});q.addCommand(Ko);q.addCommand(Xo);q.addCommand(Fo);q.addCommand(Wo);q.addCommand(Lo);q.addCommand(Vo);q.addCommand(Go);q.addCommand(wr);q.addCommand(qo);q.addCommand(Sr);q.addCommand(qe);q.addCommand(St);q.addCommand(Eo);di(q);var mi=Mo();mi.commands.length>0&&q.addCommand(mi);q.on("command:*",n=>{console.error(ie.red(`Error: Unknown command '${n[0]}'`)),console.error(),console.error(`Run '${pe} --help' to see available commands.`),process.exit(1)});q.addHelpText("after",`
3763
+ Video Projects:`),console.log("-".repeat(50));for(let e of t)console.log(` ${e}`),console.log(` ${Ut(e)}`);console.log()}),jc=new Q("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"?it("Injecting thumbnail...").start():null;try{let o=ot(n.video),r=ot(n.thumbnail),i=n.output?ot(n.output):o.replace(/\.mp4$/,"-with-thumb.mp4");await jn(o),await jn(r);let a=vc;if(a)try{await jn(a)}catch{a=null}if(!a)try{a=Je("which ffmpeg",{encoding:"utf8"}).trim()}catch{throw new Error("ffmpeg not found. Install ffmpeg or ffmpeg-static.")}let s=bc(a,["-i",o],{encoding:"utf8"});if(s.error)throw new Error(`Failed to probe video: ${s.error.message}`);let l=(s.stderr??"").match(/(\d{2,5})x(\d{2,5})/);if(!l)throw new Error("Could not detect video dimensions");let[,d,f]=l;e&&(e.text="Creating thumbnail frame...");let b="/tmp/cc-thumb-frame.mp4";Je(`${a} -y -loop 1 -i "${r}" -vf "scale=${d}:${f}:force_original_aspect_ratio=decrease,pad=${d}:${f}:(ow-iw)/2:(oh-ih)/2,setsar=1" -t 0.033 -r 30 -c:v libx264 -pix_fmt yuv420p "${b}"`,{stdio:"pipe"}),e&&(e.text="Concatenating with video...");let v="/tmp/cc-concat-video.mp4";if(Je(`${a} -y -i "${b}" -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 "${v}"`,{stdio:"pipe"}),e&&(e.text="Adding file icon thumbnail..."),Je(`${a} -y -i "${v}" -i "${r}" -map 0 -map 1 -c copy -c:v:1 png -disposition:v:1 attached_pic "${i}"`,{stdio:"pipe"}),await Vn(b,{force:!0}),await Vn(v,{force:!0}),e?.stop(),t==="json"){x({success:!0,output:i});return}if(t==="quiet"){console.log(i);return}C(`Thumbnail injected: ${i}`)}catch(o){e?.stop(),c(o instanceof Error?o.message:"Unknown error"),process.exit(p.GENERAL_ERROR)}}),Vc=new Q("thumbnail").description("Thumbnail operations").addCommand(jc),qn=new Q("video").description("Video generation commands").addCommand(Fc).addCommand(Pc).addCommand(Mc).addCommand(Vc).addCommand(ii);qn.addCommand(Lc).addCommand(ci).addCommand(Uc);import{Command as Bn}from"commander";var qc=new Bn("create").description("Start background visual-abstract generation").argument("<brief>","Dense source brief").option("--title <title>","Artifact title").option("--aspect <ratio>","1:1, 16:9, 9:16, 4:3, or 3:4","1:1").option("--quality <tier>","fast, balanced, or best","best").option("--preset <preset>","Output preset","executive-one-pager").option("--branding-id <id>","Branding ID").option("--team-id <id>","Workspace ID").option("--yes","Approve generation").action(async(n,t)=>{if(!t.yes)return c("Pass --yes to approve visual-abstract generation");try{x(await R("createVisualAbstract",{approvalReceipt:"cli:--yes",aspectRatio:t.aspect,brandingId:t.brandingId,brief:n,outputPreset:t.preset,qualityTier:t.quality,teamId:t.teamId,title:t.title}))}catch(e){c(e instanceof Error?e.message:"Generation failed"),process.exit(p.GENERAL_ERROR)}}),Bc=new Bn("status").description("Read visual-abstract generation status").argument("<run-id>","Run ID").option("--team-id <id>","Workspace ID").action(async(n,t)=>{try{x(await F("createVisualAbstract",n,t.teamId))}catch(e){c(e instanceof Error?e.message:"Status failed"),process.exit(p.GENERAL_ERROR)}}),li=new Bn("visual-abstract").description("Visual abstract commands").addCommand(qc).addCommand(Bc);var Gc=[Dr,Nr,Ar,Zr,jr,_r,Fr,qn,Wr,zr,Gr,Jr,Or,Lr,qr,Rr,li,Ur,Ir];function di(n){for(let t of Gc)n.addCommand(t)}var Jc="0.3.1",q=new Wc,pe=u.commands[0];q.name(pe).description(u.description).version(Jc,"-v, --version","Show version number").option("--debug","Enable debug logging").option("--no-color","Disable colored output").configureOutput({outputError:(n,t)=>{t(ie.red(n))}});q.addCommand(Ko);q.addCommand(Xo);q.addCommand(Fo);q.addCommand(Wo);q.addCommand(Lo);q.addCommand(Vo);q.addCommand(Go);q.addCommand(wr);q.addCommand(qo);q.addCommand(Sr);q.addCommand(qe);q.addCommand(St);q.addCommand(Eo);di(q);var mi=Mo();mi.commands.length>0&&q.addCommand(mi);q.on("command:*",n=>{console.error(ie.red(`Error: Unknown command '${n[0]}'`)),console.error(),console.error(`Run '${pe} --help' to see available commands.`),process.exit(1)});q.addHelpText("after",`
3764
3764
  ${ie.bold("Examples:")}
3765
3765
  ${ie.gray("# Log in (opens browser)")}
3766
3766
  $ ${pe} login
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conceptcraft/mindframes",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "CLI tool for creating AI-powered presentations with ConceptCraft",
5
5
  "type": "module",
6
6
  "bin": {
@@ -51,11 +51,11 @@
51
51
  "registry": "https://registry.npmjs.org"
52
52
  },
53
53
  "engines": {
54
- "node": ">=20.0.0"
54
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
55
55
  },
56
56
  "dependencies": {
57
57
  "@anthropic-ai/claude-agent-sdk": "0.3.168",
58
- "@inquirer/prompts": "^8.1.0",
58
+ "@inquirer/prompts": "8.5.2",
59
59
  "@openai/codex-sdk": "^0.142.5",
60
60
  "@remotion/bundler": "4.0.375",
61
61
  "@remotion/renderer": "4.0.375",