@vexblocks/cli 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -7
  2. package/dist/index.js +603 -607
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,101 +4,12 @@
4
4
  import { Command } from "commander";
5
5
  import pc6 from "picocolors";
6
6
 
7
- // src/commands/init.ts
7
+ // src/commands/add.ts
8
8
  import path4 from "path";
9
+ import { checkbox, confirm } from "@inquirer/prompts";
9
10
  import fs4 from "fs-extra";
10
- import pc2 from "picocolors";
11
11
  import ora from "ora";
12
- import { confirm, input, select } from "@inquirer/prompts";
13
-
14
- // src/utils/logger.ts
15
- import pc from "picocolors";
16
- var logger = {
17
- info: (message) => {
18
- console.log(pc.blue("\u2139"), message);
19
- },
20
- success: (message) => {
21
- console.log(pc.green("\u2714"), message);
22
- },
23
- warn: (message) => {
24
- console.log(pc.yellow("\u26A0"), message);
25
- },
26
- error: (message) => {
27
- console.log(pc.red("\u2716"), message);
28
- },
29
- log: (message) => {
30
- console.log(message);
31
- },
32
- break: () => {
33
- console.log();
34
- },
35
- title: (message) => {
36
- console.log();
37
- console.log(pc.bold(pc.cyan(message)));
38
- console.log();
39
- },
40
- step: (step, total, message) => {
41
- console.log(pc.dim(`[${step}/${total}]`), message);
42
- },
43
- list: (items) => {
44
- for (const item of items) {
45
- console.log(pc.dim(" \u2022"), item);
46
- }
47
- },
48
- box: (title, content) => {
49
- const maxLength = Math.max(title.length, ...content.map((c) => c.length));
50
- const border = "\u2500".repeat(maxLength + 4);
51
- console.log();
52
- console.log(pc.dim(`\u250C${border}\u2510`));
53
- console.log(pc.dim("\u2502"), pc.bold(title.padEnd(maxLength + 2)), pc.dim("\u2502"));
54
- console.log(pc.dim(`\u251C${border}\u2524`));
55
- for (const line of content) {
56
- console.log(pc.dim("\u2502"), line.padEnd(maxLength + 2), pc.dim("\u2502"));
57
- }
58
- console.log(pc.dim(`\u2514${border}\u2518`));
59
- console.log();
60
- },
61
- code: (code, language) => {
62
- console.log();
63
- console.log(pc.dim(`\`\`\`${language || ""}`));
64
- console.log(code);
65
- console.log(pc.dim("```"));
66
- console.log();
67
- }
68
- };
69
-
70
- // src/utils/fs.ts
71
- import fs from "fs-extra";
72
- import path from "path";
73
- import crypto from "crypto";
74
- async function isTurborepoProject(cwd) {
75
- const turboJsonPath = path.join(cwd, "turbo.json");
76
- return fs.pathExists(turboJsonPath);
77
- }
78
- async function getPackageManager(cwd) {
79
- if (await fs.pathExists(path.join(cwd, "pnpm-lock.yaml"))) {
80
- return "pnpm";
81
- }
82
- if (await fs.pathExists(path.join(cwd, "yarn.lock"))) {
83
- return "yarn";
84
- }
85
- if (await fs.pathExists(path.join(cwd, "bun.lockb"))) {
86
- return "bun";
87
- }
88
- return "npm";
89
- }
90
- async function createBackup(filePath) {
91
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
92
- const backupPath = `${filePath}.backup-${timestamp}`;
93
- if (await fs.pathExists(filePath)) {
94
- await fs.copy(filePath, backupPath);
95
- }
96
- return backupPath;
97
- }
98
-
99
- // src/utils/manifest.ts
100
- import fs2 from "fs-extra";
101
- import path2 from "path";
12
+ import pc2 from "picocolors";
102
13
 
103
14
  // src/utils/constants.ts
104
15
  var GITHUB_REPO = "vexblocks/vexblocks";
@@ -151,33 +62,38 @@ var OPTIONAL_ENV_VARS = {
151
62
  ]
152
63
  };
153
64
 
154
- // src/utils/manifest.ts
155
- async function readManifest(cwd) {
156
- const manifestPath = path2.join(cwd, MANIFEST_FILE);
157
- try {
158
- if (await fs2.pathExists(manifestPath)) {
159
- return await fs2.readJson(manifestPath);
160
- }
161
- return null;
162
- } catch {
163
- return null;
164
- }
65
+ // src/utils/fs.ts
66
+ import crypto from "crypto";
67
+ import path from "path";
68
+ import fs from "fs-extra";
69
+ async function isTurborepoProject(cwd) {
70
+ const turboJsonPath = path.join(cwd, "turbo.json");
71
+ return fs.pathExists(turboJsonPath);
165
72
  }
166
- async function writeManifest(cwd, manifest) {
167
- const manifestPath = path2.join(cwd, MANIFEST_FILE);
168
- await fs2.writeJson(manifestPath, manifest, { spaces: 2 });
73
+ async function getPackageManager(cwd) {
74
+ if (await fs.pathExists(path.join(cwd, "pnpm-lock.yaml"))) {
75
+ return "pnpm";
76
+ }
77
+ if (await fs.pathExists(path.join(cwd, "yarn.lock"))) {
78
+ return "yarn";
79
+ }
80
+ if (await fs.pathExists(path.join(cwd, "bun.lockb"))) {
81
+ return "bun";
82
+ }
83
+ return "npm";
169
84
  }
170
- function createManifest(version) {
171
- return {
172
- $schema: "https://vexblocks.dev/schema/vexblocks.json",
173
- version,
174
- packages: {}
175
- };
85
+ async function createBackup(filePath) {
86
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
87
+ const backupPath = `${filePath}.backup-${timestamp}`;
88
+ if (await fs.pathExists(filePath)) {
89
+ await fs.copy(filePath, backupPath);
90
+ }
91
+ return backupPath;
176
92
  }
177
93
 
178
94
  // src/utils/github.ts
179
- import fs3 from "fs-extra";
180
- import path3 from "path";
95
+ import path2 from "path";
96
+ import fs2 from "fs-extra";
181
97
  async function fetchRemoteManifest() {
182
98
  const url = `${GITHUB_RAW_URL}/templates/manifest.json`;
183
99
  const response = await fetch(url);
@@ -200,9 +116,9 @@ async function downloadFile(remotePath, localPath) {
200
116
  if (!response.ok) {
201
117
  throw new Error(`Failed to download ${remotePath}: ${response.statusText}`);
202
118
  }
203
- await fs3.ensureDir(path3.dirname(localPath));
119
+ await fs2.ensureDir(path2.dirname(localPath));
204
120
  const content = await response.text();
205
- await fs3.writeFile(localPath, content, "utf-8");
121
+ await fs2.writeFile(localPath, content, "utf-8");
206
122
  }
207
123
  async function downloadAndExtractPackage(packagePath, targetDir, onProgress) {
208
124
  const treeUrl = `https://api.github.com/repos/${GITHUB_REPO}/git/trees/${GITHUB_BRANCH}?recursive=1`;
@@ -221,7 +137,7 @@ async function downloadAndExtractPackage(packagePath, targetDir, onProgress) {
221
137
  );
222
138
  for (const file of files) {
223
139
  const relativePath = file.path.slice(packagePath.length + 1);
224
- const localPath = path3.join(targetDir, relativePath);
140
+ const localPath = path2.join(targetDir, relativePath);
225
141
  onProgress?.(relativePath);
226
142
  await downloadFile(file.path, localPath);
227
143
  }
@@ -296,261 +212,98 @@ function compareVersions(a, b) {
296
212
  return 0;
297
213
  }
298
214
 
299
- // src/commands/init.ts
300
- async function initCommand(options) {
301
- const cwd = options.cwd ? path4.resolve(options.cwd) : process.cwd();
302
- logger.title("\u{1F680} VexBlocks CMS Setup");
303
- const manifestPath = path4.join(cwd, MANIFEST_FILE);
304
- if (await fs4.pathExists(manifestPath)) {
305
- logger.warn("VexBlocks is already initialized in this directory.");
306
- const continueAnyway = options.yes || await confirm({
307
- message: "Do you want to reinitialize?",
308
- default: false
309
- });
310
- if (!continueAnyway) {
311
- logger.info("Run `vexblocks add <package>` to add packages.");
312
- return;
313
- }
314
- }
315
- const isTurbo = await isTurborepoProject(cwd);
316
- let projectType;
317
- if (isTurbo) {
318
- logger.success("Detected existing Turborepo project");
319
- projectType = "existing";
320
- } else {
321
- projectType = await select({
322
- message: "What would you like to do?",
323
- choices: [
324
- {
325
- name: "Create a new Turborepo project with VexBlocks",
326
- value: "new"
327
- },
328
- {
329
- name: "Add VexBlocks to this directory (I'll set up Turborepo manually)",
330
- value: "existing"
331
- }
332
- ]
333
- });
334
- }
335
- if (projectType === "new") {
336
- await createNewProject(cwd, options);
337
- } else {
338
- await initializeExisting(cwd, options);
339
- }
340
- }
341
- async function createNewProject(cwd, options) {
342
- const projectName = await input({
343
- message: "Project name:",
344
- default: path4.basename(cwd),
345
- validate: (value) => {
346
- if (!value) return "Project name is required";
347
- if (!/^[a-z0-9-_]+$/.test(value)) {
348
- return "Project name can only contain lowercase letters, numbers, hyphens, and underscores";
349
- }
350
- return true;
215
+ // src/utils/logger.ts
216
+ import pc from "picocolors";
217
+ var logger = {
218
+ info: (message) => {
219
+ console.log(pc.blue("\u2139"), message);
220
+ },
221
+ success: (message) => {
222
+ console.log(pc.green("\u2714"), message);
223
+ },
224
+ warn: (message) => {
225
+ console.log(pc.yellow("\u26A0"), message);
226
+ },
227
+ error: (message) => {
228
+ console.log(pc.red("\u2716"), message);
229
+ },
230
+ log: (message) => {
231
+ console.log(message);
232
+ },
233
+ break: () => {
234
+ console.log();
235
+ },
236
+ title: (message) => {
237
+ console.log();
238
+ console.log(pc.bold(pc.cyan(message)));
239
+ console.log();
240
+ },
241
+ step: (step, total, message) => {
242
+ console.log(pc.dim(`[${step}/${total}]`), message);
243
+ },
244
+ list: (items) => {
245
+ for (const item of items) {
246
+ console.log(pc.dim(" \u2022"), item);
351
247
  }
352
- });
353
- const projectDir = path4.join(cwd, projectName);
354
- if (await fs4.pathExists(projectDir)) {
355
- const overwrite = await confirm({
356
- message: `Directory ${projectName} already exists. Overwrite?`,
357
- default: false
358
- });
359
- if (!overwrite) {
360
- logger.info("Aborted.");
361
- return;
248
+ },
249
+ box: (title, content) => {
250
+ const maxLength = Math.max(title.length, ...content.map((c) => c.length));
251
+ const border = "\u2500".repeat(maxLength + 4);
252
+ console.log();
253
+ console.log(pc.dim(`\u250C${border}\u2510`));
254
+ console.log(pc.dim("\u2502"), pc.bold(title.padEnd(maxLength + 2)), pc.dim("\u2502"));
255
+ console.log(pc.dim(`\u251C${border}\u2524`));
256
+ for (const line of content) {
257
+ console.log(pc.dim("\u2502"), line.padEnd(maxLength + 2), pc.dim("\u2502"));
362
258
  }
363
- await fs4.remove(projectDir);
259
+ console.log(pc.dim(`\u2514${border}\u2518`));
260
+ console.log();
261
+ },
262
+ code: (code, language) => {
263
+ console.log();
264
+ console.log(pc.dim(`\`\`\`${language || ""}`));
265
+ console.log(code);
266
+ console.log(pc.dim("```"));
267
+ console.log();
364
268
  }
365
- const spinner = ora("Creating project structure...").start();
269
+ };
270
+
271
+ // src/utils/manifest.ts
272
+ import path3 from "path";
273
+ import fs3 from "fs-extra";
274
+ async function readManifest(cwd) {
275
+ const manifestPath = path3.join(cwd, MANIFEST_FILE);
366
276
  try {
367
- await fs4.ensureDir(projectDir);
368
- await fs4.ensureDir(path4.join(projectDir, "apps"));
369
- await fs4.ensureDir(path4.join(projectDir, "packages"));
370
- const packageJson = {
371
- name: projectName,
372
- version: "0.1.0",
373
- private: true,
374
- packageManager: "pnpm@9.15.0",
375
- workspaces: ["apps/*", "packages/*"],
376
- scripts: {
377
- dev: "turbo run dev",
378
- build: "turbo run build",
379
- lint: "turbo run lint",
380
- "generate-types": "pnpm --filter @repo/type-generator generate"
381
- },
382
- devDependencies: {
383
- turbo: "^2.6.1"
384
- }
385
- };
386
- await fs4.writeJson(path4.join(projectDir, "package.json"), packageJson, {
387
- spaces: 2
388
- });
389
- const turboJson = {
390
- $schema: "https://turbo.build/schema.json",
391
- globalDependencies: ["**/.env.*local"],
392
- globalEnv: [
393
- "CONVEX_DEPLOYMENT",
394
- "NEXT_PUBLIC_CONVEX_URL",
395
- "NEXT_PUBLIC_CONVEX_SITE_URL",
396
- "SITE_URL",
397
- "CLOUDFLARE_ACCOUNT_ID",
398
- "CLOUDFLARE_SECRET_TOKEN"
399
- ],
400
- ui: "tui",
401
- tasks: {
402
- dev: {
403
- cache: false,
404
- persistent: true
405
- },
406
- build: {
407
- dependsOn: ["^build"],
408
- inputs: ["$TURBO_DEFAULT$", ".env*"],
409
- outputs: [".next/**", "dist/**"]
410
- },
411
- lint: {
412
- outputs: []
413
- }
414
- }
415
- };
416
- await fs4.writeJson(path4.join(projectDir, "turbo.json"), turboJson, {
417
- spaces: 2
418
- });
419
- const pnpmWorkspace = `packages:
420
- - "apps/*"
421
- - "packages/*"
422
- `;
423
- await fs4.writeFile(
424
- path4.join(projectDir, "pnpm-workspace.yaml"),
425
- pnpmWorkspace
426
- );
427
- const gitignore = `# Dependencies
428
- node_modules
429
- .pnpm-store
430
-
431
- # Build outputs
432
- dist
433
- .next
434
- .turbo
435
-
436
- # Environment files
437
- .env
438
- .env.local
439
- .env.*.local
440
-
441
- # IDE
442
- .idea
443
- .vscode
444
- *.swp
445
- *.swo
446
-
447
- # OS
448
- .DS_Store
449
- Thumbs.db
450
-
451
- # Logs
452
- *.log
453
- npm-debug.log*
454
- pnpm-debug.log*
455
-
456
- # Convex
457
- .convex
458
- `;
459
- await fs4.writeFile(path4.join(projectDir, ".gitignore"), gitignore);
460
- const envExample = `# Convex
461
- CONVEX_DEPLOYMENT=
462
- NEXT_PUBLIC_CONVEX_URL=
463
-
464
- # Better Auth
465
- SITE_URL=http://localhost:3001
466
-
467
- # Cloudflare Images (optional, for media library)
468
- CLOUDFLARE_ACCOUNT_ID=
469
- CLOUDFLARE_SECRET_TOKEN=
470
-
471
- # Revalidation (optional, for ISR)
472
- REVALIDATE_SECRET=
473
- FRONTEND_URL=http://localhost:3000
474
- `;
475
- await fs4.writeFile(path4.join(projectDir, ".env.example"), envExample);
476
- const version = await getLatestVersion();
477
- const manifest = createManifest(version);
478
- await writeManifest(projectDir, manifest);
479
- spinner.succeed("Project structure created");
480
- logger.break();
481
- logger.box("\u2705 Project created successfully!", [
482
- `Directory: ${pc2.cyan(projectDir)}`,
483
- "",
484
- pc2.bold("Next steps:"),
485
- `1. ${pc2.cyan(`cd ${projectName}`)}`,
486
- `2. ${pc2.cyan("pnpm install")}`,
487
- `3. ${pc2.cyan("npx vexblocks add all")}`,
488
- "4. Set up your Convex project",
489
- `5. ${pc2.cyan("pnpm dev")}`
490
- ]);
491
- const addPackages = options.yes || await confirm({
492
- message: "Would you like to add VexBlocks packages now?",
493
- default: true
494
- });
495
- if (addPackages) {
496
- logger.break();
497
- logger.info(`Run: ${pc2.cyan("npx vexblocks add all")}`);
498
- logger.info("After navigating to your project directory.");
277
+ if (await fs3.pathExists(manifestPath)) {
278
+ return await fs3.readJson(manifestPath);
499
279
  }
500
- } catch (error) {
501
- spinner.fail("Failed to create project");
502
- throw error;
280
+ return null;
281
+ } catch {
282
+ return null;
503
283
  }
504
284
  }
505
- async function initializeExisting(cwd, _options) {
506
- const spinner = ora("Initializing VexBlocks...").start();
507
- try {
508
- const hasTurbo = await isTurborepoProject(cwd);
509
- if (!hasTurbo) {
510
- spinner.warn("No turbo.json found");
511
- logger.warn(
512
- "VexBlocks requires Turborepo. Please set up Turborepo first."
513
- );
514
- logger.info(`Run: ${pc2.cyan("npx create-turbo@latest")}`);
515
- return;
516
- }
517
- const version = await getLatestVersion();
518
- const manifest = createManifest(version);
519
- await writeManifest(cwd, manifest);
520
- spinner.succeed("VexBlocks initialized");
521
- const packageManager = await getPackageManager(cwd);
522
- logger.break();
523
- logger.box("\u2705 VexBlocks initialized!", [
524
- `Manifest: ${pc2.cyan(MANIFEST_FILE)}`,
525
- `Version: ${pc2.cyan(version)}`,
526
- "",
527
- pc2.bold("Next steps:"),
528
- `1. ${pc2.cyan("npx vexblocks add all")} - Add all CMS packages`,
529
- "2. Configure your environment variables",
530
- `3. ${pc2.cyan(`${packageManager} install`)}`,
531
- `4. ${pc2.cyan(`${packageManager === "npm" ? "npm run" : packageManager} dev`)}`
532
- ]);
533
- } catch (error) {
534
- spinner.fail("Failed to initialize");
535
- throw error;
536
- }
285
+ async function writeManifest(cwd, manifest) {
286
+ const manifestPath = path3.join(cwd, MANIFEST_FILE);
287
+ await fs3.writeJson(manifestPath, manifest, { spaces: 2 });
288
+ }
289
+ function createManifest(version) {
290
+ return {
291
+ $schema: "https://vexblocks.com/schema/vexblocks.json",
292
+ version,
293
+ packages: {}
294
+ };
537
295
  }
538
296
 
539
297
  // src/commands/add.ts
540
- import path5 from "path";
541
- import fs5 from "fs-extra";
542
- import pc3 from "picocolors";
543
- import ora2 from "ora";
544
- import { confirm as confirm2, checkbox } from "@inquirer/prompts";
545
298
  var ALL_PACKAGES = ["backend", "shared", "types", "cms"];
546
299
  async function addCommand(packages, options) {
547
- const cwd = options.cwd ? path5.resolve(options.cwd) : process.cwd();
300
+ const cwd = options.cwd ? path4.resolve(options.cwd) : process.cwd();
548
301
  logger.title("\u{1F4E6} Add VexBlocks Packages");
549
302
  const isTurbo = await isTurborepoProject(cwd);
550
303
  if (!isTurbo) {
551
304
  logger.error("This is not a Turborepo project.");
552
305
  logger.info(
553
- `Run ${pc3.cyan("npx vexblocks init")} first to set up your project.`
306
+ `Run ${pc2.cyan("npx @vexblocks/cli init")} first to set up your project.`
554
307
  );
555
308
  process.exit(1);
556
309
  }
@@ -600,13 +353,13 @@ async function addCommand(packages, options) {
600
353
  const existingPackages = [];
601
354
  for (const pkg of resolvedPackages) {
602
355
  const targetPath = getPackageTargetPath(cwd, pkg);
603
- if (await fs5.pathExists(targetPath)) {
356
+ if (await fs4.pathExists(targetPath)) {
604
357
  existingPackages.push(pkg);
605
358
  }
606
359
  }
607
360
  if (existingPackages.length > 0 && !options.overwrite) {
608
361
  logger.warn(`These packages already exist: ${existingPackages.join(", ")}`);
609
- const overwrite = options.yes || await confirm2({
362
+ const overwrite = options.yes || await confirm({
610
363
  message: "Overwrite existing packages?",
611
364
  default: false
612
365
  });
@@ -623,16 +376,16 @@ async function addCommand(packages, options) {
623
376
  }
624
377
  }
625
378
  logger.break();
626
- logger.log(pc3.bold("Packages to install:"));
379
+ logger.log(pc2.bold("Packages to install:"));
627
380
  for (const pkg of resolvedPackages) {
628
381
  const targetPath = getPackageTargetPath(cwd, pkg);
629
- const relativePath = path5.relative(cwd, targetPath);
382
+ const relativePath = path4.relative(cwd, targetPath);
630
383
  logger.log(
631
- ` ${pc3.green("+")} ${PACKAGE_NAMES[pkg]} \u2192 ${pc3.dim(relativePath)}`
384
+ ` ${pc2.green("+")} ${PACKAGE_NAMES[pkg]} \u2192 ${pc2.dim(relativePath)}`
632
385
  );
633
386
  }
634
387
  logger.break();
635
- const proceed = options.yes || await confirm2({
388
+ const proceed = options.yes || await confirm({
636
389
  message: "Proceed with installation?",
637
390
  default: true
638
391
  });
@@ -673,34 +426,34 @@ function resolveDependencies(packages) {
673
426
  function getPackageTargetPath(cwd, pkg) {
674
427
  switch (pkg) {
675
428
  case "cms":
676
- return path5.join(cwd, "apps", "cms");
429
+ return path4.join(cwd, "apps", "cms");
677
430
  case "backend":
678
- return path5.join(cwd, "packages", "backend");
431
+ return path4.join(cwd, "packages", "backend");
679
432
  case "shared":
680
- return path5.join(cwd, "packages", "cms-shared");
433
+ return path4.join(cwd, "packages", "cms-shared");
681
434
  case "types":
682
- return path5.join(cwd, "packages", "type-generator");
435
+ return path4.join(cwd, "packages", "type-generator");
683
436
  default:
684
- return path5.join(cwd, PACKAGE_PATHS[pkg]);
437
+ return path4.join(cwd, PACKAGE_PATHS[pkg]);
685
438
  }
686
439
  }
687
440
  async function installPackage(cwd, pkg, version, manifest) {
688
- const spinner = ora2(`Installing ${PACKAGE_NAMES[pkg]}...`).start();
441
+ const spinner = ora(`Installing ${PACKAGE_NAMES[pkg]}...`).start();
689
442
  try {
690
443
  const targetPath = getPackageTargetPath(cwd, pkg);
691
444
  const sourcePath = PACKAGE_PATHS[pkg];
692
445
  if (pkg === "backend") {
693
446
  await installBackendPackage(targetPath, sourcePath, spinner);
694
447
  } else {
695
- await fs5.ensureDir(targetPath);
448
+ await fs4.ensureDir(targetPath);
696
449
  await downloadAndExtractPackage(sourcePath, targetPath, (file) => {
697
- spinner.text = `Installing ${PACKAGE_NAMES[pkg]}... ${pc3.dim(file)}`;
450
+ spinner.text = `Installing ${PACKAGE_NAMES[pkg]}... ${pc2.dim(file)}`;
698
451
  });
699
452
  }
700
453
  const config = {
701
454
  version,
702
455
  installedAt: (/* @__PURE__ */ new Date()).toISOString(),
703
- path: path5.relative(cwd, targetPath)
456
+ path: path4.relative(cwd, targetPath)
704
457
  };
705
458
  manifest.packages[pkg] = config;
706
459
  spinner.succeed(`Installed ${PACKAGE_NAMES[pkg]}`);
@@ -710,14 +463,14 @@ async function installPackage(cwd, pkg, version, manifest) {
710
463
  }
711
464
  }
712
465
  async function installBackendPackage(targetPath, sourcePath, spinner) {
713
- const convexPath = path5.join(targetPath, "convex");
714
- const existingSchemaPath = path5.join(convexPath, "schema.ts");
715
- const hasSchema = await fs5.pathExists(existingSchemaPath);
466
+ const convexPath = path4.join(targetPath, "convex");
467
+ const existingSchemaPath = path4.join(convexPath, "schema.ts");
468
+ const hasSchema = await fs4.pathExists(existingSchemaPath);
716
469
  if (hasSchema) {
717
470
  spinner.text = "Detected existing Convex schema, merging CMS tables...";
718
471
  const backupPath = `${existingSchemaPath}.backup-${Date.now()}`;
719
- await fs5.copy(existingSchemaPath, backupPath);
720
- logger.info(`Backed up existing schema to ${path5.basename(backupPath)}`);
472
+ await fs4.copy(existingSchemaPath, backupPath);
473
+ logger.info(`Backed up existing schema to ${path4.basename(backupPath)}`);
721
474
  const cmsFiles = [
722
475
  "convex/cms",
723
476
  "convex/schema.cms.ts",
@@ -730,15 +483,15 @@ async function installBackendPackage(targetPath, sourcePath, spinner) {
730
483
  ];
731
484
  for (const file of cmsFiles) {
732
485
  const fullSourcePath = `${sourcePath}/${file}`;
733
- const fullTargetPath = path5.join(targetPath, file);
486
+ const fullTargetPath = path4.join(targetPath, file);
734
487
  spinner.text = `Downloading ${file}...`;
735
488
  try {
736
489
  await downloadAndExtractPackage(fullSourcePath, fullTargetPath);
737
490
  } catch {
738
491
  try {
739
492
  const content = await fetchFile(fullSourcePath);
740
- await fs5.ensureDir(path5.dirname(fullTargetPath));
741
- await fs5.writeFile(fullTargetPath, content);
493
+ await fs4.ensureDir(path4.dirname(fullTargetPath));
494
+ await fs4.writeFile(fullTargetPath, content);
742
495
  } catch {
743
496
  }
744
497
  }
@@ -756,14 +509,14 @@ async function installBackendPackage(targetPath, sourcePath, spinner) {
756
509
  // })
757
510
  // === END VEXBLOCKS CMS ===
758
511
  `;
759
- const existingContent = await fs5.readFile(existingSchemaPath, "utf-8");
512
+ const existingContent = await fs4.readFile(existingSchemaPath, "utf-8");
760
513
  if (!existingContent.includes("VEXBLOCKS CMS")) {
761
- await fs5.appendFile(existingSchemaPath, schemaInstructions);
514
+ await fs4.appendFile(existingSchemaPath, schemaInstructions);
762
515
  }
763
516
  } else {
764
- await fs5.ensureDir(targetPath);
517
+ await fs4.ensureDir(targetPath);
765
518
  await downloadAndExtractPackage(sourcePath, targetPath, (file) => {
766
- spinner.text = `Installing backend... ${pc3.dim(file)}`;
519
+ spinner.text = `Installing backend... ${pc2.dim(file)}`;
767
520
  });
768
521
  }
769
522
  }
@@ -772,28 +525,28 @@ function showNextSteps(packages) {
772
525
  logger.break();
773
526
  logger.box("\u2705 Packages installed successfully!", [
774
527
  "",
775
- pc3.bold("Next steps:"),
528
+ pc2.bold("Next steps:"),
776
529
  "",
777
- `1. Install dependencies: ${pc3.cyan(`${packageManager} install`)}`,
530
+ `1. Install dependencies: ${pc2.cyan(`${packageManager} install`)}`,
778
531
  "",
779
532
  "2. Set up environment variables:",
780
- ...getRequiredEnvVars(packages).map((v) => ` ${pc3.dim(v)}`),
533
+ ...getRequiredEnvVars(packages).map((v) => ` ${pc2.dim(v)}`),
781
534
  "",
782
535
  "3. Set up Convex (if not already):",
783
- ` ${pc3.cyan("npx convex dev")} in packages/backend`,
536
+ ` ${pc2.cyan("npx convex dev")} in packages/backend`,
784
537
  "",
785
538
  "4. Start development:",
786
- ` ${pc3.cyan(`${packageManager} dev`)}`,
539
+ ` ${pc2.cyan(`${packageManager} dev`)}`,
787
540
  "",
788
- pc3.dim("CMS will be available at http://localhost:3001")
541
+ pc2.dim("CMS will be available at http://localhost:3001")
789
542
  ]);
790
543
  const optionalVars = getOptionalEnvVars(packages);
791
544
  if (optionalVars.length > 0) {
792
545
  logger.break();
793
- logger.log(pc3.bold("Optional environment variables:"));
546
+ logger.log(pc2.bold("Optional environment variables:"));
794
547
  for (const { name, description } of optionalVars) {
795
- logger.log(` ${pc3.yellow(name)}`);
796
- logger.log(` ${pc3.dim(description)}`);
548
+ logger.log(` ${pc2.yellow(name)}`);
549
+ logger.log(` ${pc2.dim(description)}`);
797
550
  }
798
551
  }
799
552
  }
@@ -822,219 +575,50 @@ function getOptionalEnvVars(packages) {
822
575
  return vars;
823
576
  }
824
577
 
825
- // src/commands/upgrade.ts
826
- import path6 from "path";
827
- import fs6 from "fs-extra";
828
- import pc4 from "picocolors";
829
- import ora3 from "ora";
830
- import { confirm as confirm3 } from "@inquirer/prompts";
831
- async function upgradeCommand(packages, options) {
832
- const cwd = options.cwd ? path6.resolve(options.cwd) : process.cwd();
833
- logger.title("\u2B06\uFE0F Upgrade VexBlocks Packages");
578
+ // src/commands/diff.ts
579
+ import path5 from "path";
580
+ import { createTwoFilesPatch } from "diff";
581
+ import fs5 from "fs-extra";
582
+ import ora2 from "ora";
583
+ import pc3 from "picocolors";
584
+ async function diffCommand(packageArg, options) {
585
+ const cwd = options.cwd ? path5.resolve(options.cwd) : process.cwd();
586
+ logger.title("\u{1F50D} VexBlocks Diff");
834
587
  const manifest = await readManifest(cwd);
835
588
  if (!manifest) {
836
- logger.error("No vexblocks.json found. Run `vexblocks init` first.");
589
+ logger.error("No vexblocks.json found. Run `npx @vexblocks/cli init` first.");
837
590
  process.exit(1);
838
591
  }
839
- const spinner = ora3("Checking for updates...").start();
840
- const latestVersion = await getLatestVersion();
841
- spinner.stop();
842
592
  const installedPackages = Object.keys(manifest.packages);
843
593
  if (installedPackages.length === 0) {
844
- logger.info("No packages installed. Run `vexblocks add <package>` first.");
594
+ logger.info("No packages installed.");
845
595
  return;
846
596
  }
847
- let packagesToUpgrade;
848
- if (packages.length === 0) {
849
- packagesToUpgrade = installedPackages;
850
- } else {
851
- packagesToUpgrade = packages.filter(
852
- (p) => installedPackages.includes(p)
853
- );
854
- if (packagesToUpgrade.length === 0) {
855
- logger.error("None of the specified packages are installed.");
597
+ let packageToDiff;
598
+ if (packageArg) {
599
+ if (!installedPackages.includes(packageArg)) {
600
+ logger.error(`Package "${packageArg}" is not installed.`);
601
+ logger.info(`Installed packages: ${installedPackages.join(", ")}`);
856
602
  return;
857
603
  }
858
- }
859
- const updates = [];
860
- for (const pkg of packagesToUpgrade) {
861
- const config = manifest.packages[pkg];
862
- if (!config) continue;
863
- const comparison = compareVersions(config.version, latestVersion);
864
- if (comparison < 0) {
865
- updates.push({
866
- pkg,
867
- from: config.version,
868
- to: latestVersion
869
- });
870
- }
871
- }
872
- if (updates.length === 0) {
873
- logger.success("All packages are up to date!");
874
- logger.info(`Current version: ${pc4.cyan(latestVersion)}`);
875
- return;
876
- }
877
- logger.log(pc4.bold("\nAvailable updates:"));
878
- logger.break();
879
- for (const update of updates) {
880
- const isManaged = MANAGED_PACKAGES.includes(update.pkg);
881
- logger.log(
882
- ` ${PACKAGE_NAMES[update.pkg]}`,
883
- pc4.dim(`${update.from} \u2192 `),
884
- pc4.green(update.to),
885
- isManaged ? pc4.yellow(" (managed)") : ""
886
- );
887
- }
888
- const changelog = await getChangelog(updates[0].from, latestVersion);
889
- if (Object.keys(changelog).length > 0) {
890
- logger.break();
891
- logger.log(pc4.bold("Changelog:"));
892
- for (const [version, changes] of Object.entries(changelog)) {
893
- logger.log(`
894
- ${pc4.cyan(version)}`);
895
- for (const change of changes) {
896
- logger.log(` ${pc4.dim("\u2022")} ${change}`);
897
- }
898
- }
899
- }
900
- if (options.check) {
901
- logger.break();
902
- logger.info(
903
- `Run ${pc4.cyan("npx vexblocks upgrade")} to apply these updates.`
904
- );
905
- return;
906
- }
907
- logger.break();
908
- const proceed = options.yes || await confirm3({
909
- message: `Upgrade ${updates.length} package(s)?`,
910
- default: true
911
- });
912
- if (!proceed) {
913
- logger.info("Aborted.");
604
+ packageToDiff = packageArg;
605
+ } else if (installedPackages.length === 1) {
606
+ packageToDiff = installedPackages[0];
607
+ } else {
608
+ logger.error("Please specify a package to diff.");
609
+ logger.info(`Usage: ${pc3.cyan("vexblocks diff <package>")}`);
610
+ logger.info(`Installed packages: ${installedPackages.join(", ")}`);
914
611
  return;
915
612
  }
916
- for (const update of updates) {
917
- await upgradePackage(cwd, update.pkg, update.to, manifest, options);
918
- }
919
- manifest.version = latestVersion;
920
- await writeManifest(cwd, manifest);
921
- logger.break();
922
- logger.success(`Upgraded to version ${pc4.cyan(latestVersion)}`);
923
- logger.info(
924
- "Run your package manager's install command to update dependencies."
925
- );
926
- }
927
- async function upgradePackage(cwd, pkg, version, manifest, options) {
928
- const spinner = ora3(`Upgrading ${PACKAGE_NAMES[pkg]}...`).start();
613
+ const spinner = ora2(`Comparing ${PACKAGE_NAMES[packageToDiff]}...`).start();
929
614
  try {
930
- const config = manifest.packages[pkg];
615
+ const config = manifest.packages[packageToDiff];
931
616
  if (!config) {
932
- spinner.skip(`${PACKAGE_NAMES[pkg]} not installed`);
933
- return;
934
- }
935
- const targetPath = path6.join(cwd, config.path);
936
- if (!await fs6.pathExists(targetPath)) {
937
- spinner.warn(
938
- `${PACKAGE_NAMES[pkg]} directory not found at ${config.path}`
939
- );
617
+ spinner.fail("Package configuration not found");
940
618
  return;
941
619
  }
942
- const isManaged = MANAGED_PACKAGES.includes(pkg);
943
- if (isManaged || options.force) {
944
- const backupPath = await createBackup(targetPath);
945
- spinner.text = `Created backup at ${path6.basename(backupPath)}`;
946
- await fs6.remove(targetPath);
947
- const sourcePath = getSourcePath(pkg);
948
- await downloadAndExtractPackage(sourcePath, targetPath, (file) => {
949
- spinner.text = `Upgrading ${PACKAGE_NAMES[pkg]}... ${pc4.dim(file)}`;
950
- });
951
- } else {
952
- spinner.text = `Upgrading CMS files in ${PACKAGE_NAMES[pkg]}...`;
953
- const cmsFiles = ["convex/cms", "convex/schema.cms.ts"];
954
- for (const file of cmsFiles) {
955
- const targetFilePath = path6.join(targetPath, file);
956
- const sourceFilePath = `packages/backend/${file}`;
957
- if (await fs6.pathExists(targetFilePath)) {
958
- await createBackup(targetFilePath);
959
- }
960
- try {
961
- await fs6.remove(targetFilePath);
962
- await downloadAndExtractPackage(sourceFilePath, targetFilePath);
963
- } catch {
964
- }
965
- }
966
- }
967
- const newConfig = {
968
- ...config,
969
- version,
970
- installedAt: (/* @__PURE__ */ new Date()).toISOString()
971
- };
972
- manifest.packages[pkg] = newConfig;
973
- spinner.succeed(`Upgraded ${PACKAGE_NAMES[pkg]} to ${version}`);
974
- } catch (error) {
975
- spinner.fail(`Failed to upgrade ${PACKAGE_NAMES[pkg]}`);
976
- throw error;
977
- }
978
- }
979
- function getSourcePath(pkg) {
980
- switch (pkg) {
981
- case "cms":
982
- return "apps/cms";
983
- case "backend":
984
- return "packages/backend";
985
- case "shared":
986
- return "packages/cms-shared";
987
- case "types":
988
- return "packages/type-generator";
989
- default:
990
- throw new Error(`Unknown package: ${pkg}`);
991
- }
992
- }
993
-
994
- // src/commands/diff.ts
995
- import path7 from "path";
996
- import fs7 from "fs-extra";
997
- import pc5 from "picocolors";
998
- import ora4 from "ora";
999
- import { createTwoFilesPatch } from "diff";
1000
- async function diffCommand(packageArg, options) {
1001
- const cwd = options.cwd ? path7.resolve(options.cwd) : process.cwd();
1002
- logger.title("\u{1F50D} VexBlocks Diff");
1003
- const manifest = await readManifest(cwd);
1004
- if (!manifest) {
1005
- logger.error("No vexblocks.json found. Run `vexblocks init` first.");
1006
- process.exit(1);
1007
- }
1008
- const installedPackages = Object.keys(manifest.packages);
1009
- if (installedPackages.length === 0) {
1010
- logger.info("No packages installed.");
1011
- return;
1012
- }
1013
- let packageToDiff;
1014
- if (packageArg) {
1015
- if (!installedPackages.includes(packageArg)) {
1016
- logger.error(`Package "${packageArg}" is not installed.`);
1017
- logger.info(`Installed packages: ${installedPackages.join(", ")}`);
1018
- return;
1019
- }
1020
- packageToDiff = packageArg;
1021
- } else if (installedPackages.length === 1) {
1022
- packageToDiff = installedPackages[0];
1023
- } else {
1024
- logger.error("Please specify a package to diff.");
1025
- logger.info(`Usage: ${pc5.cyan("vexblocks diff <package>")}`);
1026
- logger.info(`Installed packages: ${installedPackages.join(", ")}`);
1027
- return;
1028
- }
1029
- const spinner = ora4(`Comparing ${PACKAGE_NAMES[packageToDiff]}...`).start();
1030
- try {
1031
- const config = manifest.packages[packageToDiff];
1032
- if (!config) {
1033
- spinner.fail("Package configuration not found");
1034
- return;
1035
- }
1036
- const localPath = path7.join(cwd, config.path);
1037
- const remotePath = getSourcePath2(packageToDiff);
620
+ const localPath = path5.join(cwd, config.path);
621
+ const remotePath = getSourcePath(packageToDiff);
1038
622
  const remoteFiles = await getPackageFiles(remotePath);
1039
623
  let totalDiffs = 0;
1040
624
  let newFiles = 0;
@@ -1042,12 +626,12 @@ async function diffCommand(packageArg, options) {
1042
626
  let deletedFiles = 0;
1043
627
  spinner.stop();
1044
628
  for (const file of remoteFiles) {
1045
- const localFilePath = path7.join(localPath, file);
629
+ const localFilePath = path5.join(localPath, file);
1046
630
  const remoteFilePath = `${remotePath}/${file}`;
1047
631
  let localContent = "";
1048
632
  let remoteContent = "";
1049
- if (await fs7.pathExists(localFilePath)) {
1050
- localContent = await fs7.readFile(localFilePath, "utf-8");
633
+ if (await fs5.pathExists(localFilePath)) {
634
+ localContent = await fs5.readFile(localFilePath, "utf-8");
1051
635
  }
1052
636
  try {
1053
637
  remoteContent = await fetchFile(remoteFilePath);
@@ -1057,11 +641,11 @@ async function diffCommand(packageArg, options) {
1057
641
  if (!localContent && remoteContent) {
1058
642
  newFiles++;
1059
643
  totalDiffs++;
1060
- logger.log(`${pc5.green("+")} ${file} ${pc5.dim("(new file)")}`);
644
+ logger.log(`${pc3.green("+")} ${file} ${pc3.dim("(new file)")}`);
1061
645
  } else if (localContent !== remoteContent) {
1062
646
  modifiedFiles++;
1063
647
  totalDiffs++;
1064
- logger.log(`${pc5.yellow("~")} ${file} ${pc5.dim("(modified)")}`);
648
+ logger.log(`${pc3.yellow("~")} ${file} ${pc3.dim("(modified)")}`);
1065
649
  const patch = createTwoFilesPatch(
1066
650
  `local/${file}`,
1067
651
  `remote/${file}`,
@@ -1075,14 +659,14 @@ async function diffCommand(packageArg, options) {
1075
659
  let lineCount = 0;
1076
660
  for (const line of lines) {
1077
661
  if (lineCount >= maxLines) {
1078
- logger.log(pc5.dim(` ... (${lines.length - maxLines} more lines)`));
662
+ logger.log(pc3.dim(` ... (${lines.length - maxLines} more lines)`));
1079
663
  break;
1080
664
  }
1081
665
  if (line.startsWith("+") && !line.startsWith("+++")) {
1082
- logger.log(pc5.green(` ${line}`));
666
+ logger.log(pc3.green(` ${line}`));
1083
667
  lineCount++;
1084
668
  } else if (line.startsWith("-") && !line.startsWith("---")) {
1085
- logger.log(pc5.red(` ${line}`));
669
+ logger.log(pc3.red(` ${line}`));
1086
670
  lineCount++;
1087
671
  }
1088
672
  }
@@ -1097,7 +681,7 @@ async function diffCommand(packageArg, options) {
1097
681
  }
1098
682
  deletedFiles++;
1099
683
  totalDiffs++;
1100
- logger.log(`${pc5.red("-")} ${file} ${pc5.dim("(deleted in remote)")}`);
684
+ logger.log(`${pc3.red("-")} ${file} ${pc3.dim("(deleted in remote)")}`);
1101
685
  }
1102
686
  }
1103
687
  logger.break();
@@ -1106,15 +690,15 @@ async function diffCommand(packageArg, options) {
1106
690
  "No differences found. Local files match the latest version."
1107
691
  );
1108
692
  } else {
1109
- logger.log(pc5.bold("Summary:"));
1110
- if (newFiles > 0) logger.log(` ${pc5.green(`+${newFiles}`)} new files`);
693
+ logger.log(pc3.bold("Summary:"));
694
+ if (newFiles > 0) logger.log(` ${pc3.green(`+${newFiles}`)} new files`);
1111
695
  if (modifiedFiles > 0)
1112
- logger.log(` ${pc5.yellow(`~${modifiedFiles}`)} modified files`);
696
+ logger.log(` ${pc3.yellow(`~${modifiedFiles}`)} modified files`);
1113
697
  if (deletedFiles > 0)
1114
- logger.log(` ${pc5.red(`-${deletedFiles}`)} files removed in remote`);
698
+ logger.log(` ${pc3.red(`-${deletedFiles}`)} files removed in remote`);
1115
699
  logger.break();
1116
700
  logger.info(
1117
- `Run ${pc5.cyan("npx vexblocks upgrade")} to update to the latest version.`
701
+ `Run ${pc3.cyan("npx @vexblocks/cli upgrade")} to update to the latest version.`
1118
702
  );
1119
703
  }
1120
704
  } catch (error) {
@@ -1122,7 +706,7 @@ async function diffCommand(packageArg, options) {
1122
706
  throw error;
1123
707
  }
1124
708
  }
1125
- function getSourcePath2(pkg) {
709
+ function getSourcePath(pkg) {
1126
710
  switch (pkg) {
1127
711
  case "cms":
1128
712
  return "apps/cms";
@@ -1138,10 +722,10 @@ function getSourcePath2(pkg) {
1138
722
  }
1139
723
  async function getLocalFiles(dir, base = "") {
1140
724
  const files = [];
1141
- if (!await fs7.pathExists(dir)) {
725
+ if (!await fs5.pathExists(dir)) {
1142
726
  return files;
1143
727
  }
1144
- const entries = await fs7.readdir(dir, { withFileTypes: true });
728
+ const entries = await fs5.readdir(dir, { withFileTypes: true });
1145
729
  for (const entry of entries) {
1146
730
  const relativePath = base ? `${base}/${entry.name}` : entry.name;
1147
731
  if (entry.isDirectory()) {
@@ -1149,7 +733,7 @@ async function getLocalFiles(dir, base = "") {
1149
733
  continue;
1150
734
  }
1151
735
  const subFiles = await getLocalFiles(
1152
- path7.join(dir, entry.name),
736
+ path5.join(dir, entry.name),
1153
737
  relativePath
1154
738
  );
1155
739
  files.push(...subFiles);
@@ -1160,10 +744,422 @@ async function getLocalFiles(dir, base = "") {
1160
744
  return files;
1161
745
  }
1162
746
 
747
+ // src/commands/init.ts
748
+ import path6 from "path";
749
+ import { confirm as confirm2, input, select } from "@inquirer/prompts";
750
+ import fs6 from "fs-extra";
751
+ import ora3 from "ora";
752
+ import pc4 from "picocolors";
753
+ async function initCommand(options) {
754
+ const cwd = options.cwd ? path6.resolve(options.cwd) : process.cwd();
755
+ logger.title("\u{1F680} VexBlocks CMS Setup");
756
+ const manifestPath = path6.join(cwd, MANIFEST_FILE);
757
+ if (await fs6.pathExists(manifestPath)) {
758
+ logger.warn("VexBlocks is already initialized in this directory.");
759
+ const continueAnyway = options.yes || await confirm2({
760
+ message: "Do you want to reinitialize?",
761
+ default: false
762
+ });
763
+ if (!continueAnyway) {
764
+ logger.info("Run `npx @vexblocks/cli add <package>` to add packages.");
765
+ return;
766
+ }
767
+ }
768
+ const isTurbo = await isTurborepoProject(cwd);
769
+ let projectType;
770
+ if (isTurbo) {
771
+ logger.success("Detected existing Turborepo project");
772
+ projectType = "existing";
773
+ } else {
774
+ projectType = await select({
775
+ message: "What would you like to do?",
776
+ choices: [
777
+ {
778
+ name: "Create a new Turborepo project with VexBlocks",
779
+ value: "new"
780
+ },
781
+ {
782
+ name: "Add VexBlocks to this directory (I'll set up Turborepo manually)",
783
+ value: "existing"
784
+ }
785
+ ]
786
+ });
787
+ }
788
+ if (projectType === "new") {
789
+ await createNewProject(cwd, options);
790
+ } else {
791
+ await initializeExisting(cwd, options);
792
+ }
793
+ }
794
+ async function createNewProject(cwd, options) {
795
+ const projectName = await input({
796
+ message: "Project name:",
797
+ default: path6.basename(cwd),
798
+ validate: (value) => {
799
+ if (!value) return "Project name is required";
800
+ if (!/^[a-z0-9-_]+$/.test(value)) {
801
+ return "Project name can only contain lowercase letters, numbers, hyphens, and underscores";
802
+ }
803
+ return true;
804
+ }
805
+ });
806
+ const projectDir = path6.join(cwd, projectName);
807
+ if (await fs6.pathExists(projectDir)) {
808
+ const overwrite = await confirm2({
809
+ message: `Directory ${projectName} already exists. Overwrite?`,
810
+ default: false
811
+ });
812
+ if (!overwrite) {
813
+ logger.info("Aborted.");
814
+ return;
815
+ }
816
+ await fs6.remove(projectDir);
817
+ }
818
+ const spinner = ora3("Creating project structure...").start();
819
+ try {
820
+ await fs6.ensureDir(projectDir);
821
+ await fs6.ensureDir(path6.join(projectDir, "apps"));
822
+ await fs6.ensureDir(path6.join(projectDir, "packages"));
823
+ const packageJson = {
824
+ name: projectName,
825
+ version: "0.1.0",
826
+ private: true,
827
+ packageManager: "pnpm@9.15.0",
828
+ workspaces: ["apps/*", "packages/*"],
829
+ scripts: {
830
+ dev: "turbo run dev",
831
+ build: "turbo run build",
832
+ lint: "turbo run lint",
833
+ "generate-types": "pnpm --filter @repo/type-generator generate"
834
+ },
835
+ devDependencies: {
836
+ turbo: "^2.6.1"
837
+ }
838
+ };
839
+ await fs6.writeJson(path6.join(projectDir, "package.json"), packageJson, {
840
+ spaces: 2
841
+ });
842
+ const turboJson = {
843
+ $schema: "https://turbo.build/schema.json",
844
+ globalDependencies: ["**/.env.*local"],
845
+ globalEnv: [
846
+ "CONVEX_DEPLOYMENT",
847
+ "NEXT_PUBLIC_CONVEX_URL",
848
+ "NEXT_PUBLIC_CONVEX_SITE_URL",
849
+ "SITE_URL",
850
+ "CLOUDFLARE_ACCOUNT_ID",
851
+ "CLOUDFLARE_SECRET_TOKEN"
852
+ ],
853
+ ui: "tui",
854
+ tasks: {
855
+ dev: {
856
+ cache: false,
857
+ persistent: true
858
+ },
859
+ build: {
860
+ dependsOn: ["^build"],
861
+ inputs: ["$TURBO_DEFAULT$", ".env*"],
862
+ outputs: [".next/**", "dist/**"]
863
+ },
864
+ lint: {
865
+ outputs: []
866
+ }
867
+ }
868
+ };
869
+ await fs6.writeJson(path6.join(projectDir, "turbo.json"), turboJson, {
870
+ spaces: 2
871
+ });
872
+ const pnpmWorkspace = `packages:
873
+ - "apps/*"
874
+ - "packages/*"
875
+ `;
876
+ await fs6.writeFile(
877
+ path6.join(projectDir, "pnpm-workspace.yaml"),
878
+ pnpmWorkspace
879
+ );
880
+ const gitignore = `# Dependencies
881
+ node_modules
882
+ .pnpm-store
883
+
884
+ # Build outputs
885
+ dist
886
+ .next
887
+ .turbo
888
+
889
+ # Environment files
890
+ .env
891
+ .env.local
892
+ .env.*.local
893
+
894
+ # IDE
895
+ .idea
896
+ .vscode
897
+ *.swp
898
+ *.swo
899
+
900
+ # OS
901
+ .DS_Store
902
+ Thumbs.db
903
+
904
+ # Logs
905
+ *.log
906
+ npm-debug.log*
907
+ pnpm-debug.log*
908
+
909
+ # Convex
910
+ .convex
911
+ `;
912
+ await fs6.writeFile(path6.join(projectDir, ".gitignore"), gitignore);
913
+ const envExample = `# Convex
914
+ CONVEX_DEPLOYMENT=
915
+ NEXT_PUBLIC_CONVEX_URL=
916
+
917
+ # Better Auth
918
+ SITE_URL=http://localhost:3001
919
+
920
+ # Cloudflare Images (optional, for media library)
921
+ CLOUDFLARE_ACCOUNT_ID=
922
+ CLOUDFLARE_SECRET_TOKEN=
923
+
924
+ # Revalidation (optional, for ISR)
925
+ REVALIDATE_SECRET=
926
+ FRONTEND_URL=http://localhost:3000
927
+ `;
928
+ await fs6.writeFile(path6.join(projectDir, ".env.example"), envExample);
929
+ const version = await getLatestVersion();
930
+ const manifest = createManifest(version);
931
+ await writeManifest(projectDir, manifest);
932
+ spinner.succeed("Project structure created");
933
+ logger.break();
934
+ logger.box("\u2705 Project created successfully!", [
935
+ `Directory: ${pc4.cyan(projectDir)}`,
936
+ "",
937
+ pc4.bold("Next steps:"),
938
+ `1. ${pc4.cyan(`cd ${projectName}`)}`,
939
+ `2. ${pc4.cyan("pnpm install")}`,
940
+ `3. ${pc4.cyan("npx @vexblocks/cli add all")}`,
941
+ "4. Set up your Convex project",
942
+ `5. ${pc4.cyan("pnpm dev")}`
943
+ ]);
944
+ const addPackages = options.yes || await confirm2({
945
+ message: "Would you like to add VexBlocks packages now?",
946
+ default: true
947
+ });
948
+ if (addPackages) {
949
+ logger.break();
950
+ logger.info(`Run: ${pc4.cyan("npx @vexblocks/cli add all")}`);
951
+ logger.info("After navigating to your project directory.");
952
+ }
953
+ } catch (error) {
954
+ spinner.fail("Failed to create project");
955
+ throw error;
956
+ }
957
+ }
958
+ async function initializeExisting(cwd, _options) {
959
+ const spinner = ora3("Initializing VexBlocks...").start();
960
+ try {
961
+ const hasTurbo = await isTurborepoProject(cwd);
962
+ if (!hasTurbo) {
963
+ spinner.warn("No turbo.json found");
964
+ logger.warn(
965
+ "VexBlocks requires Turborepo. Please set up Turborepo first."
966
+ );
967
+ logger.info(`Run: ${pc4.cyan("npx create-turbo@latest")}`);
968
+ return;
969
+ }
970
+ const version = await getLatestVersion();
971
+ const manifest = createManifest(version);
972
+ await writeManifest(cwd, manifest);
973
+ spinner.succeed("VexBlocks initialized");
974
+ const packageManager = await getPackageManager(cwd);
975
+ logger.break();
976
+ logger.box("\u2705 VexBlocks initialized!", [
977
+ `Manifest: ${pc4.cyan(MANIFEST_FILE)}`,
978
+ `Version: ${pc4.cyan(version)}`,
979
+ "",
980
+ pc4.bold("Next steps:"),
981
+ `1. ${pc4.cyan("npx @vexblocks/cli add all")} - Add all CMS packages`,
982
+ "2. Configure your environment variables",
983
+ `3. ${pc4.cyan(`${packageManager} install`)}`,
984
+ `4. ${pc4.cyan(`${packageManager === "npm" ? "npm run" : packageManager} dev`)}`
985
+ ]);
986
+ } catch (error) {
987
+ spinner.fail("Failed to initialize");
988
+ throw error;
989
+ }
990
+ }
991
+
992
+ // src/commands/upgrade.ts
993
+ import path7 from "path";
994
+ import { confirm as confirm3 } from "@inquirer/prompts";
995
+ import fs7 from "fs-extra";
996
+ import ora4 from "ora";
997
+ import pc5 from "picocolors";
998
+ async function upgradeCommand(packages, options) {
999
+ const cwd = options.cwd ? path7.resolve(options.cwd) : process.cwd();
1000
+ logger.title("\u2B06\uFE0F Upgrade VexBlocks Packages");
1001
+ const manifest = await readManifest(cwd);
1002
+ if (!manifest) {
1003
+ logger.error("No vexblocks.json found. Run `npx @vexblocks/cli init` first.");
1004
+ process.exit(1);
1005
+ }
1006
+ const spinner = ora4("Checking for updates...").start();
1007
+ const latestVersion = await getLatestVersion();
1008
+ spinner.stop();
1009
+ const installedPackages = Object.keys(manifest.packages);
1010
+ if (installedPackages.length === 0) {
1011
+ logger.info("No packages installed. Run `npx @vexblocks/cli add <package>` first.");
1012
+ return;
1013
+ }
1014
+ let packagesToUpgrade;
1015
+ if (packages.length === 0) {
1016
+ packagesToUpgrade = installedPackages;
1017
+ } else {
1018
+ packagesToUpgrade = packages.filter(
1019
+ (p) => installedPackages.includes(p)
1020
+ );
1021
+ if (packagesToUpgrade.length === 0) {
1022
+ logger.error("None of the specified packages are installed.");
1023
+ return;
1024
+ }
1025
+ }
1026
+ const updates = [];
1027
+ for (const pkg of packagesToUpgrade) {
1028
+ const config = manifest.packages[pkg];
1029
+ if (!config) continue;
1030
+ const comparison = compareVersions(config.version, latestVersion);
1031
+ if (comparison < 0) {
1032
+ updates.push({
1033
+ pkg,
1034
+ from: config.version,
1035
+ to: latestVersion
1036
+ });
1037
+ }
1038
+ }
1039
+ if (updates.length === 0) {
1040
+ logger.success("All packages are up to date!");
1041
+ logger.info(`Current version: ${pc5.cyan(latestVersion)}`);
1042
+ return;
1043
+ }
1044
+ logger.log(pc5.bold("\nAvailable updates:"));
1045
+ logger.break();
1046
+ for (const update of updates) {
1047
+ const isManaged = MANAGED_PACKAGES.includes(update.pkg);
1048
+ const managedLabel = isManaged ? pc5.yellow(" (managed)") : "";
1049
+ logger.log(
1050
+ ` ${PACKAGE_NAMES[update.pkg]} ${pc5.dim(`${update.from} \u2192 `)}${pc5.green(update.to)}${managedLabel}`
1051
+ );
1052
+ }
1053
+ const changelog = await getChangelog(updates[0].from, latestVersion);
1054
+ if (Object.keys(changelog).length > 0) {
1055
+ logger.break();
1056
+ logger.log(pc5.bold("Changelog:"));
1057
+ for (const [version, changes] of Object.entries(changelog)) {
1058
+ logger.log(`
1059
+ ${pc5.cyan(version)}`);
1060
+ for (const change of changes) {
1061
+ logger.log(` ${pc5.dim("\u2022")} ${change}`);
1062
+ }
1063
+ }
1064
+ }
1065
+ if (options.check) {
1066
+ logger.break();
1067
+ logger.info(
1068
+ `Run ${pc5.cyan("npx @vexblocks/cli upgrade")} to apply these updates.`
1069
+ );
1070
+ return;
1071
+ }
1072
+ logger.break();
1073
+ const proceed = options.yes || await confirm3({
1074
+ message: `Upgrade ${updates.length} package(s)?`,
1075
+ default: true
1076
+ });
1077
+ if (!proceed) {
1078
+ logger.info("Aborted.");
1079
+ return;
1080
+ }
1081
+ for (const update of updates) {
1082
+ await upgradePackage(cwd, update.pkg, update.to, manifest, options);
1083
+ }
1084
+ manifest.version = latestVersion;
1085
+ await writeManifest(cwd, manifest);
1086
+ logger.break();
1087
+ logger.success(`Upgraded to version ${pc5.cyan(latestVersion)}`);
1088
+ logger.info(
1089
+ "Run your package manager's install command to update dependencies."
1090
+ );
1091
+ }
1092
+ async function upgradePackage(cwd, pkg, version, manifest, options) {
1093
+ const spinner = ora4(`Upgrading ${PACKAGE_NAMES[pkg]}...`).start();
1094
+ try {
1095
+ const config = manifest.packages[pkg];
1096
+ if (!config) {
1097
+ spinner.info(`${PACKAGE_NAMES[pkg]} not installed`);
1098
+ return;
1099
+ }
1100
+ const targetPath = path7.join(cwd, config.path);
1101
+ if (!await fs7.pathExists(targetPath)) {
1102
+ spinner.warn(
1103
+ `${PACKAGE_NAMES[pkg]} directory not found at ${config.path}`
1104
+ );
1105
+ return;
1106
+ }
1107
+ const isManaged = MANAGED_PACKAGES.includes(pkg);
1108
+ if (isManaged || options.force) {
1109
+ const backupPath = await createBackup(targetPath);
1110
+ spinner.text = `Created backup at ${path7.basename(backupPath)}`;
1111
+ await fs7.remove(targetPath);
1112
+ const sourcePath = getSourcePath2(pkg);
1113
+ await downloadAndExtractPackage(sourcePath, targetPath, (file) => {
1114
+ spinner.text = `Upgrading ${PACKAGE_NAMES[pkg]}... ${pc5.dim(file)}`;
1115
+ });
1116
+ } else {
1117
+ spinner.text = `Upgrading CMS files in ${PACKAGE_NAMES[pkg]}...`;
1118
+ const cmsFiles = ["convex/cms", "convex/schema.cms.ts"];
1119
+ for (const file of cmsFiles) {
1120
+ const targetFilePath = path7.join(targetPath, file);
1121
+ const sourceFilePath = `packages/backend/${file}`;
1122
+ if (await fs7.pathExists(targetFilePath)) {
1123
+ await createBackup(targetFilePath);
1124
+ }
1125
+ try {
1126
+ await fs7.remove(targetFilePath);
1127
+ await downloadAndExtractPackage(sourceFilePath, targetFilePath);
1128
+ } catch {
1129
+ }
1130
+ }
1131
+ }
1132
+ const newConfig = {
1133
+ ...config,
1134
+ version,
1135
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
1136
+ };
1137
+ manifest.packages[pkg] = newConfig;
1138
+ spinner.succeed(`Upgraded ${PACKAGE_NAMES[pkg]} to ${version}`);
1139
+ } catch (error) {
1140
+ spinner.fail(`Failed to upgrade ${PACKAGE_NAMES[pkg]}`);
1141
+ throw error;
1142
+ }
1143
+ }
1144
+ function getSourcePath2(pkg) {
1145
+ switch (pkg) {
1146
+ case "cms":
1147
+ return "apps/cms";
1148
+ case "backend":
1149
+ return "packages/backend";
1150
+ case "shared":
1151
+ return "packages/cms-shared";
1152
+ case "types":
1153
+ return "packages/type-generator";
1154
+ default:
1155
+ throw new Error(`Unknown package: ${pkg}`);
1156
+ }
1157
+ }
1158
+
1163
1159
  // src/utils/package-info.ts
1164
- import fs8 from "fs-extra";
1165
1160
  import path8 from "path";
1166
1161
  import { fileURLToPath } from "url";
1162
+ import fs8 from "fs-extra";
1167
1163
  var __filename = fileURLToPath(import.meta.url);
1168
1164
  var __dirname = path8.dirname(__filename);
1169
1165
  async function getPackageInfo() {