@barbapapazes/content-creation 0.15.0 → 0.15.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.
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +1 -6
- package/dist/index.d.mts +68 -0
- package/dist/index.mjs +1 -0
- package/package.json +9 -1
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import e from"node:process";import{intro as t,isCancel as n,log as r,multiselect as i,outro as a,select as o,text as s}from"@clack/prompts";import{cac as c}from"cac";import{addDays as l,format as u,parse as d,setHours as f,setMinutes as p,setSeconds as m}from"date-fns";import{existsSync as h,mkdirSync as g,readFileSync as _,readdirSync as v,statSync as y,writeFileSync as b}from"node:fs";import{dirname as ee,isAbsolute as x,join as S,resolve as C}from"node:path";import w from"gray-matter";import{homedir as T}from"node:os";import{loadConfig as E}from"c12";var te=`0.15.0`;const D={linkedin:{mainFile:`linkedin.md`,additionalFiles:[`linkedin-script.md`]},x:{mainFile:`x.md`},youtube:{mainFile:`youtube-script.md`,additionalFiles:[`youtube-description.md`]},instagram:{mainFile:`instagram-script.md`,additionalFiles:[`instagram-description.md`]}},O={thematic:[],templatesDir:void 0,templates:{},scheduling:{}};function k(t,n){return t?x(t)?t:C(T(),t):n?ee(n):e.cwd()}function A(e,t){let n={};if(e?.footerPath){let r=C(t,e.footerPath);h(r)&&(n.footerPath=r)}return n}function j(e,t){let n={};if(e?.templatePath){let r=C(t,e.templatePath);h(r)&&(n.templatePath=r)}return n}async function M(){let{config:t,configFile:n}=await E({name:`content-creation`,defaults:O,globalRc:!0,dotenv:!0}),r=k(t.templatesDir,n),i={linkedin:A(t.templates?.linkedin,r),youtube:j(t.templates?.youtube,r),instagram:j(t.templates?.instagram,r)},a={automationEndpoint:t.scheduling?.automationEndpoint||e.env.AUTOMATION_ENDPOINT,cfAccessClientId:t.scheduling?.cfAccessClientId||e.env.CF_ACCESS_CLIENT_ID,cfAccessClientSecret:t.scheduling?.cfAccessClientSecret||e.env.CF_ACCESS_CLIENT_SECRET};return{thematic:t.thematic||[],templatesDir:t.templatesDir||``,templates:i,scheduling:a}}function N(){return`
|
|
3
|
-
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Je m'appelle Estéban, et pour toi, je décode l'IA pour t'aider à passer à l'action. Abonne-toi ! 🛠️
|
|
7
|
-
`}function P(e){return h(e)?_(e,`utf-8`):``}function F(t,n,i,a,o=e.cwd()){let s=C(S(o,u(t,`yyyy`),u(t,`MM`),u(t,`dd`)));h(s)||(g(s,{recursive:!0}),r.success(`Created directory: ${s}`));for(let e of n)I(e,s,i,a);return s}function I(e,t,n,i){let a=D[e],o=S(t,a.mainFile);if(h(o)?r.info(`${a.mainFile} already exists, skipping`):L(e,o,n,i),a.additionalFiles)for(let o of a.additionalFiles){let a=S(t,o);z(o,e,n)&&(h(a)?r.info(`${o} already exists, skipping`):R(e,a,o,i))}}function L(e,t,n,i){let a={title:n.title};e===`linkedin`&&(n.hasVideo&&(a.video=!0),n.hasImages&&(a.images=[null]));let o=``;if(e===`linkedin`){let e=i.templates.linkedin?.footerPath;o=e?P(e):N()}b(t,w.stringify(o,a),`utf-8`),r.success(`Created ${t}`)}function R(e,t,n,i){let a=``;if(n.endsWith(`-description.md`)){let t=i.templates[e]?.templatePath;t&&(a=P(t))}b(t,a,`utf-8`),r.success(`Created ${t}`)}function z(e,t,n){return t===`linkedin`&&e===`linkedin-script.md`?n.hasVideo||!1:!0}function B(e,t){let n=[],i=D[t].mainFile;try{let t=v(e).filter(t=>y(S(e,t)).isDirectory()&&/^\d{4}$/.test(t));for(let a of t){let t=S(e,a),o=v(t).filter(e=>y(S(t,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of o){let o=S(t,e),s=v(o).filter(e=>y(S(o,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of s){let s=S(S(o,t),i);if(h(s)){let o=`${a}-${e}-${t}`,c=new Date(o);try{let{data:r}=w(_(s,`utf-8`)),l=r.title||`Untitled (${o})`;n.push({path:s,date:c,title:l,relativePath:`${a}/${e}/${t}/${i}`})}catch(e){r.warn(`Error reading ${s}: ${e}`)}}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime())}function V(e){switch(e){case`linkedin`:return`linkedin-posts.md`;case`x`:return`x-posts.md`;case`youtube`:return`youtube-videos.md`;case`instagram`:return`instagram-posts.md`}}function H(e){switch(e){case`linkedin`:return`LinkedIn Posts`;case`x`:return`X Posts`;case`youtube`:return`YouTube Videos`;case`instagram`:return`Instagram Posts`}}function U(e,t){let n=B(e,t);if(n.length===0){r.info(`No ${t} posts found`);return}let i=H(t),a=V(t),o=`# ${i}\n\n`;o+=`_Generated on ${new Date().toLocaleDateString()}_\n\n`,o+=`Total posts: ${n.length}\n\n`;for(let e of n){let t=e.date.toLocaleDateString(`en-US`,{year:`numeric`,month:`long`,day:`numeric`});o+=`- [${e.title}](${e.relativePath}) - _${t}_\n`}b(S(e,a),o,`utf-8`),r.success(`Created ${a} with ${n.length} posts`)}function ne(t=e.cwd(),n){let r=n||[`linkedin`,`x`,`youtube`,`instagram`];for(let e of r)U(t,e)}function W(e){return e.toLowerCase().trim().normalize(`NFD`).replace(/[\u0300-\u036F]/g,``).replace(/[^a-z0-9\s-]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+|-+$/g,``)||`untitled`}function G(t,n,i=e.cwd()){let a=W(t),o=S(C(S(i,`resources`)),a),s=`${n}.md`,c=S(o,s);if(h(c))return r.info(`${s} already exists at: ${c}`),o;g(o,{recursive:!0}),r.success(`Created directory: ${o}`);let l={title:t,url:``,date:``};return b(c,w.stringify(``,l),`utf-8`),r.success(`Created ${s} at: ${c}`),o}function K(t=e.cwd()){let n=[];try{let e=v(t).filter(e=>y(S(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let r of e){let e=S(t,r),i=v(e).filter(t=>y(S(e,t)).isDirectory()&&/^\d{2}$/.test(t));for(let t of i){let i=S(e,t),a=v(i).filter(e=>y(S(i,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of a){let a=S(i,e);if(h(S(a,`linkedin.md`))){let i=`${r}-${t}-${e}`;n.push({path:a,date:new Date(i),displayPath:`${r}/${t}/${e}`})}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime()).slice(0,8)}function q(e){let t=[`.jpg`,`.jpeg`,`.png`],n=[];try{let r=v(e);for(let i of r)if(y(S(e,i)).isFile()){let e=i.toLowerCase().substring(i.lastIndexOf(`.`));t.includes(e)&&n.push(i)}}catch(e){r.error(`Error reading directory: ${e}`)}return n.sort()}function J(e,t){try{let{data:n,content:i}=w(_(e,`utf-8`));n.images=t.length>0?t:[null],b(e,w.stringify(i,n),`utf-8`),r.success(`Updated ${e} with ${t.length} image(s)`)}catch(e){r.error(`Error updating frontmatter: ${e}`)}}async function Y(t){if(t.length===0)return r.warn(`No dated folders with linkedin.md found`),null;let i=await o({message:`Select a folder to link images:`,options:t.map(e=>({label:e.displayPath,value:e.path,hint:`Link images in ${e.displayPath}`}))});return n(i)&&(r.error(`Operation cancelled.`),e.exit(0)),t.find(e=>e.path===i)||null}async function X(t=e.cwd()){let n=K(t);if(n.length===0){r.warn(`No dated folders with linkedin.md found`);return}let i=await Y(n);if(!i)return;let a=q(i.path);if(a.length===0){r.info(`No images found in ${i.displayPath}`),J(S(i.path,`linkedin.md`),[]);return}r.info(`Found ${a.length} image(s): ${a.join(`, `)}`),J(S(i.path,`linkedin.md`),a)}async function Z(t,n){let i=S(t,`x.md`),{data:a,content:o}=w(_(i,`utf-8`));a.scheduled===!0&&(r.warn(`This content is already scheduled. Skipping.`),e.exit(0)),(!n.scheduling.automationEndpoint||!n.scheduling.cfAccessClientId||!n.scheduling.cfAccessClientSecret)&&(r.error(`Scheduling configuration is missing. Please set AUTOMATION_ENDPOINT, CF_ACCESS_CLIENT_ID, and CF_ACCESS_CLIENT_SECRET in your .env file or config.`),e.exit(1));let s=t.split(`/`),c=s[s.length-1],l=s[s.length-2],h=d(`${s[s.length-3]}-${l}-${c}`,`yyyy-MM-dd`,new Date);h=f(h,11),h=p(h,0),h=m(h,0);let g=h.getTime(),v={content:o.trim(),scheduleAt:g};try{let t=await fetch(n.scheduling.automationEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`,"CF-Access-Client-Id":n.scheduling.cfAccessClientId,"CF-Access-Client-Secret":n.scheduling.cfAccessClientSecret},body:JSON.stringify(v)});if(!t.ok){let n=await t.text();r.error(`Failed to schedule reminder: ${t.status} ${t.statusText}\n${n}`),e.exit(1)}let s=await t.json(),c={...a,scheduled:!0};b(i,w.stringify(o,c),`utf-8`);let l=new Date(s.scheduledAt);r.success(`✓ Reminder scheduled successfully!`),r.info(`Scheduled for: ${u(l,`yyyy-MM-dd HH:mm:ss`)} UTC`)}catch(t){r.error(`Failed to schedule reminder: ${t instanceof Error?t.message:String(t)}`),e.exit(1)}}async function re(){let t=new Date,i=[];for(let e=0;e<7;e++){let n=l(t,e),r=u(n,`yyyy-MM-dd`),a=u(n,`EEEE`),o=r,s=`Create content for ${a}, ${r}`;e===0?(o=`Today - ${a} (${r})`,s=`Create content for today (${a})`):e===1?(o=`Tomorrow - ${a} (${r})`,s=`Create content for tomorrow (${a})`):(o=`In ${e} days - ${a} (${r})`,s=`Create content for ${a}, ${r}`),i.push({label:o,value:`day-${e}`,hint:s})}i.push({label:`Custom date`,value:`custom`,hint:`Enter a specific date`});let a=await o({message:`When do you want to create content?`,options:i});if(n(a)&&(r.error(`Operation cancelled.`),e.exit(0)),a!==`custom`)return l(t,Number.parseInt(a.split(`-`)[1]));let c=await s({message:`Enter date (YYYY-MM-DD):`,placeholder:u(t,`yyyy-MM-dd`),validate:e=>{if(!e)return`Date is required`;if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return`Invalid date format. Please use YYYY-MM-DD`;try{let t=d(e,`yyyy-MM-dd`,new Date);if(Number.isNaN(t.getTime()))return`Invalid date`}catch{return`Invalid date`}}});return n(c)&&(r.error(`Operation cancelled.`),e.exit(0)),d(c,`yyyy-MM-dd`,new Date)}async function Q(){let t=await s({message:`What is the title of your content?`,placeholder:`Enter content title`,validate:e=>{if(!e||e.trim().length===0)return`Title is required`}});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t.trim()}async function ie(){let t=await o({message:`Is a LinkedIn video planned?`,options:[{label:`Yes`,value:!0,hint:`Add video: true to frontmatter`},{label:`No`,value:!1,hint:`No video metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function ae(){let t=await o({message:`Are there images?`,options:[{label:`Yes`,value:!0,hint:`Add images section to frontmatter`},{label:`No`,value:!1,hint:`No images metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function oe(){let t=await o({message:`What type of resource is this?`,options:[{label:`Article`,value:`article`,hint:`Create article.md`},{label:`Video`,value:`video`,hint:`Create video.md`},{label:`Audio`,value:`audio`,hint:`Create audio.md`},{label:`Tweet`,value:`tweet`,hint:`Create tweet.md`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function se(){let t=await i({message:`Which content types do you want to create?`,options:[{label:`LinkedIn`,value:`linkedin`,hint:`Create LinkedIn post and optional script`},{label:`X (Twitter)`,value:`x`,hint:`Create X post`},{label:`YouTube`,value:`youtube`,hint:`Create YouTube script and description`},{label:`Instagram`,value:`instagram`,hint:`Create Instagram script and description`}],required:!0});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function ce(t){let i=[];h(t)||(r.error(`Base path does not exist: ${t}`),e.exit(1));let a=v(t).filter(e=>y(S(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let e of a){let n=S(t,e),r=v(n).filter(e=>y(S(n,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of r){let r=S(n,t),a=v(r).filter(e=>y(S(r,e)).isDirectory()&&/^\d{2}$/.test(e));for(let n of a){let a=S(r,n);if(h(S(a,`x.md`))){let r=`${e}-${t}-${n}`;i.push({path:a,date:r,title:``})}}}}i.length===0&&(r.error(`No x.md files found in dated folders`),e.exit(1)),i.sort((e,t)=>t.date.localeCompare(e.date));let s=await o({message:`Which content do you want to schedule a reminder for?`,options:i.map(e=>({label:e.date,value:e.path,hint:`Schedule reminder for ${e.date}`}))});return n(s)&&(r.error(`Operation cancelled.`),e.exit(0)),s}const $=c(`content-creation`);$.command(`[path]`,`Create a dated content directory with content files`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create dated content directory`);let i=await M(),o=await Q(),s=await se(),c={title:o};s.includes(`linkedin`)&&(c.hasVideo=await ie(),c.hasImages=await ae());let l=await re();F(l,s,c,i,n||r?.path||e.cwd());let d=s.join(`, `);a(`✓ Content directory created: ${u(l,`yyyy/MM/dd`)} (${d})`)}),$.command(`link-images [path]`,`Link images to LinkedIn frontmatter in recent folders`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Link Images to LinkedIn`),await X(n||r?.path||e.cwd()),a(`✓ Images linked successfully`)}),$.command(`resource [path]`,`Create a resource with article/video/audio markdown file`).option(`--path <path>`,`Base path for resource directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create Resource`);let i=await Q(),o=await oe();G(i,o,n||r?.path||e.cwd()),a(`✓ Resource created: resources/${W(i)}/${o}.md`)}),$.command(`create-index [path]`,`Create index files for each content type`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create Index Files`),ne(n||r?.path||e.cwd()),a(`✓ Index files generated successfully`)}),$.command(`schedule-reminder [path]`,`Schedule a reminder for X content`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Schedule X Reminder`);let i=await M();await Z(await ce(n||r?.path||e.cwd()),i),a(`✓ Reminder scheduled successfully`)}),$.help(),$.version(te),$.parse();export{};
|
|
2
|
+
import e from"node:process";import{intro as t,isCancel as n,log as r,multiselect as i,outro as a,select as o,text as s}from"@clack/prompts";import{cac as c}from"cac";import{addDays as l,format as u,parse as d,setHours as f,setMinutes as p,setSeconds as m}from"date-fns";import{existsSync as h,mkdirSync as g,readFileSync as _,readdirSync as v,statSync as y,writeFileSync as b}from"node:fs";import{dirname as ee,isAbsolute as x,join as S,resolve as C}from"node:path";import w from"gray-matter";import{homedir as T}from"node:os";import{loadConfig as E}from"c12";var te=`0.15.2`;const D={linkedin:{mainFile:`linkedin.md`,additionalFiles:[`linkedin-script.md`]},x:{mainFile:`x.md`},youtube:{mainFile:`youtube-script.md`,additionalFiles:[`youtube-description.md`]},instagram:{mainFile:`instagram-script.md`,additionalFiles:[`instagram-description.md`]}},O={thematic:[],templatesDir:void 0,templates:{},scheduling:{}};function k(t,n){return t?x(t)?t:C(T(),t):n?ee(n):e.cwd()}function A(e,t){let n={};if(e?.footerPath){let r=C(t,e.footerPath);h(r)&&(n.footerPath=r)}return n}function j(e,t){let n={};if(e?.templatePath){let r=C(t,e.templatePath);h(r)&&(n.templatePath=r)}return n}async function M(){let{config:t,configFile:n}=await E({name:`content-creation`,defaults:O,globalRc:!0,dotenv:!0}),r=k(t.templatesDir,n),i={linkedin:A(t.templates?.linkedin,r),youtube:j(t.templates?.youtube,r),instagram:j(t.templates?.instagram,r)},a={automationEndpoint:t.scheduling?.automationEndpoint||e.env.AUTOMATION_ENDPOINT,cfAccessClientId:t.scheduling?.cfAccessClientId||e.env.CF_ACCESS_CLIENT_ID,cfAccessClientSecret:t.scheduling?.cfAccessClientSecret||e.env.CF_ACCESS_CLIENT_SECRET};return{thematic:t.thematic||[],templatesDir:t.templatesDir||``,templates:i,scheduling:a}}function N(e){return h(e)?_(e,`utf-8`):``}function P(t,n,i,a,o=e.cwd()){let s=C(S(o,u(t,`yyyy`),u(t,`MM`),u(t,`dd`)));h(s)||(g(s,{recursive:!0}),r.success(`Created directory: ${s}`));for(let e of n)F(e,s,i,a);return s}function F(e,t,n,i){let a=D[e],o=S(t,a.mainFile);if(h(o)?r.info(`${a.mainFile} already exists, skipping`):I(e,o,n,i),a.additionalFiles)for(let o of a.additionalFiles){let a=S(t,o);R(o,e,n)&&(h(a)?r.info(`${o} already exists, skipping`):L(e,a,o,i))}}function I(e,t,n,i){let a={title:n.title};e===`linkedin`&&(n.hasVideo&&(a.video=!0),n.hasImages&&(a.images=[null]));let o=``;if(e===`linkedin`){let e=i.templates.linkedin?.footerPath;o=e?N(e):``}b(t,w.stringify(o,a),`utf-8`),r.success(`Created ${t}`)}function L(e,t,n,i){let a=``;if(n.endsWith(`-description.md`)){let t=i.templates[e]?.templatePath;t&&(a=N(t))}b(t,a,`utf-8`),r.success(`Created ${t}`)}function R(e,t,n){return t===`linkedin`&&e===`linkedin-script.md`?n.hasVideo||!1:!0}function z(e,t){let n=[],i=D[t].mainFile;try{let t=v(e).filter(t=>y(S(e,t)).isDirectory()&&/^\d{4}$/.test(t));for(let a of t){let t=S(e,a),o=v(t).filter(e=>y(S(t,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of o){let o=S(t,e),s=v(o).filter(e=>y(S(o,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of s){let s=S(S(o,t),i);if(h(s)){let o=`${a}-${e}-${t}`,c=new Date(o);try{let{data:r}=w(_(s,`utf-8`)),l=r.title||`Untitled (${o})`;n.push({path:s,date:c,title:l,relativePath:`${a}/${e}/${t}/${i}`})}catch(e){r.warn(`Error reading ${s}: ${e}`)}}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime())}function B(e){switch(e){case`linkedin`:return`linkedin-posts.md`;case`x`:return`x-posts.md`;case`youtube`:return`youtube-videos.md`;case`instagram`:return`instagram-posts.md`}}function V(e){switch(e){case`linkedin`:return`LinkedIn Posts`;case`x`:return`X Posts`;case`youtube`:return`YouTube Videos`;case`instagram`:return`Instagram Posts`}}function H(e,t){let n=z(e,t);if(n.length===0){r.info(`No ${t} posts found`);return}let i=V(t),a=B(t),o=`# ${i}\n\n`;o+=`_Generated on ${new Date().toLocaleDateString()}_\n\n`,o+=`Total posts: ${n.length}\n\n`;for(let e of n){let t=e.date.toLocaleDateString(`en-US`,{year:`numeric`,month:`long`,day:`numeric`});o+=`- [${e.title}](${e.relativePath}) - _${t}_\n`}b(S(e,a),o,`utf-8`),r.success(`Created ${a} with ${n.length} posts`)}function U(t=e.cwd(),n){let r=n||[`linkedin`,`x`,`youtube`,`instagram`];for(let e of r)H(t,e)}function W(e){return e.toLowerCase().trim().normalize(`NFD`).replace(/[\u0300-\u036F]/g,``).replace(/[^a-z0-9\s-]/g,``).replace(/\s+/g,`-`).replace(/-+/g,`-`).replace(/^-+|-+$/g,``)||`untitled`}function G(t,n,i=e.cwd()){let a=W(t),o=S(C(S(i,`resources`)),a),s=`${n}.md`,c=S(o,s);if(h(c))return r.info(`${s} already exists at: ${c}`),o;g(o,{recursive:!0}),r.success(`Created directory: ${o}`);let l={title:t,url:``,date:``};return b(c,w.stringify(``,l),`utf-8`),r.success(`Created ${s} at: ${c}`),o}function K(t=e.cwd()){let n=[];try{let e=v(t).filter(e=>y(S(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let r of e){let e=S(t,r),i=v(e).filter(t=>y(S(e,t)).isDirectory()&&/^\d{2}$/.test(t));for(let t of i){let i=S(e,t),a=v(i).filter(e=>y(S(i,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of a){let a=S(i,e);if(h(S(a,`linkedin.md`))){let i=`${r}-${t}-${e}`;n.push({path:a,date:new Date(i),displayPath:`${r}/${t}/${e}`})}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>t.date.getTime()-e.date.getTime()).slice(0,8)}function q(e){let t=[`.jpg`,`.jpeg`,`.png`],n=[];try{let r=v(e);for(let i of r)if(y(S(e,i)).isFile()){let e=i.toLowerCase().substring(i.lastIndexOf(`.`));t.includes(e)&&n.push(i)}}catch(e){r.error(`Error reading directory: ${e}`)}return n.sort()}function J(e,t){try{let{data:n,content:i}=w(_(e,`utf-8`));n.images=t.length>0?t:[null],b(e,w.stringify(i,n),`utf-8`),r.success(`Updated ${e} with ${t.length} image(s)`)}catch(e){r.error(`Error updating frontmatter: ${e}`)}}async function Y(t){if(t.length===0)return r.warn(`No dated folders with linkedin.md found`),null;let i=await o({message:`Select a folder to link images:`,options:t.map(e=>({label:e.displayPath,value:e.path,hint:`Link images in ${e.displayPath}`}))});return n(i)&&(r.error(`Operation cancelled.`),e.exit(0)),t.find(e=>e.path===i)||null}async function X(t=e.cwd()){let n=K(t);if(n.length===0){r.warn(`No dated folders with linkedin.md found`);return}let i=await Y(n);if(!i)return;let a=q(i.path);if(a.length===0){r.info(`No images found in ${i.displayPath}`),J(S(i.path,`linkedin.md`),[]);return}r.info(`Found ${a.length} image(s): ${a.join(`, `)}`),J(S(i.path,`linkedin.md`),a)}async function Z(t,n){let i=S(t,`x.md`),{data:a,content:o}=w(_(i,`utf-8`));a.scheduled===!0&&(r.warn(`This content is already scheduled. Skipping.`),e.exit(0)),(!n.scheduling.automationEndpoint||!n.scheduling.cfAccessClientId||!n.scheduling.cfAccessClientSecret)&&(r.error(`Scheduling configuration is missing. Please set AUTOMATION_ENDPOINT, CF_ACCESS_CLIENT_ID, and CF_ACCESS_CLIENT_SECRET in your .env file or config.`),e.exit(1));let s=t.split(`/`),c=s[s.length-1],l=s[s.length-2],h=d(`${s[s.length-3]}-${l}-${c}`,`yyyy-MM-dd`,new Date);h=f(h,11),h=p(h,0),h=m(h,0);let g=h.getTime(),v={content:o.trim(),scheduleAt:g};try{let t=await fetch(n.scheduling.automationEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`,"CF-Access-Client-Id":n.scheduling.cfAccessClientId,"CF-Access-Client-Secret":n.scheduling.cfAccessClientSecret},body:JSON.stringify(v)});if(!t.ok){let n=await t.text();r.error(`Failed to schedule reminder: ${t.status} ${t.statusText}\n${n}`),e.exit(1)}let s=await t.json(),c={...a,scheduled:!0};b(i,w.stringify(o,c),`utf-8`);let l=new Date(s.scheduledAt);r.success(`✓ Reminder scheduled successfully!`),r.info(`Scheduled for: ${u(l,`yyyy-MM-dd HH:mm:ss`)} UTC`)}catch(t){r.error(`Failed to schedule reminder: ${t instanceof Error?t.message:String(t)}`),e.exit(1)}}async function ne(){let t=new Date,i=[];for(let e=0;e<7;e++){let n=l(t,e),r=u(n,`yyyy-MM-dd`),a=u(n,`EEEE`),o=r,s=`Create content for ${a}, ${r}`;e===0?(o=`Today - ${a} (${r})`,s=`Create content for today (${a})`):e===1?(o=`Tomorrow - ${a} (${r})`,s=`Create content for tomorrow (${a})`):(o=`In ${e} days - ${a} (${r})`,s=`Create content for ${a}, ${r}`),i.push({label:o,value:`day-${e}`,hint:s})}i.push({label:`Custom date`,value:`custom`,hint:`Enter a specific date`});let a=await o({message:`When do you want to create content?`,options:i});if(n(a)&&(r.error(`Operation cancelled.`),e.exit(0)),a!==`custom`)return l(t,Number.parseInt(a.split(`-`)[1]));let c=await s({message:`Enter date (YYYY-MM-DD):`,placeholder:u(t,`yyyy-MM-dd`),validate:e=>{if(!e)return`Date is required`;if(!/^\d{4}-\d{2}-\d{2}$/.test(e))return`Invalid date format. Please use YYYY-MM-DD`;try{let t=d(e,`yyyy-MM-dd`,new Date);if(Number.isNaN(t.getTime()))return`Invalid date`}catch{return`Invalid date`}}});return n(c)&&(r.error(`Operation cancelled.`),e.exit(0)),d(c,`yyyy-MM-dd`,new Date)}async function Q(){let t=await s({message:`What is the title of your content?`,placeholder:`Enter content title`,validate:e=>{if(!e||e.trim().length===0)return`Title is required`}});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t.trim()}async function re(){let t=await o({message:`Is a LinkedIn video planned?`,options:[{label:`Yes`,value:!0,hint:`Add video: true to frontmatter`},{label:`No`,value:!1,hint:`No video metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function ie(){let t=await o({message:`Are there images?`,options:[{label:`Yes`,value:!0,hint:`Add images section to frontmatter`},{label:`No`,value:!1,hint:`No images metadata`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function ae(){let t=await o({message:`What type of resource is this?`,options:[{label:`Article`,value:`article`,hint:`Create article.md`},{label:`Video`,value:`video`,hint:`Create video.md`},{label:`Audio`,value:`audio`,hint:`Create audio.md`},{label:`Tweet`,value:`tweet`,hint:`Create tweet.md`}]});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function oe(){let t=await i({message:`Which content types do you want to create?`,options:[{label:`LinkedIn`,value:`linkedin`,hint:`Create LinkedIn post and optional script`},{label:`X (Twitter)`,value:`x`,hint:`Create X post`},{label:`YouTube`,value:`youtube`,hint:`Create YouTube script and description`},{label:`Instagram`,value:`instagram`,hint:`Create Instagram script and description`}],required:!0});return n(t)&&(r.error(`Operation cancelled.`),e.exit(0)),t}async function se(t){let i=[];h(t)||(r.error(`Base path does not exist: ${t}`),e.exit(1));let a=v(t).filter(e=>y(S(t,e)).isDirectory()&&/^\d{4}$/.test(e));for(let e of a){let n=S(t,e),r=v(n).filter(e=>y(S(n,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of r){let r=S(n,t),a=v(r).filter(e=>y(S(r,e)).isDirectory()&&/^\d{2}$/.test(e));for(let n of a){let a=S(r,n);if(h(S(a,`x.md`))){let r=`${e}-${t}-${n}`;i.push({path:a,date:r,title:``})}}}}i.length===0&&(r.error(`No x.md files found in dated folders`),e.exit(1)),i.sort((e,t)=>t.date.localeCompare(e.date));let s=await o({message:`Which content do you want to schedule a reminder for?`,options:i.map(e=>({label:e.date,value:e.path,hint:`Schedule reminder for ${e.date}`}))});return n(s)&&(r.error(`Operation cancelled.`),e.exit(0)),s}const $=c(`content-creation`);$.command(`[path]`,`Create a dated content directory with content files`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create dated content directory`);let i=await M(),o=await Q(),s=await oe(),c={title:o};s.includes(`linkedin`)&&(c.hasVideo=await re(),c.hasImages=await ie());let l=await ne();P(l,s,c,i,n||r?.path||e.cwd());let d=s.join(`, `);a(`✓ Content directory created: ${u(l,`yyyy/MM/dd`)} (${d})`)}),$.command(`link-images [path]`,`Link images to LinkedIn frontmatter in recent folders`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Link Images to LinkedIn`),await X(n||r?.path||e.cwd()),a(`✓ Images linked successfully`)}),$.command(`resource [path]`,`Create a resource with article/video/audio markdown file`).option(`--path <path>`,`Base path for resource directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create Resource`);let i=await Q(),o=await ae();G(i,o,n||r?.path||e.cwd()),a(`✓ Resource created: resources/${W(i)}/${o}.md`)}),$.command(`create-index [path]`,`Create index files for each content type`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Create Index Files`),U(n||r?.path||e.cwd()),a(`✓ Index files generated successfully`)}),$.command(`schedule-reminder [path]`,`Schedule a reminder for X content`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(async(n,r)=>{t(`Content Creation - Schedule X Reminder`);let i=await M();await Z(await se(n||r?.path||e.cwd()),i),a(`✓ Reminder scheduled successfully`)}),$.help(),$.version(te),$.parse();export{};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Template configuration for LinkedIn
|
|
5
|
+
*/
|
|
6
|
+
interface LinkedInTemplateConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Path to a template file for the footer (resolved relative to templatesDir or config file location)
|
|
9
|
+
*/
|
|
10
|
+
footerPath?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Template configuration for YouTube/Instagram
|
|
14
|
+
*/
|
|
15
|
+
interface VideoTemplateConfig {
|
|
16
|
+
/**
|
|
17
|
+
* Path to a template file for the description blueprint (resolved relative to templatesDir or config file location)
|
|
18
|
+
*/
|
|
19
|
+
templatePath?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Scheduling configuration
|
|
23
|
+
*/
|
|
24
|
+
interface SchedulingConfig {
|
|
25
|
+
/**
|
|
26
|
+
* Automation endpoint URL
|
|
27
|
+
*/
|
|
28
|
+
automationEndpoint?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Cloudflare Access Client ID
|
|
31
|
+
*/
|
|
32
|
+
cfAccessClientId?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Cloudflare Access Client Secret
|
|
35
|
+
*/
|
|
36
|
+
cfAccessClientSecret?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Configuration for content-creation
|
|
40
|
+
*/
|
|
41
|
+
interface Config {
|
|
42
|
+
/**
|
|
43
|
+
* List of thematic areas
|
|
44
|
+
* @default []
|
|
45
|
+
*/
|
|
46
|
+
thematic?: string[];
|
|
47
|
+
/**
|
|
48
|
+
* Base directory for external template files
|
|
49
|
+
*/
|
|
50
|
+
templatesDir?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Templates configuration per content type
|
|
53
|
+
*/
|
|
54
|
+
templates?: {
|
|
55
|
+
linkedin?: LinkedInTemplateConfig;
|
|
56
|
+
youtube?: VideoTemplateConfig;
|
|
57
|
+
instagram?: VideoTemplateConfig;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Scheduling configuration
|
|
61
|
+
*/
|
|
62
|
+
scheduling?: SchedulingConfig;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/index.d.ts
|
|
66
|
+
declare function defineConfig(config: Config): Config;
|
|
67
|
+
//#endregion
|
|
68
|
+
export { defineConfig };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return e}export{e as defineConfig};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barbapapazes/content-creation",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.15.
|
|
4
|
+
"version": "0.15.2",
|
|
5
5
|
"author": "Estéban Soubiran <esteban@soubiran.dev>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"funding": "https://github.com/sponsors/Barbapapazes",
|
|
@@ -12,6 +12,14 @@
|
|
|
12
12
|
"directory": "packages/@barbapapazes/content-creation"
|
|
13
13
|
},
|
|
14
14
|
"bugs": "https://github.com/Barbapapazes/platform/issues",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.mts",
|
|
18
|
+
"import": "./dist/index.mjs"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"main": ".dist/index.mjs",
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
15
23
|
"bin": {
|
|
16
24
|
"content-creation": "./dist/cli.mjs"
|
|
17
25
|
},
|