autoblogger 0.1.15 → 0.1.17

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.
@@ -0,0 +1,1224 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli/init.ts
27
+ var fs7 = __toESM(require("fs"));
28
+ var path6 = __toESM(require("path"));
29
+ var import_child_process2 = require("child_process");
30
+ var import_picocolors3 = __toESM(require("picocolors"));
31
+
32
+ // src/cli/utils/detect.ts
33
+ var fs = __toESM(require("fs"));
34
+ var path = __toESM(require("path"));
35
+ var CONTENT_DIRS = ["content", "posts", "blog", "articles", "content/posts", "content/blog"];
36
+ function detectProject(cwd = process.cwd()) {
37
+ const info = {
38
+ isNextJs: false,
39
+ hasPrisma: false,
40
+ hasTailwind: false,
41
+ contentPaths: []
42
+ };
43
+ const packageJsonPath = path.join(cwd, "package.json");
44
+ if (fs.existsSync(packageJsonPath)) {
45
+ try {
46
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
47
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
48
+ if (deps.next) {
49
+ info.isNextJs = true;
50
+ info.nextVersion = deps.next.replace(/[\^~]/g, "");
51
+ }
52
+ } catch {
53
+ }
54
+ }
55
+ const prismaSchemaPath = path.join(cwd, "prisma", "schema.prisma");
56
+ if (fs.existsSync(prismaSchemaPath)) {
57
+ info.hasPrisma = true;
58
+ info.prismaSchemaPath = prismaSchemaPath;
59
+ }
60
+ const tailwindConfigs = [
61
+ "tailwind.config.ts",
62
+ "tailwind.config.js",
63
+ "tailwind.config.mjs",
64
+ "tailwind.config.cjs"
65
+ ];
66
+ for (const config of tailwindConfigs) {
67
+ const configPath = path.join(cwd, config);
68
+ if (fs.existsSync(configPath)) {
69
+ info.hasTailwind = true;
70
+ info.tailwindConfigPath = configPath;
71
+ break;
72
+ }
73
+ }
74
+ if (!info.hasTailwind) {
75
+ const cssConfigPaths = [
76
+ "app/globals.css",
77
+ "src/app/globals.css",
78
+ "styles/globals.css",
79
+ "app/app.css",
80
+ "src/app/app.css"
81
+ ];
82
+ for (const cssPath of cssConfigPaths) {
83
+ const fullPath = path.join(cwd, cssPath);
84
+ if (fs.existsSync(fullPath)) {
85
+ const content = fs.readFileSync(fullPath, "utf-8");
86
+ if (content.includes('@import "tailwindcss"') || content.includes("@import 'tailwindcss'")) {
87
+ info.hasTailwind = true;
88
+ info.tailwindCssPath = fullPath;
89
+ break;
90
+ }
91
+ }
92
+ }
93
+ }
94
+ const appDirPath = path.join(cwd, "app");
95
+ const srcAppDirPath = path.join(cwd, "src", "app");
96
+ if (fs.existsSync(appDirPath)) {
97
+ info.appRouterPath = "app";
98
+ } else if (fs.existsSync(srcAppDirPath)) {
99
+ info.appRouterPath = "src/app";
100
+ }
101
+ for (const dir of CONTENT_DIRS) {
102
+ const dirPath = path.join(cwd, dir);
103
+ if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
104
+ const files = fs.readdirSync(dirPath);
105
+ const hasMarkdown = files.some((f) => f.endsWith(".md") || f.endsWith(".mdx"));
106
+ if (hasMarkdown) {
107
+ info.contentPaths.push(dir);
108
+ }
109
+ }
110
+ }
111
+ return info;
112
+ }
113
+ function countMarkdownFiles(dirPath) {
114
+ if (!fs.existsSync(dirPath)) return 0;
115
+ let count = 0;
116
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
117
+ for (const entry of entries) {
118
+ const fullPath = path.join(dirPath, entry.name);
119
+ if (entry.isDirectory()) {
120
+ count += countMarkdownFiles(fullPath);
121
+ } else if (entry.name.endsWith(".md") || entry.name.endsWith(".mdx")) {
122
+ count++;
123
+ }
124
+ }
125
+ return count;
126
+ }
127
+
128
+ // src/cli/utils/backup.ts
129
+ var fs2 = __toESM(require("fs"));
130
+ var path2 = __toESM(require("path"));
131
+ var BACKUP_DIR = ".autoblogger-backup";
132
+ function createBackup(files, cwd = process.cwd()) {
133
+ const backupPath = path2.join(cwd, BACKUP_DIR);
134
+ if (!fs2.existsSync(backupPath)) {
135
+ fs2.mkdirSync(backupPath, { recursive: true });
136
+ }
137
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
138
+ const timestampedBackupPath = path2.join(backupPath, timestamp);
139
+ fs2.mkdirSync(timestampedBackupPath, { recursive: true });
140
+ for (const file of files) {
141
+ const sourcePath = path2.join(cwd, file);
142
+ if (fs2.existsSync(sourcePath)) {
143
+ const destPath = path2.join(timestampedBackupPath, file);
144
+ const destDir = path2.dirname(destPath);
145
+ if (!fs2.existsSync(destDir)) {
146
+ fs2.mkdirSync(destDir, { recursive: true });
147
+ }
148
+ fs2.copyFileSync(sourcePath, destPath);
149
+ }
150
+ }
151
+ return timestampedBackupPath;
152
+ }
153
+
154
+ // src/cli/utils/prisma-merge.ts
155
+ var fs3 = __toESM(require("fs"));
156
+ var path3 = __toESM(require("path"));
157
+ var AUTOBLOGGER_MODELS = `
158
+ // ==========================================
159
+ // AUTOBLOGGER MODELS
160
+ // ==========================================
161
+
162
+ model Post {
163
+ id String @id @default(uuid())
164
+ title String
165
+ subtitle String?
166
+ slug String @unique
167
+ markdown String
168
+ status String @default("draft") // draft, published, suggested, deleted
169
+ createdAt DateTime @default(now())
170
+ updatedAt DateTime @updatedAt
171
+ publishedAt DateTime?
172
+
173
+ // SEO fields
174
+ seoTitle String?
175
+ seoDescription String?
176
+ seoKeywords String?
177
+ noIndex Boolean @default(false)
178
+ ogImage String?
179
+
180
+ // Preview
181
+ previewToken String? @unique
182
+ previewExpiry DateTime?
183
+
184
+ // Relations
185
+ revisions Revision[]
186
+ tags PostTag[]
187
+ comments Comment[]
188
+ newsItem NewsItem?
189
+
190
+ // Auto-draft
191
+ sourceUrl String?
192
+ topicId String?
193
+ topic TopicSubscription? @relation(fields: [topicId], references: [id])
194
+ }
195
+
196
+ model Revision {
197
+ id String @id @default(uuid())
198
+ postId String
199
+ post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
200
+ title String?
201
+ subtitle String?
202
+ markdown String
203
+ createdAt DateTime @default(now())
204
+ }
205
+
206
+ model Comment {
207
+ id String @id @default(uuid())
208
+ postId String
209
+ post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
210
+
211
+ // For authenticated comments (editor comments)
212
+ userId String?
213
+ user User? @relation(fields: [userId], references: [id])
214
+
215
+ // For public comments (legacy)
216
+ authorId String?
217
+ authorName String?
218
+ authorEmail String?
219
+
220
+ // Editor comment fields
221
+ quotedText String @default("")
222
+ content String
223
+ parentId String?
224
+ parent Comment? @relation("Replies", fields: [parentId], references: [id], onDelete: Cascade)
225
+ replies Comment[] @relation("Replies")
226
+ resolved Boolean @default(false)
227
+ deletedAt DateTime?
228
+
229
+ // Legacy field
230
+ approved Boolean @default(true)
231
+
232
+ createdAt DateTime @default(now())
233
+ updatedAt DateTime @updatedAt
234
+
235
+ @@index([postId])
236
+ @@index([parentId])
237
+ }
238
+
239
+ model User {
240
+ id String @id @default(uuid())
241
+ email String @unique
242
+ name String?
243
+ role String @default("writer")
244
+ createdAt DateTime @default(now())
245
+ comments Comment[]
246
+ }
247
+
248
+ model Tag {
249
+ id String @id @default(uuid())
250
+ name String @unique
251
+ createdAt DateTime @default(now())
252
+ posts PostTag[]
253
+ }
254
+
255
+ model PostTag {
256
+ id String @id @default(uuid())
257
+ postId String
258
+ tagId String
259
+ post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
260
+ tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
261
+ createdAt DateTime @default(now())
262
+
263
+ @@unique([postId, tagId])
264
+ }
265
+
266
+ model AISettings {
267
+ id String @id @default("default")
268
+ rules String @default("")
269
+ chatRules String @default("")
270
+ rewriteRules String?
271
+ autoDraftRules String?
272
+ planRules String?
273
+ defaultModel String @default("claude-sonnet")
274
+ autoDraftWordCount Int @default(800)
275
+ generateTemplate String?
276
+ chatTemplate String?
277
+ rewriteTemplate String?
278
+ autoDraftTemplate String?
279
+ planTemplate String?
280
+ expandPlanTemplate String?
281
+ anthropicKey String?
282
+ openaiKey String?
283
+ updatedAt DateTime @updatedAt
284
+ }
285
+
286
+ model IntegrationSettings {
287
+ id String @id @default("default")
288
+ autoDraftEnabled Boolean @default(false)
289
+ postUrlPattern String @default("/e/{slug}")
290
+ updatedAt DateTime @updatedAt
291
+ }
292
+
293
+ model TopicSubscription {
294
+ id String @id @default(uuid())
295
+ name String
296
+ keywords String
297
+ rssFeeds String
298
+ isActive Boolean @default(true)
299
+ useKeywordFilter Boolean @default(true)
300
+ frequency String @default("daily")
301
+ maxPerPeriod Int @default(3)
302
+ essayFocus String?
303
+ lastRunAt DateTime?
304
+ createdAt DateTime @default(now())
305
+ updatedAt DateTime @updatedAt
306
+ posts Post[]
307
+ newsItems NewsItem[]
308
+ }
309
+
310
+ model NewsItem {
311
+ id String @id @default(uuid())
312
+ topicId String
313
+ topic TopicSubscription @relation(fields: [topicId], references: [id], onDelete: Cascade)
314
+ url String @unique
315
+ title String
316
+ summary String?
317
+ publishedAt DateTime?
318
+ status String @default("pending")
319
+ postId String? @unique
320
+ post Post? @relation(fields: [postId], references: [id])
321
+ createdAt DateTime @default(now())
322
+ }
323
+ `;
324
+ var AUTOBLOGGER_MODEL_NAMES = [
325
+ "Post",
326
+ "Revision",
327
+ "Comment",
328
+ "User",
329
+ "Tag",
330
+ "PostTag",
331
+ "AISettings",
332
+ "IntegrationSettings",
333
+ "TopicSubscription",
334
+ "NewsItem"
335
+ ];
336
+ function extractModelNames(schemaContent) {
337
+ const modelRegex = /model\s+(\w+)\s*\{/g;
338
+ const models = [];
339
+ let match;
340
+ while ((match = modelRegex.exec(schemaContent)) !== null) {
341
+ models.push(match[1]);
342
+ }
343
+ return models;
344
+ }
345
+ function checkConflicts(schemaPath) {
346
+ if (!fs3.existsSync(schemaPath)) {
347
+ return [];
348
+ }
349
+ const content = fs3.readFileSync(schemaPath, "utf-8");
350
+ const existingModels = extractModelNames(content);
351
+ return AUTOBLOGGER_MODEL_NAMES.filter((model) => existingModels.includes(model));
352
+ }
353
+ function mergeSchema(schemaPath, dbProvider) {
354
+ const conflicts = checkConflicts(schemaPath);
355
+ if (conflicts.length > 0) {
356
+ return {
357
+ success: false,
358
+ conflicts
359
+ };
360
+ }
361
+ let content;
362
+ if (fs3.existsSync(schemaPath)) {
363
+ const existing = fs3.readFileSync(schemaPath, "utf-8");
364
+ content = existing.trimEnd() + "\n" + AUTOBLOGGER_MODELS;
365
+ } else {
366
+ const provider = dbProvider || "postgresql";
367
+ content = `generator client {
368
+ provider = "prisma-client-js"
369
+ }
370
+
371
+ datasource db {
372
+ provider = "${provider}"
373
+ url = env("DATABASE_URL")
374
+ }
375
+ ${AUTOBLOGGER_MODELS}`;
376
+ }
377
+ return {
378
+ success: true,
379
+ conflicts: [],
380
+ content
381
+ };
382
+ }
383
+ function writeSchema(schemaPath, content) {
384
+ const dir = path3.dirname(schemaPath);
385
+ if (!fs3.existsSync(dir)) {
386
+ fs3.mkdirSync(dir, { recursive: true });
387
+ }
388
+ fs3.writeFileSync(schemaPath, content, "utf-8");
389
+ }
390
+
391
+ // src/cli/utils/tailwind-patch.ts
392
+ var fs4 = __toESM(require("fs"));
393
+ var AUTOBLOGGER_CONTENT_PATH = "'./node_modules/autoblogger/dist/**/*.{js,mjs}'";
394
+ var AUTOBLOGGER_SOURCE_PATH = '"./node_modules/autoblogger/dist/**/*.{js,mjs}"';
395
+ function patchTailwindConfig(configPath) {
396
+ if (!fs4.existsSync(configPath)) {
397
+ return { success: false, alreadyPatched: false };
398
+ }
399
+ let content = fs4.readFileSync(configPath, "utf-8");
400
+ if (content.includes("autoblogger")) {
401
+ return { success: true, alreadyPatched: true };
402
+ }
403
+ const contentArrayRegex = /(content\s*:\s*\[)([^\]]*?)(\])/s;
404
+ const match = content.match(contentArrayRegex);
405
+ if (match) {
406
+ const [full, start, items, end] = match;
407
+ const trimmedItems = items.trimEnd();
408
+ const needsComma = trimmedItems.length > 0 && !trimmedItems.endsWith(",");
409
+ const newItems = trimmedItems + (needsComma ? "," : "") + "\n // Autoblogger components\n " + AUTOBLOGGER_CONTENT_PATH + ",\n ";
410
+ content = content.replace(full, start + newItems + end);
411
+ return { success: true, alreadyPatched: false, content };
412
+ }
413
+ if (content.includes("export default")) {
414
+ const configObjRegex = /(export\s+default\s*\{)/;
415
+ if (configObjRegex.test(content)) {
416
+ content = content.replace(
417
+ configObjRegex,
418
+ `$1
419
+ content: [
420
+ // Autoblogger components
421
+ ${AUTOBLOGGER_CONTENT_PATH},
422
+ ],`
423
+ );
424
+ return { success: true, alreadyPatched: false, content };
425
+ }
426
+ }
427
+ if (content.includes("module.exports")) {
428
+ const moduleExportsRegex = /(module\.exports\s*=\s*\{)/;
429
+ if (moduleExportsRegex.test(content)) {
430
+ content = content.replace(
431
+ moduleExportsRegex,
432
+ `$1
433
+ content: [
434
+ // Autoblogger components
435
+ ${AUTOBLOGGER_CONTENT_PATH},
436
+ ],`
437
+ );
438
+ return { success: true, alreadyPatched: false, content };
439
+ }
440
+ }
441
+ return { success: false, alreadyPatched: false };
442
+ }
443
+ function patchTailwindCssConfig(cssPath) {
444
+ if (!fs4.existsSync(cssPath)) {
445
+ return { success: false, alreadyPatched: false, isCssConfig: true };
446
+ }
447
+ let content = fs4.readFileSync(cssPath, "utf-8");
448
+ if (!content.includes('@import "tailwindcss"') && !content.includes("@import 'tailwindcss'")) {
449
+ return { success: false, alreadyPatched: false, isCssConfig: false };
450
+ }
451
+ if (content.includes("autoblogger")) {
452
+ return { success: true, alreadyPatched: true, isCssConfig: true };
453
+ }
454
+ const importRegex = /(@import\s+["']tailwindcss["'];?\s*\n)/;
455
+ const match = content.match(importRegex);
456
+ if (match) {
457
+ content = content.replace(
458
+ importRegex,
459
+ `$1/* Autoblogger components */
460
+ @source ${AUTOBLOGGER_SOURCE_PATH};
461
+ `
462
+ );
463
+ return { success: true, alreadyPatched: false, content, isCssConfig: true };
464
+ }
465
+ return { success: false, alreadyPatched: false, isCssConfig: true };
466
+ }
467
+ function writeTailwindConfig(configPath, content) {
468
+ fs4.writeFileSync(configPath, content, "utf-8");
469
+ }
470
+
471
+ // src/cli/utils/css-patch.ts
472
+ var fs5 = __toESM(require("fs"));
473
+ var path4 = __toESM(require("path"));
474
+ var AUTOBLOGGER_CSS_IMPORT = "@import 'autoblogger/styles/autoblogger.css';";
475
+ function findGlobalsCss(projectRoot) {
476
+ const candidates = [
477
+ "app/globals.css",
478
+ "src/app/globals.css",
479
+ "styles/globals.css",
480
+ "src/styles/globals.css",
481
+ "app/global.css",
482
+ "src/app/global.css"
483
+ ];
484
+ for (const candidate of candidates) {
485
+ const fullPath = path4.join(projectRoot, candidate);
486
+ if (fs5.existsSync(fullPath)) {
487
+ return fullPath;
488
+ }
489
+ }
490
+ return null;
491
+ }
492
+ function patchGlobalsCss(cssPath) {
493
+ if (!fs5.existsSync(cssPath)) {
494
+ return { success: false, alreadyPatched: false };
495
+ }
496
+ let content = fs5.readFileSync(cssPath, "utf-8");
497
+ if (content.includes("autoblogger")) {
498
+ return { success: true, alreadyPatched: true, filePath: cssPath };
499
+ }
500
+ content = AUTOBLOGGER_CSS_IMPORT + "\n\n" + content;
501
+ fs5.writeFileSync(cssPath, content, "utf-8");
502
+ return { success: true, alreadyPatched: false, filePath: cssPath };
503
+ }
504
+
505
+ // src/cli/utils/prompts.ts
506
+ var import_prompts = __toESM(require("prompts"));
507
+ var import_picocolors = __toESM(require("picocolors"));
508
+ async function promptInit(options) {
509
+ const questions = [];
510
+ if (!options.hasPrisma) {
511
+ questions.push({
512
+ type: "select",
513
+ name: "dbProvider",
514
+ message: "Database provider:",
515
+ choices: [
516
+ { title: "PostgreSQL", value: "postgresql" },
517
+ { title: "SQLite (for development)", value: "sqlite" },
518
+ { title: "MySQL", value: "mysql" }
519
+ ],
520
+ initial: 0
521
+ });
522
+ }
523
+ questions.push({
524
+ type: "confirm",
525
+ name: "runMigration",
526
+ message: "Run database migration after setup?",
527
+ initial: true
528
+ });
529
+ if (options.contentPaths.length > 0) {
530
+ const contentSummary = options.contentPaths.map((p) => `${p} (${options.contentCounts[p]} files)`).join(", ");
531
+ console.log(import_picocolors.default.cyan(`
532
+ Found existing content: ${contentSummary}`));
533
+ questions.push({
534
+ type: "confirm",
535
+ name: "importContent",
536
+ message: "Import existing content?",
537
+ initial: true
538
+ });
539
+ if (options.contentPaths.length > 1) {
540
+ questions.push({
541
+ type: (prev) => prev ? "select" : null,
542
+ name: "importPath",
543
+ message: "Which directory to import from?",
544
+ choices: options.contentPaths.map((p) => ({
545
+ title: `${p} (${options.contentCounts[p]} files)`,
546
+ value: p
547
+ }))
548
+ });
549
+ }
550
+ }
551
+ const answers = await (0, import_prompts.default)(questions, {
552
+ onCancel: () => {
553
+ console.log(import_picocolors.default.yellow("\nSetup cancelled"));
554
+ process.exit(0);
555
+ }
556
+ });
557
+ return {
558
+ dbProvider: answers.dbProvider || "postgresql",
559
+ runMigration: answers.runMigration ?? true,
560
+ importContent: answers.importContent ?? false,
561
+ importPath: answers.importPath || options.contentPaths[0]
562
+ };
563
+ }
564
+ async function confirm(message, initial = true) {
565
+ const { confirmed } = await (0, import_prompts.default)({
566
+ type: "confirm",
567
+ name: "confirmed",
568
+ message,
569
+ initial
570
+ });
571
+ return confirmed ?? false;
572
+ }
573
+ function log(type, message) {
574
+ const icons = {
575
+ check: import_picocolors.default.green("\u2713"),
576
+ write: import_picocolors.default.blue("\u2192"),
577
+ run: import_picocolors.default.cyan("$"),
578
+ info: import_picocolors.default.cyan("\u2139"),
579
+ warn: import_picocolors.default.yellow("\u26A0"),
580
+ error: import_picocolors.default.red("\u2717"),
581
+ backup: import_picocolors.default.magenta("\u27F3"),
582
+ skip: import_picocolors.default.gray("\u25CB")
583
+ };
584
+ console.log(`${icons[type]} ${message}`);
585
+ }
586
+
587
+ // src/cli/templates/cms-config.ts
588
+ var CMS_CONFIG_TEMPLATE = `import { createAutoblogger } from 'autoblogger'
589
+
590
+ // TODO: Update this import to match your Prisma client location
591
+ // Common locations:
592
+ // import { prisma } from '@/lib/db'
593
+ // import { prisma } from '@/lib/prisma'
594
+ // import { db as prisma } from '@/server/db'
595
+ // import prisma from '@/lib/prisma'
596
+ //
597
+ // If you don't have a Prisma client file yet, create one:
598
+ // // lib/db.ts
599
+ // import { PrismaClient } from '@prisma/client'
600
+ // export const prisma = new PrismaClient()
601
+ import { prisma } from '@/lib/db'
602
+
603
+ // TODO: Import your auth function
604
+ // import { auth } from '@/lib/auth'
605
+
606
+ export const cms = createAutoblogger({
607
+ // Required: Your Prisma client instance
608
+ prisma,
609
+
610
+ // Required: Authentication configuration
611
+ auth: {
612
+ // TODO: Replace with your auth function
613
+ getSession: async () => {
614
+ // Example for NextAuth:
615
+ // return auth()
616
+
617
+ // For now, return a mock session (remove in production!)
618
+ return {
619
+ user: {
620
+ id: 'user-1',
621
+ email: 'admin@example.com',
622
+ name: 'Admin',
623
+ role: 'admin',
624
+ }
625
+ }
626
+ },
627
+
628
+ // Check if user is an admin
629
+ isAdmin: (session) => session?.user?.role === 'admin',
630
+
631
+ // Check if user can publish posts
632
+ canPublish: (session) => ['admin', 'writer'].includes(session?.user?.role ?? ''),
633
+ },
634
+
635
+ // Optional: AI configuration
636
+ ai: {
637
+ anthropicKey: process.env.ANTHROPIC_API_KEY,
638
+ openaiKey: process.env.OPENAI_API_KEY,
639
+ },
640
+
641
+ // Optional: File upload handler
642
+ // storage: {
643
+ // upload: async (file: File) => {
644
+ // const url = await uploadToYourStorage(file)
645
+ // return { url }
646
+ // }
647
+ // },
648
+ })
649
+ `;
650
+
651
+ // src/cli/templates/api-route.ts
652
+ var API_ROUTE_TEMPLATE = `import { cms } from '@/lib/cms'
653
+ import { NextRequest } from 'next/server'
654
+
655
+ async function handler(
656
+ req: NextRequest,
657
+ { params }: { params: Promise<{ path: string[] }> }
658
+ ) {
659
+ const { path } = await params
660
+ return cms.handleRequest(req, path.join('/'))
661
+ }
662
+
663
+ export { handler as GET, handler as POST, handler as PATCH, handler as DELETE }
664
+ `;
665
+
666
+ // src/cli/templates/dashboard-page.ts
667
+ var DASHBOARD_PAGE_TEMPLATE = `import { AutobloggerDashboard } from 'autoblogger/ui'
668
+ // TODO: Import your auth function
669
+ // import { auth } from '@/lib/auth'
670
+ // import { redirect } from 'next/navigation'
671
+
672
+ export default async function WriterPage({
673
+ params
674
+ }: {
675
+ params: Promise<{ path?: string[] }>
676
+ }) {
677
+ // TODO: Protect this route with your auth
678
+ // const session = await auth()
679
+ // if (!session) {
680
+ // redirect('/login')
681
+ // }
682
+
683
+ // Mock session for initial setup (remove in production!)
684
+ const session = {
685
+ user: {
686
+ id: 'user-1',
687
+ email: 'admin@example.com',
688
+ name: 'Admin',
689
+ role: 'admin',
690
+ }
691
+ }
692
+
693
+ const { path } = await params
694
+
695
+ return (
696
+ <AutobloggerDashboard
697
+ apiBasePath="/api/cms"
698
+ session={session}
699
+ path={path?.join('/') || ''}
700
+ />
701
+ )
702
+ }
703
+ `;
704
+
705
+ // src/cli/import.ts
706
+ var fs6 = __toESM(require("fs"));
707
+ var path5 = __toESM(require("path"));
708
+ var import_child_process = require("child_process");
709
+ var import_picocolors2 = __toESM(require("picocolors"));
710
+ function parseFrontmatter(content) {
711
+ const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
712
+ const match = content.match(frontmatterRegex);
713
+ if (!match) {
714
+ return { frontmatter: {}, body: content };
715
+ }
716
+ const frontmatterStr = match[1];
717
+ const body = match[2];
718
+ const frontmatter = {};
719
+ const lines = frontmatterStr.split("\n");
720
+ let currentKey = null;
721
+ let currentArray = null;
722
+ for (let i = 0; i < lines.length; i++) {
723
+ const line = lines[i];
724
+ const listItemMatch = line.match(/^\s+-\s+(.+)$/);
725
+ if (listItemMatch && currentKey && currentArray) {
726
+ const item = listItemMatch[1].trim().replace(/^["']|["']$/g, "");
727
+ currentArray.push(item);
728
+ continue;
729
+ }
730
+ if (currentKey && currentArray) {
731
+ frontmatter[currentKey] = currentArray;
732
+ currentKey = null;
733
+ currentArray = null;
734
+ }
735
+ const colonIndex = line.indexOf(":");
736
+ if (colonIndex === -1) continue;
737
+ const key = line.slice(0, colonIndex).trim();
738
+ let value = line.slice(colonIndex + 1).trim();
739
+ if (value === "") {
740
+ const nextLine = lines[i + 1];
741
+ if (nextLine && /^\s+-\s+/.test(nextLine)) {
742
+ currentKey = key;
743
+ currentArray = [];
744
+ continue;
745
+ }
746
+ }
747
+ if (value.startsWith('"') && value.endsWith('"')) {
748
+ value = value.slice(1, -1);
749
+ } else if (value.startsWith("'") && value.endsWith("'")) {
750
+ value = value.slice(1, -1);
751
+ } else if (value.startsWith("[") && value.endsWith("]")) {
752
+ const arrayStr = value.slice(1, -1);
753
+ value = arrayStr.split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
754
+ } else if (value === "true") {
755
+ value = true;
756
+ } else if (value === "false") {
757
+ value = false;
758
+ }
759
+ frontmatter[key] = value;
760
+ }
761
+ if (currentKey && currentArray) {
762
+ frontmatter[currentKey] = currentArray;
763
+ }
764
+ return { frontmatter, body };
765
+ }
766
+ function slugify(text) {
767
+ return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
768
+ }
769
+ function parseMarkdownFile(filePath) {
770
+ try {
771
+ const content = fs6.readFileSync(filePath, "utf-8");
772
+ const { frontmatter, body } = parseFrontmatter(content);
773
+ let title = frontmatter.title;
774
+ if (!title) {
775
+ const headingMatch = body.match(/^#\s+(.+)$/m);
776
+ title = headingMatch ? headingMatch[1] : path5.basename(filePath, path5.extname(filePath));
777
+ }
778
+ const slug = frontmatter.slug || slugify(path5.basename(filePath, path5.extname(filePath))) || slugify(title);
779
+ let publishedAt;
780
+ if (frontmatter.date) {
781
+ const parsed = new Date(frontmatter.date);
782
+ if (!isNaN(parsed.getTime())) {
783
+ publishedAt = parsed;
784
+ }
785
+ }
786
+ let tags;
787
+ if (Array.isArray(frontmatter.tags)) {
788
+ tags = frontmatter.tags;
789
+ } else if (typeof frontmatter.tags === "string") {
790
+ tags = [frontmatter.tags];
791
+ }
792
+ return {
793
+ title,
794
+ slug,
795
+ markdown: body.trim(),
796
+ subtitle: frontmatter.subtitle,
797
+ publishedAt,
798
+ tags,
799
+ seoDescription: frontmatter.description || frontmatter.excerpt
800
+ };
801
+ } catch (error) {
802
+ console.error(import_picocolors2.default.red(`Error parsing ${filePath}:`), error);
803
+ return null;
804
+ }
805
+ }
806
+ function findMarkdownFiles(dir) {
807
+ const files = [];
808
+ function walk(currentDir) {
809
+ const entries = fs6.readdirSync(currentDir, { withFileTypes: true });
810
+ for (const entry of entries) {
811
+ const fullPath = path5.join(currentDir, entry.name);
812
+ if (entry.isDirectory()) {
813
+ if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
814
+ walk(fullPath);
815
+ }
816
+ } else if (entry.name.endsWith(".md") || entry.name.endsWith(".mdx")) {
817
+ files.push(fullPath);
818
+ }
819
+ }
820
+ }
821
+ walk(dir);
822
+ return files;
823
+ }
824
+ async function importContent(dirPath, options = {}) {
825
+ const cwd = process.cwd();
826
+ const absolutePath = path5.isAbsolute(dirPath) ? dirPath : path5.join(cwd, dirPath);
827
+ if (!fs6.existsSync(absolutePath)) {
828
+ throw new Error(`Directory not found: ${dirPath}`);
829
+ }
830
+ const files = findMarkdownFiles(absolutePath);
831
+ if (files.length === 0) {
832
+ log("info", "No markdown files found");
833
+ return;
834
+ }
835
+ log("info", `Found ${files.length} markdown files`);
836
+ const posts = [];
837
+ for (const file of files) {
838
+ const post = parseMarkdownFile(file);
839
+ if (post) {
840
+ posts.push(post);
841
+ }
842
+ }
843
+ if (options.dryRun) {
844
+ console.log(import_picocolors2.default.cyan("\n--- Dry run: would import the following posts ---\n"));
845
+ for (const post of posts) {
846
+ console.log(` - ${post.title} (${post.slug})`);
847
+ }
848
+ console.log(import_picocolors2.default.cyan("\n--- No changes made ---"));
849
+ return;
850
+ }
851
+ const status = options.status || "draft";
852
+ const importScript = generateImportScript(posts, status, options.tag);
853
+ const scriptPath = path5.join(cwd, ".autoblogger-import.mjs");
854
+ fs6.writeFileSync(scriptPath, importScript);
855
+ try {
856
+ (0, import_child_process.execSync)(`node ${scriptPath}`, {
857
+ cwd,
858
+ stdio: "inherit"
859
+ });
860
+ log("check", `Imported ${posts.length} posts as ${status}`);
861
+ } finally {
862
+ if (fs6.existsSync(scriptPath)) {
863
+ fs6.unlinkSync(scriptPath);
864
+ }
865
+ }
866
+ }
867
+ function generateImportScript(posts, status, tag) {
868
+ const postsData = JSON.stringify(posts.map((p) => ({
869
+ ...p,
870
+ publishedAt: p.publishedAt?.toISOString()
871
+ })), null, 2);
872
+ return `
873
+ import { PrismaClient } from '@prisma/client'
874
+
875
+ const prisma = new PrismaClient()
876
+
877
+ const posts = ${postsData}
878
+
879
+ async function main() {
880
+ // Create tag if specified
881
+ ${tag ? `
882
+ let tagRecord = await prisma.tag.findUnique({ where: { name: '${tag}' } })
883
+ if (!tagRecord) {
884
+ tagRecord = await prisma.tag.create({ data: { name: '${tag}' } })
885
+ }
886
+ ` : ""}
887
+
888
+ for (const post of posts) {
889
+ // Check if slug already exists
890
+ const existing = await prisma.post.findUnique({ where: { slug: post.slug } })
891
+ if (existing) {
892
+ console.log(' Skipped (exists):', post.title)
893
+ continue
894
+ }
895
+
896
+ // Create post
897
+ const created = await prisma.post.create({
898
+ data: {
899
+ title: post.title,
900
+ slug: post.slug,
901
+ markdown: post.markdown,
902
+ subtitle: post.subtitle || null,
903
+ status: '${status}',
904
+ seoDescription: post.seoDescription || null,
905
+ publishedAt: post.publishedAt ? new Date(post.publishedAt) : ${status === "published" ? "new Date()" : "null"},
906
+ },
907
+ })
908
+
909
+ // Create tags if they exist
910
+ if (post.tags && post.tags.length > 0) {
911
+ for (const tagName of post.tags) {
912
+ let tagRecord = await prisma.tag.findUnique({ where: { name: tagName } })
913
+ if (!tagRecord) {
914
+ tagRecord = await prisma.tag.create({ data: { name: tagName } })
915
+ }
916
+ await prisma.postTag.create({
917
+ data: { postId: created.id, tagId: tagRecord.id },
918
+ })
919
+ }
920
+ }
921
+
922
+ ${tag ? `
923
+ // Add specified tag
924
+ await prisma.postTag.create({
925
+ data: { postId: created.id, tagId: tagRecord.id },
926
+ })
927
+ ` : ""}
928
+
929
+ console.log(' Imported:', post.title)
930
+ }
931
+ }
932
+
933
+ main()
934
+ .catch(console.error)
935
+ .finally(() => prisma.$disconnect())
936
+ `;
937
+ }
938
+
939
+ // src/cli/init.ts
940
+ async function init(options = {}) {
941
+ const cwd = process.cwd();
942
+ console.log(import_picocolors3.default.bold("\nSetting up Autoblogger...\n"));
943
+ const project = detectProject(cwd);
944
+ if (!project.isNextJs) {
945
+ console.log(import_picocolors3.default.yellow("Warning: This does not appear to be a Next.js project."));
946
+ if (!options.yes) {
947
+ const proceed = await confirm("Continue anyway?", false);
948
+ if (!proceed) {
949
+ console.log("Setup cancelled.");
950
+ return;
951
+ }
952
+ }
953
+ } else {
954
+ log("check", `Detected Next.js ${project.nextVersion || ""} project`);
955
+ }
956
+ if (project.hasPrisma) {
957
+ log("check", `Found ${project.prismaSchemaPath}`);
958
+ }
959
+ if (project.hasTailwind) {
960
+ log("check", `Found ${path6.basename(project.tailwindConfigPath)}`);
961
+ }
962
+ if (!project.appRouterPath) {
963
+ console.log(import_picocolors3.default.red("Error: Could not find App Router (app/ or src/app/ directory)"));
964
+ console.log("Autoblogger requires Next.js App Router.");
965
+ return;
966
+ }
967
+ const contentCounts = {};
968
+ for (const contentPath of project.contentPaths) {
969
+ contentCounts[contentPath] = countMarkdownFiles(path6.join(cwd, contentPath));
970
+ }
971
+ let answers = {
972
+ dbProvider: "postgresql",
973
+ runMigration: !options.skipMigrate,
974
+ importContent: !!options.importPath,
975
+ importPath: options.importPath || project.contentPaths[0]
976
+ };
977
+ if (!options.yes) {
978
+ answers = await promptInit({
979
+ hasPrisma: project.hasPrisma,
980
+ contentPaths: project.contentPaths,
981
+ contentCounts
982
+ });
983
+ } else if (project.contentPaths.length > 0 && !options.importPath) {
984
+ answers.importContent = true;
985
+ answers.importPath = project.contentPaths[0];
986
+ }
987
+ const prismaPath = project.prismaSchemaPath || path6.join(cwd, "prisma", "schema.prisma");
988
+ const conflicts = checkConflicts(prismaPath);
989
+ if (conflicts.length > 0) {
990
+ console.log(import_picocolors3.default.red(`
991
+ Error: Found conflicting model names in your Prisma schema:`));
992
+ conflicts.forEach((c) => console.log(import_picocolors3.default.red(` - ${c}`)));
993
+ console.log("\nPlease rename these models before running autoblogger init.");
994
+ return;
995
+ }
996
+ log("check", "No model conflicts found");
997
+ if (options.dryRun) {
998
+ console.log(import_picocolors3.default.cyan("\n--- Dry run mode: showing what would be done ---\n"));
999
+ console.log("Would create/update:");
1000
+ console.log(` - ${prismaPath} (add 11 models)`);
1001
+ console.log(` - lib/cms.ts`);
1002
+ console.log(` - ${project.appRouterPath}/api/cms/[...path]/route.ts`);
1003
+ console.log(` - ${project.appRouterPath}/writer/[[...path]]/page.tsx`);
1004
+ if (project.tailwindConfigPath) {
1005
+ console.log(` - ${project.tailwindConfigPath} (add content path)`);
1006
+ }
1007
+ if (answers.runMigration) {
1008
+ console.log("\nWould run:");
1009
+ console.log(" - npx prisma migrate dev --name add-autoblogger");
1010
+ console.log(" - npx prisma generate");
1011
+ }
1012
+ if (answers.importContent && answers.importPath) {
1013
+ const count = contentCounts[answers.importPath] || countMarkdownFiles(path6.join(cwd, answers.importPath));
1014
+ console.log(`
1015
+ Would import ${count} posts from ${answers.importPath}`);
1016
+ }
1017
+ console.log(import_picocolors3.default.cyan("\n--- No changes made ---"));
1018
+ return;
1019
+ }
1020
+ const filesToBackup = [];
1021
+ if (project.hasPrisma) filesToBackup.push("prisma/schema.prisma");
1022
+ if (project.tailwindConfigPath) filesToBackup.push(path6.relative(cwd, project.tailwindConfigPath));
1023
+ if (fs7.existsSync(path6.join(cwd, "lib", "cms.ts"))) filesToBackup.push("lib/cms.ts");
1024
+ if (filesToBackup.length > 0) {
1025
+ const backupPath = createBackup(filesToBackup, cwd);
1026
+ log("backup", `Created backup at ${path6.relative(cwd, backupPath)}`);
1027
+ }
1028
+ const mergeResult = mergeSchema(prismaPath, answers.dbProvider);
1029
+ if (!mergeResult.success) {
1030
+ console.log(import_picocolors3.default.red("Error: Failed to merge Prisma schema"));
1031
+ return;
1032
+ }
1033
+ writeSchema(prismaPath, mergeResult.content);
1034
+ log("write", `Updated ${path6.relative(cwd, prismaPath)} (added 11 models)`);
1035
+ const libDir = path6.join(cwd, "lib");
1036
+ const cmsConfigPath = path6.join(libDir, "cms.ts");
1037
+ if (fs7.existsSync(cmsConfigPath)) {
1038
+ log("skip", "lib/cms.ts already exists");
1039
+ } else {
1040
+ if (!fs7.existsSync(libDir)) {
1041
+ fs7.mkdirSync(libDir, { recursive: true });
1042
+ }
1043
+ fs7.writeFileSync(cmsConfigPath, CMS_CONFIG_TEMPLATE);
1044
+ log("write", "Created lib/cms.ts");
1045
+ }
1046
+ const apiRoutePath = path6.join(cwd, project.appRouterPath, "api", "cms", "[...path]", "route.ts");
1047
+ if (fs7.existsSync(apiRoutePath)) {
1048
+ log("skip", `${project.appRouterPath}/api/cms/[...path]/route.ts already exists`);
1049
+ } else {
1050
+ const apiRouteDir = path6.dirname(apiRoutePath);
1051
+ if (!fs7.existsSync(apiRouteDir)) {
1052
+ fs7.mkdirSync(apiRouteDir, { recursive: true });
1053
+ }
1054
+ fs7.writeFileSync(apiRoutePath, API_ROUTE_TEMPLATE);
1055
+ log("write", `Created ${project.appRouterPath}/api/cms/[...path]/route.ts`);
1056
+ }
1057
+ const dashboardPath = path6.join(cwd, project.appRouterPath, "writer", "[[...path]]", "page.tsx");
1058
+ if (fs7.existsSync(dashboardPath)) {
1059
+ log("skip", `${project.appRouterPath}/writer/[[...path]]/page.tsx already exists`);
1060
+ } else {
1061
+ const dashboardDir = path6.dirname(dashboardPath);
1062
+ if (!fs7.existsSync(dashboardDir)) {
1063
+ fs7.mkdirSync(dashboardDir, { recursive: true });
1064
+ }
1065
+ fs7.writeFileSync(dashboardPath, DASHBOARD_PAGE_TEMPLATE);
1066
+ log("write", `Created ${project.appRouterPath}/writer/[[...path]]/page.tsx`);
1067
+ }
1068
+ if (project.tailwindConfigPath) {
1069
+ const patchResult = patchTailwindConfig(project.tailwindConfigPath);
1070
+ if (patchResult.alreadyPatched) {
1071
+ log("skip", "Tailwind config already includes autoblogger");
1072
+ } else if (patchResult.success && patchResult.content) {
1073
+ writeTailwindConfig(project.tailwindConfigPath, patchResult.content);
1074
+ log("write", `Updated ${path6.basename(project.tailwindConfigPath)}`);
1075
+ } else {
1076
+ log("warn", "Could not auto-patch Tailwind config. Please add manually:");
1077
+ console.log(import_picocolors3.default.gray(" content: ['./node_modules/autoblogger/dist/**/*.{js,mjs}']"));
1078
+ }
1079
+ } else if (project.tailwindCssPath) {
1080
+ const patchResult = patchTailwindCssConfig(project.tailwindCssPath);
1081
+ if (patchResult.alreadyPatched) {
1082
+ log("skip", "Tailwind CSS config already includes autoblogger");
1083
+ } else if (patchResult.success && patchResult.content) {
1084
+ writeTailwindConfig(project.tailwindCssPath, patchResult.content);
1085
+ log("write", `Updated ${path6.basename(project.tailwindCssPath)} (Tailwind v4)`);
1086
+ } else {
1087
+ log("warn", "Could not auto-patch Tailwind v4 CSS config. Please add manually:");
1088
+ console.log(import_picocolors3.default.gray(' @source "./node_modules/autoblogger/dist/**/*.{js,mjs}";'));
1089
+ }
1090
+ }
1091
+ const globalsCssPath = findGlobalsCss(cwd);
1092
+ if (globalsCssPath) {
1093
+ const cssResult = patchGlobalsCss(globalsCssPath);
1094
+ if (cssResult.alreadyPatched) {
1095
+ log("skip", "globals.css already imports autoblogger styles");
1096
+ } else if (cssResult.success) {
1097
+ log("write", `Updated ${path6.relative(cwd, globalsCssPath)} (added autoblogger CSS import)`);
1098
+ }
1099
+ } else {
1100
+ log("warn", "Could not find globals.css. Please add manually:");
1101
+ console.log(import_picocolors3.default.gray(" @import 'autoblogger/styles/autoblogger.css';"));
1102
+ }
1103
+ if (answers.runMigration) {
1104
+ console.log("");
1105
+ log("run", "prisma migrate dev --name add-autoblogger");
1106
+ try {
1107
+ (0, import_child_process2.execSync)("npx prisma migrate dev --name add-autoblogger", {
1108
+ cwd,
1109
+ stdio: "inherit"
1110
+ });
1111
+ log("run", "prisma generate");
1112
+ (0, import_child_process2.execSync)("npx prisma generate", {
1113
+ cwd,
1114
+ stdio: "inherit"
1115
+ });
1116
+ } catch (error) {
1117
+ log("error", "Migration failed. You may need to run it manually:");
1118
+ console.log(import_picocolors3.default.gray(" npx prisma migrate dev --name add-autoblogger"));
1119
+ console.log(import_picocolors3.default.gray(" npx prisma generate"));
1120
+ }
1121
+ }
1122
+ if (answers.importContent && answers.importPath) {
1123
+ console.log("");
1124
+ const count = contentCounts[answers.importPath] || countMarkdownFiles(path6.join(cwd, answers.importPath));
1125
+ log("info", `Found ${count} markdown files in ${answers.importPath}`);
1126
+ try {
1127
+ await importContent(path6.join(cwd, answers.importPath), { status: "draft" });
1128
+ } catch (error) {
1129
+ log("warn", `Content import failed: ${error instanceof Error ? error.message : error}`);
1130
+ console.log(import_picocolors3.default.gray(" You can import later with: npx autoblogger import <path>"));
1131
+ }
1132
+ }
1133
+ console.log("");
1134
+ console.log(import_picocolors3.default.green(import_picocolors3.default.bold("Done!")) + " Visit " + import_picocolors3.default.cyan("localhost:3000/writer") + " to access your CMS dashboard.");
1135
+ console.log("");
1136
+ console.log(import_picocolors3.default.gray("Next steps:"));
1137
+ console.log(import_picocolors3.default.gray(" 1. Update lib/cms.ts with your auth configuration"));
1138
+ console.log(import_picocolors3.default.gray(" 2. Add your auth check to app/writer/[[...path]]/page.tsx"));
1139
+ console.log(import_picocolors3.default.gray(" 3. Set ANTHROPIC_API_KEY and/or OPENAI_API_KEY for AI features"));
1140
+ console.log("");
1141
+ }
1142
+
1143
+ // src/cli/index.ts
1144
+ var import_picocolors4 = __toESM(require("picocolors"));
1145
+ var VERSION = "0.1.0";
1146
+ var HELP = `
1147
+ ${import_picocolors4.default.bold("autoblogger")} - CLI for setting up Autoblogger CMS
1148
+
1149
+ ${import_picocolors4.default.bold("Usage:")}
1150
+ npx autoblogger <command> [options]
1151
+
1152
+ ${import_picocolors4.default.bold("Commands:")}
1153
+ init Set up Autoblogger in your Next.js project
1154
+ import <path> Import markdown/MDX content into the database
1155
+
1156
+ ${import_picocolors4.default.bold("Init Options:")}
1157
+ --yes Skip prompts and use defaults
1158
+ --skip-migrate Don't run database migration
1159
+ --import=<path> Import content from specified path after setup
1160
+ --dry-run Show what would be done without making changes
1161
+
1162
+ ${import_picocolors4.default.bold("Import Options:")}
1163
+ --status=<status> Set imported posts status (draft, published) [default: draft]
1164
+ --tag=<tag> Add a tag to all imported posts
1165
+ --dry-run Show what would be imported without making changes
1166
+
1167
+ ${import_picocolors4.default.bold("Examples:")}
1168
+ npx autoblogger init
1169
+ npx autoblogger init --yes
1170
+ npx autoblogger init --import=./content/posts
1171
+ npx autoblogger import ./posts --status=draft
1172
+ `;
1173
+ async function main() {
1174
+ const args = process.argv.slice(2);
1175
+ const command = args[0];
1176
+ const flags = {
1177
+ yes: args.includes("--yes") || args.includes("-y"),
1178
+ skipMigrate: args.includes("--skip-migrate"),
1179
+ dryRun: args.includes("--dry-run"),
1180
+ help: args.includes("--help") || args.includes("-h"),
1181
+ version: args.includes("--version") || args.includes("-v"),
1182
+ import: args.find((a) => a.startsWith("--import="))?.split("=")[1],
1183
+ status: args.find((a) => a.startsWith("--status="))?.split("=")[1],
1184
+ tag: args.find((a) => a.startsWith("--tag="))?.split("=")[1]
1185
+ };
1186
+ if (flags.version) {
1187
+ console.log(`autoblogger v${VERSION}`);
1188
+ process.exit(0);
1189
+ }
1190
+ if (flags.help || !command) {
1191
+ console.log(HELP);
1192
+ process.exit(0);
1193
+ }
1194
+ try {
1195
+ if (command === "init") {
1196
+ await init({
1197
+ yes: flags.yes,
1198
+ skipMigrate: flags.skipMigrate,
1199
+ importPath: flags.import,
1200
+ dryRun: flags.dryRun
1201
+ });
1202
+ } else if (command === "import") {
1203
+ const path7 = args[1];
1204
+ if (!path7) {
1205
+ console.error(import_picocolors4.default.red("Error: Please specify a path to import from"));
1206
+ console.log("\nUsage: npx autoblogger import <path>");
1207
+ process.exit(1);
1208
+ }
1209
+ await importContent(path7, {
1210
+ status: flags.status || "draft",
1211
+ tag: flags.tag,
1212
+ dryRun: flags.dryRun
1213
+ });
1214
+ } else {
1215
+ console.error(import_picocolors4.default.red(`Unknown command: ${command}`));
1216
+ console.log(HELP);
1217
+ process.exit(1);
1218
+ }
1219
+ } catch (error) {
1220
+ console.error(import_picocolors4.default.red("Error:"), error instanceof Error ? error.message : error);
1221
+ process.exit(1);
1222
+ }
1223
+ }
1224
+ main();