@barbapapazes/content-creation 0.18.6 → 0.18.7
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/README.md +74 -20
- package/dist/cli.mjs +6 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ A CLI tool to streamline multi-platform content creation by generating dated dir
|
|
|
11
11
|
- Config-driven templates for each content type
|
|
12
12
|
- Never overwrites existing files (skip if exists behavior)
|
|
13
13
|
- Generate separate index files per content type
|
|
14
|
+
- Manage article series with generated README indexes and ordered inserts
|
|
14
15
|
- List upcoming content from today onward with absolute file paths
|
|
15
16
|
- Generate and upload a LinkedIn publication calendar for Google Calendar subscriptions
|
|
16
17
|
- Link images to LinkedIn post frontmatter
|
|
@@ -25,6 +26,15 @@ npm install -g @barbapapazes/content-creation
|
|
|
25
26
|
|
|
26
27
|
## Usage
|
|
27
28
|
|
|
29
|
+
Several workflows are now grouped under domain-oriented commands for better discoverability:
|
|
30
|
+
|
|
31
|
+
- `content-creation index ...`
|
|
32
|
+
- `content-creation series ...`
|
|
33
|
+
- `content-creation linkedin ...`
|
|
34
|
+
- `content-creation x ...`
|
|
35
|
+
- `content-creation publication ...`
|
|
36
|
+
- `content-creation resource ...`
|
|
37
|
+
|
|
28
38
|
### Create Content Directory
|
|
29
39
|
|
|
30
40
|
Create a dated directory with content files for selected platforms:
|
|
@@ -65,10 +75,10 @@ Generate index files for each content type found in your dated folders:
|
|
|
65
75
|
|
|
66
76
|
```bash
|
|
67
77
|
# Generate index files in current directory
|
|
68
|
-
content-creation create
|
|
78
|
+
content-creation index create
|
|
69
79
|
|
|
70
80
|
# Generate index files at a specific path
|
|
71
|
-
content-creation
|
|
81
|
+
content-creation index create --path /path/to/content
|
|
72
82
|
```
|
|
73
83
|
|
|
74
84
|
This command scans your `YYYY/MM/DD` directory structure and creates:
|
|
@@ -97,11 +107,10 @@ Total posts: 15
|
|
|
97
107
|
List all content scheduled for today or later:
|
|
98
108
|
|
|
99
109
|
```bash
|
|
100
|
-
|
|
101
|
-
content-creation list-upcoming
|
|
110
|
+
content-creation publication list-upcoming
|
|
102
111
|
|
|
103
112
|
# List upcoming content at a specific path
|
|
104
|
-
content-creation list-upcoming --path /path/to/content
|
|
113
|
+
content-creation publication list-upcoming --path /path/to/content
|
|
105
114
|
```
|
|
106
115
|
|
|
107
116
|
This command scans your `YYYY/MM/DD` directory structure, reads the content title from frontmatter, and prints each matching content item with its absolute file path so it is easy to click from the terminal.
|
|
@@ -116,16 +125,64 @@ Example output:
|
|
|
116
125
|
/absolute/path/to/content/2026/04/20/x.md
|
|
117
126
|
```
|
|
118
127
|
|
|
128
|
+
### Manage Series
|
|
129
|
+
|
|
130
|
+
Work with article series stored under `series/<series-slug>/`.
|
|
131
|
+
|
|
132
|
+
Each series directory can contain:
|
|
133
|
+
|
|
134
|
+
- a `README.md` file with the editorial context for the series;
|
|
135
|
+
- numbered markdown files such as `1.introduction.md`, `2.my-next-article.md`, etc.
|
|
136
|
+
|
|
137
|
+
#### Generate the series index
|
|
138
|
+
|
|
139
|
+
Refresh the generated `## Index` section in each series `README.md` based on the numbered article files:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Generate indexes for all series in the current directory
|
|
143
|
+
content-creation series create-index
|
|
144
|
+
|
|
145
|
+
# Generate the index for a specific series only
|
|
146
|
+
content-creation series create-index --series my-series-slug
|
|
147
|
+
|
|
148
|
+
# Generate series indexes from another base path
|
|
149
|
+
content-creation series create-index --path /path/to/content
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The command preserves the existing `README.md` content and only manages the generated `## Index` section.
|
|
153
|
+
|
|
154
|
+
#### Introduce a new article in a series
|
|
155
|
+
|
|
156
|
+
Insert a new article at a specific position in the series:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Interactive mode
|
|
160
|
+
content-creation series introduce
|
|
161
|
+
|
|
162
|
+
# Fully scripted mode
|
|
163
|
+
content-creation series introduce \
|
|
164
|
+
--series my-series-slug \
|
|
165
|
+
--title "What tool calling really changes" \
|
|
166
|
+
--position 6
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This command will:
|
|
170
|
+
|
|
171
|
+
1. create a new markdown file with frontmatter containing the title;
|
|
172
|
+
2. rename subsequent numbered files to make room for the new article;
|
|
173
|
+
3. regenerate the series `README.md` index automatically.
|
|
174
|
+
|
|
175
|
+
Generated article files follow the pattern `<index>.<slug>.md`.
|
|
176
|
+
|
|
119
177
|
### Publish LinkedIn Calendar
|
|
120
178
|
|
|
121
179
|
Generate a Google Calendar-compatible `.ics` file for LinkedIn publications, upload it to Cloudflare R2 using Wrangler, and print the subscription URL:
|
|
122
180
|
|
|
123
181
|
```bash
|
|
124
|
-
|
|
125
|
-
content-creation publish-linkedin-calendar
|
|
182
|
+
content-creation linkedin publish-calendar
|
|
126
183
|
|
|
127
184
|
# Publish the LinkedIn calendar from a specific content directory
|
|
128
|
-
content-creation publish-
|
|
185
|
+
content-creation linkedin publish-calendar --path /path/to/content
|
|
129
186
|
```
|
|
130
187
|
|
|
131
188
|
This command will:
|
|
@@ -155,11 +212,10 @@ https://calendar.soubiran.dev/content-creation.ics?token=<your-calendar-token>
|
|
|
155
212
|
Scan recent LinkedIn posts and link images to their frontmatter:
|
|
156
213
|
|
|
157
214
|
```bash
|
|
158
|
-
|
|
159
|
-
content-creation link-images
|
|
215
|
+
content-creation linkedin link-images
|
|
160
216
|
|
|
161
217
|
# Link images at a specific path
|
|
162
|
-
content-creation link-images --path /path/to/content
|
|
218
|
+
content-creation linkedin link-images --path /path/to/content
|
|
163
219
|
```
|
|
164
220
|
|
|
165
221
|
### Create Resource
|
|
@@ -167,11 +223,11 @@ content-creation link-images --path /path/to/content
|
|
|
167
223
|
Create a resource directory with article, video, or audio content:
|
|
168
224
|
|
|
169
225
|
```bash
|
|
170
|
-
#
|
|
171
|
-
content-creation resource
|
|
226
|
+
# Preferred grouped command
|
|
227
|
+
content-creation resource create
|
|
172
228
|
|
|
173
229
|
# Create resource at a specific path
|
|
174
|
-
content-creation resource --path /path/to/resources
|
|
230
|
+
content-creation resource create --path /path/to/resources
|
|
175
231
|
```
|
|
176
232
|
|
|
177
233
|
### Schedule Reminder
|
|
@@ -179,11 +235,10 @@ content-creation resource --path /path/to/resources
|
|
|
179
235
|
Schedule a reminder for X (Twitter) content to be posted at 11:00 AM UTC on the date specified in the folder path:
|
|
180
236
|
|
|
181
237
|
```bash
|
|
182
|
-
|
|
183
|
-
content-creation schedule-reminder
|
|
238
|
+
content-creation x schedule-reminder
|
|
184
239
|
|
|
185
240
|
# Schedule reminder at a specific path
|
|
186
|
-
content-creation schedule-reminder --path /path/to/content
|
|
241
|
+
content-creation x schedule-reminder --path /path/to/content
|
|
187
242
|
```
|
|
188
243
|
|
|
189
244
|
The command will:
|
|
@@ -211,11 +266,10 @@ See `.env.example` for a template.
|
|
|
211
266
|
Mark a publication as ready by setting `ready: true` in the selected markdown file frontmatter:
|
|
212
267
|
|
|
213
268
|
```bash
|
|
214
|
-
|
|
215
|
-
content-creation ready
|
|
269
|
+
content-creation publication ready
|
|
216
270
|
|
|
217
271
|
# Mark a publication as ready from a specific content directory
|
|
218
|
-
content-creation ready --path /path/to/content
|
|
272
|
+
content-creation publication ready --path /path/to/content
|
|
219
273
|
```
|
|
220
274
|
|
|
221
275
|
This command will:
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
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,parseISO as d,setHours as f,setMinutes as ee,setSeconds as p}from"date-fns";import{existsSync as m,mkdirSync as h,readFileSync as g,readdirSync as _,statSync as v,writeFileSync as y}from"node:fs";import{dirname as b,isAbsolute as te,join as x,resolve as S}from"node:path";import C from"gray-matter";import{loadConfig as ne}from"c12";import{execFileSync as w}from"node:child_process";import{createHash as T}from"node:crypto";import{fileURLToPath as E}from"node:url";var D=`0.18.6`;const O={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`]}},re={thematic:[],templatesDir:void 0,templates:{},scheduling:{},calendar:{}};function ie(t,n){return t?te(t)?t:S(n?b(n):e.cwd(),t):n?b(n):e.cwd()}function ae(e,t){let n={};if(e?.footerPath){let r=S(t,e.footerPath);m(r)&&(n.footerPath=r)}return n}function k(e,t){let n={};if(e?.templatePath){let r=S(t,e.templatePath);m(r)&&(n.templatePath=r)}return n}async function A(){let{config:t,configFile:n}=await ne({name:`content-creation`,defaults:re,globalRc:!0,dotenv:!0}),r=ie(t.templatesDir,n),i={linkedin:ae(t.templates?.linkedin,r),youtube:k(t.templates?.youtube,r),instagram:k(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},o={publicUrl:t.calendar?.publicUrl||e.env.CALENDAR_PUBLIC_URL,token:t.calendar?.token||e.env.CALENDAR_TOKEN};return{thematic:t.thematic||[],templatesDir:t.templatesDir||``,templates:i,scheduling:a,calendar:o}}function j(e){return m(e)?g(e,`utf-8`):``}function oe(t,n,i,a,o=e.cwd()){let s=S(x(o,u(t,`yyyy`),u(t,`MM`),u(t,`dd`)));m(s)||(h(s,{recursive:!0}),r.success(`Created directory: ${s}`));let c=se(i);for(let e of n)ce(e,s,c,a);return s}function se(e){return e.hasVideo?{...e,hasImages:!1}:{...e,hasImages:!0}}function ce(e,t,n,i){let a=O[e],o=x(t,a.mainFile);if(m(o)?r.info(`${a.mainFile} already exists, skipping`):le(e,o,n,i),a.additionalFiles)for(let o of a.additionalFiles){let a=x(t,o);M(o,e,n)&&(m(a)?r.info(`${o} already exists, skipping`):ue(e,a,o,i))}}function le(e,t,n,i){let a={title:n.title};e===`linkedin`&&(n.theme&&(a.theme=n.theme),n.hasVideo&&(a.video=!0),n.hasImages&&(a.images=[null]));let o=``;if(e===`linkedin`){let e=i.templates.linkedin?.footerPath;o=e?j(e):``}y(t,C.stringify(o,a),`utf-8`),r.success(`Created ${t}`)}function ue(e,t,n,i){let a=``;if(n.endsWith(`-description.md`)){let t=i.templates[e]?.templatePath;t&&(a=j(t))}y(t,a,`utf-8`),r.success(`Created ${t}`)}function M(e,t,n){return t===`linkedin`&&e===`linkedin-script.md`?n.hasVideo||!1:!0}const N=[`linkedin`,`x`,`youtube`,`instagram`],P={linkedin:`LinkedIn`,x:`X`,youtube:`YouTube`,instagram:`Instagram`};function F(e,t,n){let r=n===`date-asc`?e.date.getTime()-t.date.getTime():t.date.getTime()-e.date.getTime();if(r!==0)return r;let i=N.indexOf(e.contentType)-N.indexOf(t.contentType);return i===0?e.relativePath.localeCompare(t.relativePath):i}function I(e,t={}){let n=[],i=t.contentTypes?.length?t.contentTypes:[...N],a=t.fromDate,o=t.sort||`date-desc`;try{let t=_(e).filter(t=>v(x(e,t)).isDirectory()&&/^\d{4}$/.test(t));for(let o of t){let t=x(e,o),s=_(t).filter(e=>v(x(t,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of s){let s=x(t,e),c=_(s).filter(e=>v(x(s,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of c){let c=x(s,t),l=`${o}-${e}-${t}`,u=new Date(Number(o),Number(e)-1,Number(t));if(!(a&&u.getTime()<a.getTime()))for(let a of i){let i=O[a].mainFile,s=S(c,i);if(m(s))try{let{data:r}=C(g(s,`utf-8`)),d=r,f=d.title||`Untitled (${l})`;n.push({contentType:a,path:s,folderPath:c,date:u,title:f,relativePath:`${o}/${e}/${t}/${i}`,frontmatter:d})}catch(e){r.warn(`Error reading ${s}: ${e}`)}}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>F(e,t,o))}function L(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 R(e){switch(e){case`linkedin`:return`LinkedIn Posts`;case`x`:return`X Posts`;case`youtube`:return`YouTube Videos`;case`instagram`:return`Instagram Posts`}}function z(e,t){let n=I(e,{contentTypes:[t],sort:`date-desc`});if(n.length===0){r.info(`No ${t} posts found`);return}let i=R(t),a=L(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`}y(x(e,a),o,`utf-8`),r.success(`Created ${a} with ${n.length} posts`)}function B(t=e.cwd(),n){let r=n||[...N];for(let e of r)z(t,e)}function V(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 H(t,n,i=e.cwd()){let a=V(t),o=x(S(x(i,`resources`)),a),s=`${n}.md`,c=x(o,s);if(m(c))return r.info(`${s} already exists at: ${c}`),o;h(o,{recursive:!0}),r.success(`Created directory: ${o}`);let l={title:t,url:``,date:``};return y(c,C.stringify(``,l),`utf-8`),r.success(`Created ${s} at: ${c}`),o}const U=`load-more`;async function de(){let t=new Date,i=7;for(;;){let a=await o({message:`When do you want to create content?`,options:fe(t,i)});if(n(a)&&(r.error(`Operation cancelled.`),e.exit(0)),a===`load-more`){i+=7;continue}return d(a)}}function fe(e,t){let n=[];for(let r=0;r<t;r++){let t=l(e,r),i=u(t,`yyyy-MM-dd`),a=u(t,`EEEE`),o=i,s=`Create content for ${a}, ${i}`;r===0?(o=`Today - ${a} (${i})`,s=`Create content for today (${a})`):r===1?(o=`Tomorrow - ${a} (${i})`,s=`Create content for tomorrow (${a})`):(o=`In ${r} days - ${a} (${i})`,s=`Create content for ${a}, ${i}`),n.push({label:o,value:i,hint:s})}return n.push({label:`Show 7 more dates (${t+1}-${t+7} days ahead)`,value:`load-more`,hint:`Extend the list up to ${u(l(e,t+6),`yyyy-MM-dd`)}`}),n}async function W(){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 pe(){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 me(t){if(t.length===0)return;let i=await o({message:`What is the content theme? (optional)`,options:[{label:`No theme`,value:``,hint:`Leave theme empty`},...t.map(e=>({label:e,value:e,hint:`Use theme: ${e}`}))]});n(i)&&(r.error(`Operation cancelled.`),e.exit(0));let a=i;return a.length>0?a:void 0}async function he(){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 ge(){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 _e(e){return G({basePath:e,contentTypes:[`x`],message:`Which X publication do you want to schedule a reminder for?`,emptyMessage:`No X publications found in dated folders`,sort:`date-desc`})}async function ve(e){return G({basePath:e,contentTypes:[`linkedin`],message:`Which LinkedIn publication do you want to link images for?`,emptyMessage:`No LinkedIn publications found in dated folders`,sort:`date-desc`})}async function ye(e){return G({basePath:e,contentTypes:[...N],message:`Which publication do you want to mark as ready?`,emptyMessage:`No publications found in dated folders`,sort:`date-desc`})}async function G(t){let i=I(t.basePath,{contentTypes:t.contentTypes,sort:t.sort});i.length===0&&(r.error(t.emptyMessage),e.exit(1));let a=20;for(;;){let s=await o({message:t.message,options:be(i,a)});if(n(s)&&(r.error(`Operation cancelled.`),e.exit(0)),s===U){a+=20;continue}let c=i.find(e=>e.path===s);return c||(r.error(`Unable to resolve selected publication: ${s}`),e.exit(1)),c}}function be(e,t){let n=e.slice(0,t).map(e=>xe(e));if(t>=e.length)return n;let r=Math.min(t+20,e.length);return[...n,{label:`Show ${r-t} more publications (${t+1}-${r} of ${e.length})`,value:U,hint:`Extend the list to ${e[r-1]?.relativePath}`}]}function xe(e){return{label:`${u(e.date,`yyyy-MM-dd`)} · ${P[e.contentType]} · ${e.title}`,value:e.path,hint:e.relativePath}}function Se(e){let t=[`.jpg`,`.jpeg`,`.png`],n=[];try{let r=_(e);for(let i of r)if(v(x(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 K(e,t){try{let{data:n,content:i}=C(g(e.path,`utf-8`));n.images=t.length>0?t:[null];let a=C.stringify(i,n);y(e.path,a,`utf-8`),r.success(`LinkedIn publication updated: ${e.relativePath} (${t.length} image(s))`)}catch(e){r.error(`Error updating frontmatter: ${e}`)}}async function Ce(t=e.cwd()){let n=await ve(t),i=Se(n.folderPath);if(i.length===0){r.info(`No images found for LinkedIn publication: ${n.relativePath}`),K(n,[]);return}r.info(`Found ${i.length} image(s) for ${n.relativePath}: ${i.join(`, `)}`),K(n,i)}function we(e){let t=new Date;t.setHours(0,0,0,0);let n=I(e,{fromDate:t,sort:`date-asc`});if(n.length===0){r.info(`No upcoming publications found from today onward.`);return}r.info(`Found ${n.length} upcoming publication${n.length===1?``:`s`}:`),console.log(``);for(let e of n){let t=u(e.date,`EEEE, MMMM d, yyyy`),n=u(e.date,`yyyy-MM-dd`);console.log(`${t} (${n}) · ${P[e.contentType]} · ${e.title}`),console.log(e.path),console.log(``)}}function Te(t){let n=t.path;try{let{data:e,content:i}=C(g(n,`utf-8`));if(e.ready===!0){r.warn(`This publication is already marked as ready.`);return}let a={...e,ready:!0};y(n,C.stringify(i,a),`utf-8`),r.success(`Publication marked as ready: ${t.relativePath}`)}catch(t){r.error(`Failed to mark publication as ready: ${t instanceof Error?t.message:String(t)}`),e.exit(1)}}const q=E(new URL(`../../`,import.meta.url)),J=`content-creation.ics`;function Y(e){return e.replace(/\\/g,`\\\\`).replace(/\r\n|\r|\n/g,`\\n`).replace(/;/g,`\\;`).replace(/,/g,`\\,`)}function Ee(e){if(e.length<=75)return e;let t=[],n=e;for(;n.length>75;)t.push(n.slice(0,75)),n=` ${n.slice(75)}`;return t.push(n),t.join(`\r
|
|
3
|
-
`)}function
|
|
4
|
-
`)}function
|
|
5
|
-
`)}
|
|
2
|
+
import{cac as e}from"cac";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{addDays as c,format as l,parseISO as u,setHours as d,setMinutes as f,setSeconds as ee}from"date-fns";import{existsSync as p,mkdirSync as m,readFileSync as h,readdirSync as g,renameSync as te,statSync as _,writeFileSync as v}from"node:fs";import{dirname as y,isAbsolute as ne,join as b,resolve as x}from"node:path";import S from"node:process";import C from"gray-matter";import{loadConfig as re}from"c12";import{execFileSync as ie}from"node:child_process";import{createHash as ae}from"node:crypto";import{fileURLToPath as oe}from"node:url";var se=`0.18.7`;const w={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`]}},ce={thematic:[],templatesDir:void 0,templates:{},scheduling:{},calendar:{}};function T(e,t){return e?ne(e)?e:x(t?y(t):S.cwd(),e):t?y(t):S.cwd()}function le(e,t){let n={};if(e?.footerPath){let r=x(t,e.footerPath);p(r)&&(n.footerPath=r)}return n}function E(e,t){let n={};if(e?.templatePath){let r=x(t,e.templatePath);p(r)&&(n.templatePath=r)}return n}async function D(){let{config:e,configFile:t}=await re({name:`content-creation`,defaults:ce,globalRc:!0,dotenv:!0}),n=T(e.templatesDir,t),r={linkedin:le(e.templates?.linkedin,n),youtube:E(e.templates?.youtube,n),instagram:E(e.templates?.instagram,n)},i={automationEndpoint:e.scheduling?.automationEndpoint||S.env.AUTOMATION_ENDPOINT,cfAccessClientId:e.scheduling?.cfAccessClientId||S.env.CF_ACCESS_CLIENT_ID,cfAccessClientSecret:e.scheduling?.cfAccessClientSecret||S.env.CF_ACCESS_CLIENT_SECRET},a={publicUrl:e.calendar?.publicUrl||S.env.CALENDAR_PUBLIC_URL,token:e.calendar?.token||S.env.CALENDAR_TOKEN};return{thematic:e.thematic||[],templatesDir:e.templatesDir||``,templates:r,scheduling:i,calendar:a}}function O(e){return p(e)?h(e,`utf-8`):``}function ue(e,t,n,i,a=S.cwd()){let o=x(b(a,l(e,`yyyy`),l(e,`MM`),l(e,`dd`)));p(o)||(m(o,{recursive:!0}),r.success(`Created directory: ${o}`));let s=de(n);for(let e of t)fe(e,o,s,i);return o}function de(e){return e.hasVideo?{...e,hasImages:!1}:{...e,hasImages:!0}}function fe(e,t,n,i){let a=w[e],o=b(t,a.mainFile);if(p(o)?r.info(`${a.mainFile} already exists, skipping`):pe(e,o,n,i),a.additionalFiles)for(let o of a.additionalFiles){let a=b(t,o);he(o,e,n)&&(p(a)?r.info(`${o} already exists, skipping`):me(e,a,o,i))}}function pe(e,t,n,i){let a={title:n.title};e===`linkedin`&&(n.theme&&(a.theme=n.theme),n.hasVideo&&(a.video=!0),n.hasImages&&(a.images=[null]));let o=``;if(e===`linkedin`){let e=i.templates.linkedin?.footerPath;o=e?O(e):``}v(t,C.stringify(o,a),`utf-8`),r.success(`Created ${t}`)}function me(e,t,n,i){let a=``;if(n.endsWith(`-description.md`)){let t=i.templates[e]?.templatePath;t&&(a=O(t))}v(t,a,`utf-8`),r.success(`Created ${t}`)}function he(e,t,n){return t===`linkedin`&&e===`linkedin-script.md`?n.hasVideo||!1:!0}const k=[`linkedin`,`x`,`youtube`,`instagram`],A={linkedin:`LinkedIn`,x:`X`,youtube:`YouTube`,instagram:`Instagram`};function ge(e,t,n){let r=n===`date-asc`?e.date.getTime()-t.date.getTime():t.date.getTime()-e.date.getTime();if(r!==0)return r;let i=k.indexOf(e.contentType)-k.indexOf(t.contentType);return i===0?e.relativePath.localeCompare(t.relativePath):i}function j(e,t={}){let n=[],i=t.contentTypes?.length?t.contentTypes:[...k],a=t.fromDate,o=t.sort||`date-desc`;try{let t=g(e).filter(t=>_(b(e,t)).isDirectory()&&/^\d{4}$/.test(t));for(let o of t){let t=b(e,o),s=g(t).filter(e=>_(b(t,e)).isDirectory()&&/^\d{2}$/.test(e));for(let e of s){let s=b(t,e),c=g(s).filter(e=>_(b(s,e)).isDirectory()&&/^\d{2}$/.test(e));for(let t of c){let c=b(s,t),l=`${o}-${e}-${t}`,u=new Date(Number(o),Number(e)-1,Number(t));if(!(a&&u.getTime()<a.getTime()))for(let a of i){let i=w[a].mainFile,s=x(c,i);if(p(s))try{let{data:r}=C(h(s,`utf-8`)),d=r,f=d.title||`Untitled (${l})`;n.push({contentType:a,path:s,folderPath:c,date:u,title:f,relativePath:`${o}/${e}/${t}/${i}`,frontmatter:d})}catch(e){r.warn(`Error reading ${s}: ${e}`)}}}}}}catch(e){return r.error(`Error reading directories: ${e}`),[]}return n.sort((e,t)=>ge(e,t,o))}const M=`## Index`,N=`<!-- content-creation:series-index:start -->`,P=`<!-- content-creation:series-index:end -->`;function _e(e){return x(b(e,`series`))}function F(e){let t=_e(e);return p(t)?g(t).filter(e=>_(b(t,e)).isDirectory()).map(e=>{let n=b(t,e);return{name:e,path:n,title:be(n,e)}}).sort((e,t)=>e.title.localeCompare(t.title,`fr`)):(r.error(`Series directory not found: ${t}`),[])}function I(e,t){return F(e).find(e=>e.name===t)}function L(e){return g(e).map(e=>Se(e)).filter(e=>e!==null).map(t=>({...t,path:b(e,t.fileName),title:xe(b(e,t.fileName),t.slug)})).sort((e,t)=>e.index-t.index)}function R(e){let t=L(e),n=b(e,`README.md`);v(n,ve(p(n)?h(n,`utf-8`):``,t,e),`utf-8`),r.success(`Generated series index: ${n}`)}function ve(e,t,n){let r=ye(t);if(e.includes(N)&&e.includes(P))return e.replace(RegExp(`${B(M)}\\n\\n${B(N)}[\\s\\S]*?${B(P)}`),r);let i=e.trimEnd();return i.length>0?`${i}\n\n${r}\n`:`# ${z(n.split(`/`).pop()||`series`)}\n\n${r}\n`}function ye(e){let t=[M,``,N];if(e.length===0)t.push(``,`_No articles yet._`);else{t.push(``);for(let n of e)t.push(`${n.index}. [${n.title}](./${n.fileName})`)}return t.push(``,P),t.join(`
|
|
3
|
+
`)}function be(e,t){let n=b(e,`README.md`);if(p(n)){let e=h(n,`utf-8`).split(`
|
|
4
|
+
`).map(e=>e.trim()).find(e=>e.startsWith(`# `));if(e)return e.slice(2).trim()}return z(t)}function xe(e,t){try{let{data:t}=C(h(e,`utf-8`)),n=typeof t.title==`string`?t.title.trim():``;if(n.length>0)return n}catch(t){r.warn(`Unable to read article title from ${e}: ${t}`)}return z(t)}function Se(e){let t=e.match(/^(\d+)\.([^.]+(?:\.[^.]+)*)\.md$/);return t?{index:Number(t[1]),slug:t[2],fileName:e}:null}function z(e){return e.split(`-`).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(` `)}function B(e){return e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}const V=`load-more`;async function Ce(){let e=new Date,t=7;for(;;){let i=await o({message:`When do you want to create content?`,options:we(e,t)});if(n(i)&&(r.error(`Operation cancelled.`),S.exit(0)),i===`load-more`){t+=7;continue}return u(i)}}function we(e,t){let n=[];for(let r=0;r<t;r++){let t=c(e,r),i=l(t,`yyyy-MM-dd`),a=l(t,`EEEE`),o=i,s=`Create content for ${a}, ${i}`;r===0?(o=`Today - ${a} (${i})`,s=`Create content for today (${a})`):r===1?(o=`Tomorrow - ${a} (${i})`,s=`Create content for tomorrow (${a})`):(o=`In ${r} days - ${a} (${i})`,s=`Create content for ${a}, ${i}`),n.push({label:o,value:i,hint:s})}return n.push({label:`Show 7 more dates (${t+1}-${t+7} days ahead)`,value:`load-more`,hint:`Extend the list up to ${l(c(e,t+6),`yyyy-MM-dd`)}`}),n}async function H(){let e=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(e)&&(r.error(`Operation cancelled.`),S.exit(0)),e.trim()}async function Te(){let e=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(e)&&(r.error(`Operation cancelled.`),S.exit(0)),e}async function Ee(e){if(e.length===0)return;let t=await o({message:`What is the content theme? (optional)`,options:[{label:`No theme`,value:``,hint:`Leave theme empty`},...e.map(e=>({label:e,value:e,hint:`Use theme: ${e}`}))]});n(t)&&(r.error(`Operation cancelled.`),S.exit(0));let i=t;return i.length>0?i:void 0}async function De(){let e=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(e)&&(r.error(`Operation cancelled.`),S.exit(0)),e}async function Oe(){let e=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(e)&&(r.error(`Operation cancelled.`),S.exit(0)),e}async function ke(e){return U({basePath:e,contentTypes:[`x`],message:`Which X publication do you want to schedule a reminder for?`,emptyMessage:`No X publications found in dated folders`,sort:`date-desc`})}async function Ae(e){return U({basePath:e,contentTypes:[`linkedin`],message:`Which LinkedIn publication do you want to link images for?`,emptyMessage:`No LinkedIn publications found in dated folders`,sort:`date-desc`})}async function je(e){return U({basePath:e,contentTypes:[...k],message:`Which publication do you want to mark as ready?`,emptyMessage:`No publications found in dated folders`,sort:`date-desc`})}async function U(e){let t=j(e.basePath,{contentTypes:e.contentTypes,sort:e.sort});t.length===0&&(r.error(e.emptyMessage),S.exit(1));let i=20;for(;;){let a=await o({message:e.message,options:Me(t,i)});if(n(a)&&(r.error(`Operation cancelled.`),S.exit(0)),a===V){i+=20;continue}let s=t.find(e=>e.path===a);return s||(r.error(`Unable to resolve selected publication: ${a}`),S.exit(1)),s}}function Me(e,t){let n=e.slice(0,t).map(e=>Ne(e));if(t>=e.length)return n;let r=Math.min(t+20,e.length);return[...n,{label:`Show ${r-t} more publications (${t+1}-${r} of ${e.length})`,value:V,hint:`Extend the list to ${e[r-1]?.relativePath}`}]}function Ne(e){return{label:`${l(e.date,`yyyy-MM-dd`)} · ${A[e.contentType]} · ${e.title}`,value:e.path,hint:e.relativePath}}async function Pe(e){let t=F(e);if(t.length===0&&(r.error(`No series directories found`),S.exit(1)),t.length===1)return t[0];let i=await o({message:`Which series do you want to work on?`,options:t.map(e=>({label:e.title,value:e.path,hint:`series/${e.name}`}))});n(i)&&(r.error(`Operation cancelled.`),S.exit(0));let a=t.find(e=>e.path===i);return a||(r.error(`Unable to resolve selected series: ${i}`),S.exit(1)),a}async function Fe(e){let t=L(e),i=t.map(e=>({label:`Insert at #${e.index} · before ${e.title}`,value:String(e.index),hint:e.fileName}));i.push({label:`Append as #${t.length+1}`,value:String(t.length+1),hint:`Add the new article at the end of the series`});let a=await o({message:`Where should the new article be inserted?`,options:i});return n(a)&&(r.error(`Operation cancelled.`),S.exit(0)),Number(a)}function W(e,t){return e||t?.path||S.cwd()}async function Ie(e,n){t(`Content Creation - Create dated content directory`);let r=await D(),i=await H(),o=await Oe(),s={title:i};o.includes(`linkedin`)&&(s.hasVideo=await Te(),s.hasImages=!s.hasVideo,r.thematic.length>0&&(s.theme=await Ee(r.thematic)));let c=await Ce();ue(c,o,s,r,W(e,n));let u=o.join(`, `);a(`✓ Content directory created: ${l(c,`yyyy/MM/dd`)} (${u})`)}function Le(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 Re(e){switch(e){case`linkedin`:return`LinkedIn Posts`;case`x`:return`X Posts`;case`youtube`:return`YouTube Videos`;case`instagram`:return`Instagram Posts`}}function ze(e,t){let n=j(e,{contentTypes:[t],sort:`date-desc`});if(n.length===0){r.info(`No ${t} posts found`);return}let i=Re(t),a=Le(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`}v(b(e,a),o,`utf-8`),r.success(`Created ${a} with ${n.length} posts`)}function Be(e=S.cwd(),t){let n=t||[...k];for(let t of n)ze(e,t)}function Ve(e,n){t(`Content Creation - Create Index Files`),Be(W(e,n)),a(`✓ Index files generated successfully`)}function He(e,t,n){if(e===`create`){Ve(t,n);return}throw Error(`Unknown index action: ${e}. Supported actions are: create`)}function Ue(e){let t=[`.jpg`,`.jpeg`,`.png`],n=[];try{let r=g(e);for(let i of r)if(_(b(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 G(e,t){try{let{data:n,content:i}=C(h(e.path,`utf-8`));n.images=t.length>0?t:[null];let a=C.stringify(i,n);v(e.path,a,`utf-8`),r.success(`LinkedIn publication updated: ${e.relativePath} (${t.length} image(s))`)}catch(e){r.error(`Error updating frontmatter: ${e}`)}}async function We(e=S.cwd()){let t=await Ae(e),n=Ue(t.folderPath);if(n.length===0){r.info(`No images found for LinkedIn publication: ${t.relativePath}`),G(t,[]);return}r.info(`Found ${n.length} image(s) for ${t.relativePath}: ${n.join(`, `)}`),G(t,n)}const K=oe(new URL(`../../`,import.meta.url)),q=`content-creation.ics`;function J(e){return e.replace(/\\/g,`\\\\`).replace(/\r\n|\r|\n/g,`\\n`).replace(/;/g,`\\;`).replace(/,/g,`\\,`)}function Ge(e){if(e.length<=75)return e;let t=[],n=e;for(;n.length>75;)t.push(n.slice(0,75)),n=` ${n.slice(75)}`;return t.push(n),t.join(`\r
|
|
5
|
+
`)}function Ke(e){return e.toISOString().replace(/[-:]/g,``).replace(/\.\d{3}Z$/,`Z`)}function Y(e){return l(e,`yyyyMMdd`)}function X(e){return e.split(`/`).map(e=>encodeURIComponent(e)).join(`/`)}function qe(e){return`https://github.com/barbapapazes/content-creation/blob/main/${X(e)}`}function Je(e){let t=X(e);return`vscode://file${t.startsWith(`/`)?``:`/`}${t}`}function Ye(e){let t=[`GitHub: ${qe(e.relativePath)}`,`VS Code: ${Je(e.path)}`,`Status: ${e.ready?`ready`:`not ready`}`];return e.theme&&t.push(`Theme: ${e.theme}`),typeof e.video==`boolean`&&t.push(`Video planned: ${e.video?`yes`:`no`}`),e.imageCount>0&&t.push(`Images planned: ${e.imageCount}`),t.join(`
|
|
6
|
+
`)}function Xe(e){return`${e.ready?`Ready`:`Not ready`} · ${e.title} · ${A.linkedin}`}function Ze(e){return`linkedin-${ae(`sha1`).update(e).digest(`hex`)}@barbapapazes`}function Qe(e){let t=[`BEGIN:VCALENDAR`,`VERSION:2.0`,`PRODID:-//Barbapapazes//Content Creation LinkedIn Calendar//EN`,`CALSCALE:GREGORIAN`,`METHOD:PUBLISH`,`X-WR-CALNAME:${J(`content-creation.ics`)}`];for(let n of e){let e=Ke(new Date(Date.UTC(n.date.getFullYear(),n.date.getMonth(),n.date.getDate()))),r=c(n.date,1);t.push(`BEGIN:VEVENT`,`UID:${Ze(n.relativePath)}`,`DTSTAMP:${e}`,`SUMMARY:${J(Xe(n))}`,`DTSTART;VALUE=DATE:${Y(n.date)}`,`DTEND;VALUE=DATE:${Y(r)}`,`TRANSP:TRANSPARENT`,`DESCRIPTION:${J(Ye(n))}`,`END:VEVENT`)}return t.push(`END:VCALENDAR`),`${t.map(Ge).join(`\r
|
|
7
|
+
`)}\r\n`}function $e(){let e=b(K,`node_modules`,`.bin`,S.platform===`win32`?`wrangler.cmd`:`wrangler`);return p(e)?e:`wrangler`}function et(e){(!e.calendar.publicUrl||!e.calendar.token)&&(r.error(`Calendar publishing configuration is missing. Please set CALENDAR_PUBLIC_URL and CALENDAR_TOKEN in your .env file or config.`),S.exit(1));let t=e.calendar.publicUrl.endsWith(`/`)?e.calendar.publicUrl:`${e.calendar.publicUrl}/`,n=new URL(q,t);return n.searchParams.set(`token`,e.calendar.token),n.toString()}function tt(e){return j(e,{contentTypes:[`linkedin`],sort:`date-asc`}).map(e=>{let t=e.frontmatter;return{...e,title:t.title||e.title,ready:t.ready===!0,theme:t.theme,video:t.video,imageCount:t.images?.filter(Boolean).length??0}})}function nt(e){let t=$e();try{ie(t,[`r2`,`object`,`put`,`content-creation/${q}`,`--pipe`,`--content-type`,`text/calendar; charset=utf-8`,`--remote`],{cwd:K,stdio:[`pipe`,`inherit`,`inherit`],env:S.env,input:e})}catch(e){r.error(`Failed to upload LinkedIn calendar: ${e instanceof Error?e.message:String(e)}`),S.exit(1)}}async function rt(e,t){let n=tt(e);if(n.length===0)return r.info(`No LinkedIn publications found. Skipping calendar upload.`),null;let i=et(t);return nt(Qe(n)),r.success(`✓ LinkedIn calendar uploaded to R2 as ${q}`),r.info(`Subscription URL: ${i}`),i}async function it(e,n){t(`Content Creation - Link Images to LinkedIn Publication`),await We(W(e,n)),a(`✓ LinkedIn publication images linked`)}async function at(e,n){t(`Content Creation - Publish LinkedIn Calendar`);let r=await D();if(await rt(W(e,n),r)){a(`✓ LinkedIn calendar published successfully`);return}a(`✓ No LinkedIn publications found, calendar was not updated`)}async function ot(e,t,n){if(e===`link-images`){await it(t,n);return}if(e===`publish-calendar`){await at(t,n);return}throw Error(`Unknown linkedin action: ${e}. Supported actions are: link-images, publish-calendar`)}function st(e){let t=new Date;t.setHours(0,0,0,0);let n=j(e,{fromDate:t,sort:`date-asc`});if(n.length===0){r.info(`No upcoming publications found from today onward.`);return}r.info(`Found ${n.length} upcoming publication${n.length===1?``:`s`}:`),console.log(``);for(let e of n){let t=l(e.date,`EEEE, MMMM d, yyyy`),n=l(e.date,`yyyy-MM-dd`);console.log(`${t} (${n}) · ${A[e.contentType]} · ${e.title}`),console.log(e.path),console.log(``)}}function ct(e){let t=e.path;try{let{data:n,content:i}=C(h(t,`utf-8`));if(n.ready===!0){r.warn(`This publication is already marked as ready.`);return}let a={...n,ready:!0};v(t,C.stringify(i,a),`utf-8`),r.success(`Publication marked as ready: ${e.relativePath}`)}catch(e){r.error(`Failed to mark publication as ready: ${e instanceof Error?e.message:String(e)}`),S.exit(1)}}function lt(e,n){t(`Content Creation - List Upcoming Publications`),st(W(e,n)),a(`✓ Upcoming publications listed`)}async function ut(e,n){t(`Content Creation - Mark Publication Ready`),ct(await je(W(e,n))),a(`✓ Publication marked as ready`)}async function dt(e,t,n){if(e===`list-upcoming`){lt(t,n);return}if(e===`ready`){await ut(t,n);return}throw Error(`Unknown publication action: ${e}. Supported actions are: list-upcoming, ready`)}function Z(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 ft(e,t,n=S.cwd()){let i=Z(e),a=b(x(b(n,`resources`)),i),o=`${t}.md`,s=b(a,o);if(p(s))return r.info(`${o} already exists at: ${s}`),a;m(a,{recursive:!0}),r.success(`Created directory: ${a}`);let c={title:e,url:``,date:``};return v(s,C.stringify(``,c),`utf-8`),r.success(`Created ${o} at: ${s}`),a}async function pt(e,n){t(`Content Creation - Create Resource`);let r=await H(),i=await De();ft(r,i,W(e,n)),a(`✓ Resource created: resources/${Z(r)}/${i}.md`)}async function Q(e,t,n){if(e===`create`){await pt(t,n);return}throw Error(`Unknown resource action: ${e}. Supported actions are: create`)}function mt(e,t){let n=F(e);if(n.length===0)return;let r=t?n.filter(e=>e.name===t):n;if(r.length===0)throw Error(`Series not found: ${t}`);for(let e of r)R(e.path)}function ht(e,t,n){let i=L(e),a=_t(n,i.length),o=b(e,`${a}.${Z(t)}.md`);if(p(o))throw Error(`Article already exists at ${o}`);return gt(e,i,a),v(o,C.stringify(``,{title:t}),`utf-8`),r.success(`Created article: ${o}`),R(e),o}function gt(e,t,n){let i=t.filter(e=>e.index>=n).sort((e,t)=>t.index-e.index);for(let t of i){let n=`${t.index+1}.${t.slug}.md`,i=b(e,n);te(t.path,i),r.info(`Renamed ${t.fileName} → ${n}`)}}function _t(e,t){if(!Number.isInteger(e))throw TypeError(`Article index must be an integer`);if(e<1||e>t+1)throw RangeError(`Article index must be between 1 and ${t+1}`);return e}async function vt(e,n,r){let i=W(n,r);if(e===`create-index`){t(`Content Creation - Create Series Index`),mt(i,r?.series),a(r?.series?`✓ Series index generated for ${r.series}`:`✓ Series indexes generated successfully`);return}if(e===`introduce`){t(`Content Creation - Introduce Series Article`);let e=r?.title||await H(),n=r?.series?I(i,r.series):await Pe(i);if(!n)throw Error(`Series not found: ${r?.series}`);let o=r?.position?Number(r.position):await Fe(n.path);ht(n.path,e,o),a(`✓ Series article created: ${n.name}/${o}.${Z(e)}.md`);return}throw Error(`Unknown series action: ${e}. Supported actions are: create-index, introduce`)}async function yt(e,t){let n=e.path,{data:i,content:a}=C(h(n,`utf-8`));i.scheduled===!0&&(r.warn(`This X publication is already scheduled. Skipping.`),S.exit(0)),(!t.scheduling.automationEndpoint||!t.scheduling.cfAccessClientId||!t.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.`),S.exit(1));let o=new Date(e.date);o=d(o,11),o=f(o,0),o=ee(o,0);let s=o.getTime(),c={content:a.trim(),scheduleAt:s};try{let e=await fetch(t.scheduling.automationEndpoint,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`application/json`,"CF-Access-Client-Id":t.scheduling.cfAccessClientId,"CF-Access-Client-Secret":t.scheduling.cfAccessClientSecret},body:JSON.stringify(c)});if(!e.ok){let t=await e.text();r.error(`Failed to schedule reminder: ${e.status} ${e.statusText}\n${t}`),S.exit(1)}let o=await e.json(),s={...i,scheduled:!0};v(n,C.stringify(a,s),`utf-8`);let u=new Date(o.scheduledAt);r.success(`✓ X publication reminder scheduled`),r.info(`Reminder scheduled for: ${l(u,`yyyy-MM-dd HH:mm:ss`)} UTC`)}catch(e){r.error(`Failed to schedule X publication reminder: ${e instanceof Error?e.message:String(e)}`),S.exit(1)}}async function bt(e,n){t(`Content Creation - Schedule X Publication Reminder`);let r=await D();await yt(await ke(W(e,n)),r),a(`✓ X publication reminder scheduled`)}async function xt(e,t,n){if(e===`schedule-reminder`){await bt(t,n);return}throw Error(`Unknown x action: ${e}. Supported actions are: schedule-reminder`)}const $=e(`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(Ie),$.command(`resource <action> [path]`,`Manage resource markdown files`).option(`--path <path>`,`Base path for resource directory (defaults to current directory)`).action(Q),$.command(`series <action> [path]`,`Manage article series and generated indexes`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).option(`--series <series>`,`Series folder name inside the series directory`).option(`--title <title>`,`Title of the new series article`).option(`--position <position>`,`Insert position in the series index (1-based)`).action(vt),$.command(`index <action> [path]`,`Manage generated indexes`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(He),$.command(`linkedin <action> [path]`,`Manage LinkedIn-specific workflows`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(ot),$.command(`x <action> [path]`,`Manage X-specific workflows`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(xt),$.command(`publication <action> [path]`,`Manage publication workflows`).option(`--path <path>`,`Base path for content directory (defaults to current directory)`).action(dt),$.help(),$.version(se),$.parse();export{};
|
package/package.json
CHANGED