@rebasepro/cli 0.0.1-canary.4d4fb3e → 0.0.1-canary.ca2cb6e

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 (35) hide show
  1. package/dist/commands/cli.test.d.ts +1 -0
  2. package/dist/commands/dev.test.d.ts +1 -0
  3. package/dist/commands/doctor.d.ts +1 -0
  4. package/dist/commands/generate_sdk.d.ts +1 -1
  5. package/dist/commands/init.test.d.ts +1 -0
  6. package/dist/index.cjs +188 -62
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.es.js +188 -62
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/utils/project.test.d.ts +1 -0
  11. package/package.json +67 -66
  12. package/templates/template/.env.template +22 -1
  13. package/templates/template/README.md +2 -2
  14. package/templates/template/backend/Dockerfile +6 -6
  15. package/templates/template/backend/functions/hello.ts +24 -6
  16. package/templates/template/backend/package.json +2 -2
  17. package/templates/template/backend/src/env.ts +52 -0
  18. package/templates/template/backend/src/index.ts +86 -34
  19. package/templates/template/backend/tsconfig.json +1 -1
  20. package/templates/template/config/collections/authors.ts +45 -0
  21. package/templates/template/config/collections/index.ts +5 -0
  22. package/templates/template/{shared → config}/collections/posts.ts +39 -1
  23. package/templates/template/config/collections/tags.ts +27 -0
  24. package/templates/template/{shared → config}/package.json +1 -1
  25. package/templates/template/docker-compose.yml +12 -0
  26. package/templates/template/frontend/Dockerfile +3 -3
  27. package/templates/template/frontend/package.json +2 -2
  28. package/templates/template/frontend/src/App.tsx +11 -12
  29. package/templates/template/frontend/src/main.tsx +2 -2
  30. package/templates/template/frontend/vite.config.ts +1 -1
  31. package/templates/template/pnpm-workspace.yaml +1 -1
  32. package/templates/template/scripts/example.ts +91 -0
  33. package/templates/template/shared/collections/index.ts +0 -3
  34. /package/templates/template/{shared → config}/index.ts +0 -0
  35. /package/templates/template/{shared → config}/tsconfig.json +0 -0
package/dist/index.es.js CHANGED
@@ -81,8 +81,8 @@ async function promptForOptions(rawArgs) {
81
81
  });
82
82
  }
83
83
  const answers = await inquirer.prompt(questions);
84
- const projectName = nameArg || answers.projectName;
85
- const targetDirectory = path.resolve(process.cwd(), projectName);
84
+ const targetDirectory = path.resolve(process.cwd(), nameArg || answers.projectName);
85
+ const projectName = path.basename(targetDirectory);
86
86
  const templateDirectory = path.resolve(cliRoot, "templates", "template");
87
87
  return {
88
88
  projectName,
@@ -179,7 +179,7 @@ POSTGRES_PASSWORD=${dbPassword}
179
179
  console.log("");
180
180
  console.log(` ${chalk.cyan("pnpm dev")}`);
181
181
  console.log("");
182
- console.log(chalk.gray("This starts both the backend (Express + PostgreSQL)") + chalk.gray(" and the frontend (Vite + React) concurrently."));
182
+ console.log(chalk.gray("This starts both the backend (Hono + PostgreSQL)") + chalk.gray(" and the frontend (Vite + React) concurrently."));
183
183
  console.log("");
184
184
  console.log(chalk.gray("Docs: https://rebase.pro/docs"));
185
185
  console.log(chalk.gray("GitHub: https://github.com/rebasepro/rebase"));
@@ -190,14 +190,21 @@ async function replacePlaceholders(options) {
190
190
  "package.json",
191
191
  "frontend/package.json",
192
192
  "backend/package.json",
193
- "shared/package.json",
193
+ "config/package.json",
194
194
  "frontend/index.html"
195
195
  ];
196
+ const packageJsonPath = path.resolve(cliRoot, "package.json");
197
+ let cliVersion = "latest";
198
+ if (fs.existsSync(packageJsonPath)) {
199
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
200
+ cliVersion = pkg.version || "latest";
201
+ }
196
202
  for (const file of filesToProcess) {
197
203
  const fullPath = path.resolve(options.targetDirectory, file);
198
204
  if (!fs.existsSync(fullPath)) continue;
199
205
  let content = fs.readFileSync(fullPath, "utf-8");
200
206
  content = content.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName);
207
+ content = content.replace(/("@rebasepro\/[^"]+":\s*)"workspace:\*"/g, `$1"${cliVersion}"`);
201
208
  fs.writeFileSync(fullPath, content, "utf-8");
202
209
  }
203
210
  }
@@ -309,7 +316,7 @@ async function generateSdkCommand(args) {
309
316
  console.log(chalk.green.bold(" ✓ SDK generated successfully!"));
310
317
  console.log("");
311
318
  console.log(chalk.gray(" Usage:"));
312
- console.log(chalk.gray(` import { createRebaseClient } from '@rebasepro/client';`));
319
+ console.log(chalk.gray(" import { createRebaseClient } from '@rebasepro/client';"));
313
320
  console.log(chalk.gray(` import type { Database } from './${path.relative(cwd, path.join(resolvedOutput, "database.types"))}';`));
314
321
  console.log("");
315
322
  console.log(chalk.gray(" const rebase = createRebaseClient<Database>({"));
@@ -336,7 +343,7 @@ function findProjectRoot(startDir = process.cwd()) {
336
343
  }
337
344
  } catch {
338
345
  }
339
- if (fs.existsSync(path.join(dir, "backend")) && fs.existsSync(path.join(dir, "shared"))) {
346
+ if (fs.existsSync(path.join(dir, "backend")) && fs.existsSync(path.join(dir, "config"))) {
340
347
  return dir;
341
348
  }
342
349
  }
@@ -353,7 +360,10 @@ function getActiveBackendPlugin(backendDir) {
353
360
  if (!fs.existsSync(pkgPath)) return null;
354
361
  try {
355
362
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
356
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
363
+ const deps = {
364
+ ...pkg.dependencies,
365
+ ...pkg.devDependencies
366
+ };
357
367
  for (const dep of Object.keys(deps)) {
358
368
  if (dep.startsWith("@rebasepro/server-") && dep !== "@rebasepro/server-core") {
359
369
  return dep;
@@ -464,13 +474,13 @@ async function schemaCommand(subcommand, rawArgs) {
464
474
  console.error(chalk.red("✗ Could not find tsx binary."));
465
475
  process.exit(1);
466
476
  }
467
- await execa(tsxBin, [pluginCli, "schema", subcommand, ...rawArgs.slice(2)], {
477
+ await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
468
478
  cwd: backendDir,
469
479
  stdio: "inherit",
470
480
  env
471
481
  });
472
482
  } else {
473
- await execa("node", [pluginCli, "schema", subcommand, ...rawArgs.slice(2)], {
483
+ await execa("node", [pluginCli, ...rawArgs.slice(2)], {
474
484
  cwd: backendDir,
475
485
  stdio: "inherit",
476
486
  env
@@ -528,13 +538,13 @@ async function dbCommand(subcommand, rawArgs) {
528
538
  console.error(chalk.red("✗ Could not find tsx binary."));
529
539
  process.exit(1);
530
540
  }
531
- await execa(tsxBin, [pluginCli, "db", subcommand, ...rawArgs.slice(2)], {
541
+ await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
532
542
  cwd: backendDir,
533
543
  stdio: "inherit",
534
544
  env
535
545
  });
536
546
  } else {
537
- await execa("node", [pluginCli, "db", subcommand, ...rawArgs.slice(2)], {
547
+ await execa("node", [pluginCli, ...rawArgs.slice(2)], {
538
548
  cwd: backendDir,
539
549
  stdio: "inherit",
540
550
  env
@@ -572,6 +582,27 @@ ${chalk.green.bold("Examples")}
572
582
  rebase db branch create feature_auth
573
583
  `);
574
584
  }
585
+ const DEV_PORT_FILENAME = ".rebase-dev-port";
586
+ function getProjectPort(projectRoot) {
587
+ let hash = 0;
588
+ for (let i = 0; i < projectRoot.length; i++) {
589
+ hash = (hash << 5) - hash + projectRoot.charCodeAt(i) | 0;
590
+ }
591
+ return 3001 + Math.abs(hash) % 999;
592
+ }
593
+ function resolveStartPort(projectRoot, explicitPort) {
594
+ if (explicitPort) return explicitPort;
595
+ if (process.env.PORT) return parseInt(process.env.PORT, 10);
596
+ try {
597
+ const portFile = path.join(projectRoot, DEV_PORT_FILENAME);
598
+ if (fs.existsSync(portFile)) {
599
+ const saved = parseInt(fs.readFileSync(portFile, "utf-8").trim(), 10);
600
+ if (saved > 0 && saved < 65536) return saved;
601
+ }
602
+ } catch {
603
+ }
604
+ return getProjectPort(projectRoot);
605
+ }
575
606
  async function devCommand(rawArgs) {
576
607
  const args = arg(
577
608
  {
@@ -599,6 +630,7 @@ async function devCommand(rawArgs) {
599
630
  const frontendDir = findFrontendDir(projectRoot);
600
631
  const backendOnly = args["--backend-only"] || false;
601
632
  const frontendOnly = args["--frontend-only"] || false;
633
+ const startPort = resolveStartPort(projectRoot, args["--port"]);
602
634
  console.log("");
603
635
  console.log(chalk.bold(" 🚀 Rebase Dev Server"));
604
636
  console.log("");
@@ -607,6 +639,7 @@ async function devCommand(rawArgs) {
607
639
  let backendUrl = "";
608
640
  let debounceSummary = null;
609
641
  let bannerPrinted = false;
642
+ let resolvedBackendPort = null;
610
643
  const stripAnsi = (str) => str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "");
611
644
  function printSummary() {
612
645
  if (!frontendUrl || !backendUrl) return;
@@ -619,7 +652,7 @@ async function devCommand(rawArgs) {
619
652
  console.log(chalk.cyan("│ ✨ Rebase Admin App is ready! │"));
620
653
  const cleanUrl = stripAnsi(frontendUrl);
621
654
  const paddedUrl = cleanUrl.padEnd(40);
622
- console.log(chalk.cyan(`│ 👉 Frontend URL: `) + chalk.white(paddedUrl) + chalk.cyan(`│`));
655
+ console.log(chalk.cyan("│ 👉 Frontend URL: ") + chalk.white(paddedUrl) + chalk.cyan("│"));
623
656
  console.log(chalk.cyan("│ │"));
624
657
  console.log(chalk.cyan("└────────────────────────────────────────────────────────────┘"));
625
658
  console.log("");
@@ -627,15 +660,74 @@ async function devCommand(rawArgs) {
627
660
  }, 500);
628
661
  }
629
662
  const cleanup = () => {
663
+ try {
664
+ const portFile = path.join(projectRoot, DEV_PORT_FILENAME);
665
+ if (fs.existsSync(portFile)) fs.unlinkSync(portFile);
666
+ const urlFile = path.join(projectRoot, ".rebase-dev-url");
667
+ if (fs.existsSync(urlFile)) fs.unlinkSync(urlFile);
668
+ } catch {
669
+ }
630
670
  children.forEach((child) => {
631
- if (!child.killed) {
632
- child.kill("SIGTERM");
671
+ if (child.pid && !child.killed) {
672
+ try {
673
+ if (process.platform === "win32") {
674
+ execa.commandSync(`taskkill /pid ${child.pid} /T /F`);
675
+ } else {
676
+ process.kill(-child.pid, "SIGKILL");
677
+ }
678
+ } catch (e) {
679
+ try {
680
+ child.kill("SIGKILL");
681
+ } catch (err) {
682
+ }
683
+ }
633
684
  }
634
685
  });
635
686
  process.exit(0);
636
687
  };
637
688
  process.on("SIGINT", cleanup);
638
689
  process.on("SIGTERM", cleanup);
690
+ function startFrontend(backendPort) {
691
+ if (!frontendDir) return;
692
+ console.log(` ${chalk.magenta("▶")} Frontend: ${chalk.gray(frontendDir)}`);
693
+ const frontendEnv = { ...process.env };
694
+ if (backendPort) {
695
+ frontendEnv.VITE_API_URL = `http://localhost:${backendPort}`;
696
+ console.log(` ${chalk.gray("↳ VITE_API_URL")} = ${chalk.white(`http://localhost:${backendPort}`)}`);
697
+ }
698
+ const frontendChild = execa(
699
+ "pnpm",
700
+ ["run", "dev"],
701
+ {
702
+ cwd: frontendDir,
703
+ stdio: ["inherit", "pipe", "pipe"],
704
+ env: frontendEnv,
705
+ shell: true,
706
+ detached: process.platform !== "win32"
707
+ }
708
+ );
709
+ frontendChild.catch(() => {
710
+ });
711
+ frontendChild.stdout?.on("data", (data) => {
712
+ const lines = data.toString().split("\n").filter(Boolean);
713
+ lines.forEach((line) => {
714
+ console.log(`${chalk.magenta.bold("[admin]")} ${line}`);
715
+ const cleanLine = stripAnsi(line);
716
+ const urlMatch = cleanLine.match(/(http:\/\/(?:localhost|127\.0\.0\.1):\d+)/);
717
+ if (cleanLine.includes("Local:") && urlMatch) {
718
+ frontendUrl = urlMatch[1];
719
+ printSummary();
720
+ }
721
+ });
722
+ });
723
+ frontendChild.stderr?.on("data", (data) => {
724
+ const lines = data.toString().split("\n").filter(Boolean);
725
+ lines.forEach((line) => {
726
+ console.log(`${chalk.magenta.bold("[admin]")} ${line}`);
727
+ });
728
+ });
729
+ children.push(frontendChild);
730
+ }
639
731
  if (!frontendOnly && backendDir) {
640
732
  const tsxBin = resolveTsx(projectRoot);
641
733
  if (!tsxBin) {
@@ -648,22 +740,19 @@ async function devCommand(rawArgs) {
648
740
  if (envFile) {
649
741
  env.DOTENV_CONFIG_PATH = envFile;
650
742
  }
651
- if (args["--port"]) {
652
- env.PORT = String(args["--port"]);
653
- }
654
- path.join(backendDir, "src", "index.ts");
655
- const watchDirs = [
656
- `--watch="${path.join("..", "shared", "**", "*")}"`
657
- ];
743
+ env.PORT = String(startPort);
658
744
  console.log(` ${chalk.cyan("▶")} Backend: ${chalk.gray(backendDir)}`);
745
+ console.log(` ${chalk.gray("↳ PORT")} = ${chalk.white(String(startPort))}`);
746
+ let frontendLaunched = false;
659
747
  const backendChild = execa(
660
748
  tsxBin,
661
- ["watch", ...watchDirs, "--conditions", "development", "src/index.ts"],
749
+ ["watch", `--watch="${path.join("..", "config", "**", "*")}"`, "--conditions", "development", "src/index.ts"],
662
750
  {
663
751
  cwd: backendDir,
664
752
  stdio: ["inherit", "pipe", "pipe"],
665
753
  env,
666
- shell: true
754
+ shell: true,
755
+ detached: process.platform !== "win32"
667
756
  }
668
757
  );
669
758
  backendChild.catch(() => {
@@ -673,9 +762,19 @@ async function devCommand(rawArgs) {
673
762
  lines.forEach((line) => {
674
763
  console.log(`${chalk.cyan.bold("[backend]")} ${line}`);
675
764
  const cleanLine = stripAnsi(line);
676
- if (cleanLine.includes("Server running at http://")) {
765
+ const serverMatch = cleanLine.match(/Server running at http:\/\/(?:localhost|127\.0\.0\.1):(\d+)/);
766
+ if (serverMatch) {
767
+ resolvedBackendPort = parseInt(serverMatch[1], 10);
677
768
  backendUrl = "started";
678
769
  printSummary();
770
+ const urlFile = path.join(projectRoot, ".rebase-dev-url");
771
+ fs.writeFileSync(urlFile, `http://localhost:${resolvedBackendPort}`, "utf-8");
772
+ const portFile = path.join(projectRoot, DEV_PORT_FILENAME);
773
+ fs.writeFileSync(portFile, String(resolvedBackendPort), "utf-8");
774
+ if (!backendOnly && frontendDir && !frontendLaunched) {
775
+ frontendLaunched = true;
776
+ startFrontend(resolvedBackendPort);
777
+ }
679
778
  }
680
779
  });
681
780
  });
@@ -689,39 +788,8 @@ async function devCommand(rawArgs) {
689
788
  } else if (!frontendOnly && !backendDir) {
690
789
  console.warn(chalk.yellow(" ⚠ No backend/ directory found, skipping backend."));
691
790
  }
692
- if (!backendOnly && frontendDir) {
693
- console.log(` ${chalk.magenta("▶")} Frontend: ${chalk.gray(frontendDir)}`);
694
- const frontendChild = execa(
695
- "pnpm",
696
- ["run", "dev"],
697
- {
698
- cwd: frontendDir,
699
- stdio: ["inherit", "pipe", "pipe"],
700
- env: process.env,
701
- shell: true
702
- }
703
- );
704
- frontendChild.catch(() => {
705
- });
706
- frontendChild.stdout?.on("data", (data) => {
707
- const lines = data.toString().split("\n").filter(Boolean);
708
- lines.forEach((line) => {
709
- console.log(`${chalk.magenta.bold("[frontend]")} ${line}`);
710
- const cleanLine = stripAnsi(line);
711
- const urlMatch = cleanLine.match(/(http:\/\/(?:localhost|127\.0\.0\.1):\d+)/);
712
- if (cleanLine.includes("Local:") && urlMatch) {
713
- frontendUrl = urlMatch[1];
714
- printSummary();
715
- }
716
- });
717
- });
718
- frontendChild.stderr?.on("data", (data) => {
719
- const lines = data.toString().split("\n").filter(Boolean);
720
- lines.forEach((line) => {
721
- console.log(`${chalk.magenta.bold("[frontend]")} ${line}`);
722
- });
723
- });
724
- children.push(frontendChild);
791
+ if (!backendOnly && frontendDir && (frontendOnly || !backendDir)) {
792
+ startFrontend(null);
725
793
  } else if (!backendOnly && !frontendDir) {
726
794
  console.warn(chalk.yellow(" ⚠ No frontend/ directory found, skipping frontend."));
727
795
  }
@@ -750,11 +818,19 @@ ${chalk.green.bold("Usage")}
750
818
  ${chalk.green.bold("Options")}
751
819
  ${chalk.blue("--backend-only, -b")} Only start the backend server
752
820
  ${chalk.blue("--frontend-only, -f")} Only start the frontend server
753
- ${chalk.blue("--port, -p")} Backend port (default: 3001)
821
+ ${chalk.blue("--port, -p")} Backend port (default: auto-detected per project)
754
822
 
755
823
  ${chalk.green.bold("Description")}
756
- Starts both the backend (tsx watch + Express) and frontend (Vite)
824
+ Starts both the backend (tsx watch + Hono) and frontend (Vite)
757
825
  dev servers concurrently with color-coded output prefixes.
826
+
827
+ Each project automatically receives a unique default port derived
828
+ from its directory path, preventing collisions when running multiple
829
+ Rebase instances simultaneously.
830
+
831
+ If the assigned port is already in use, the server will automatically
832
+ try the next available port. The frontend is started only after the
833
+ backend is ready, and VITE_API_URL is injected automatically.
758
834
  `);
759
835
  }
760
836
  async function authCommand(subcommand, rawArgs) {
@@ -891,6 +967,49 @@ ${chalk.green.bold("Examples")}
891
967
  rebase auth reset-password --email user@example.com --password MyNewPass!
892
968
  `);
893
969
  }
970
+ async function doctorCommand(rawArgs) {
971
+ const projectRoot = requireProjectRoot();
972
+ const backendDir = requireBackendDir(projectRoot);
973
+ const activePlugin = getActiveBackendPlugin(backendDir);
974
+ if (!activePlugin) {
975
+ console.error(chalk.red("✗ Could not detect an active database plugin."));
976
+ console.error(chalk.gray(" Make sure a package like @rebasepro/server-postgresql is installed in backend/package.json."));
977
+ process.exit(1);
978
+ }
979
+ const pluginCli = resolvePluginCliScript(backendDir, activePlugin);
980
+ if (!pluginCli) {
981
+ console.error(chalk.red(`✗ Could not find CLI entry point for ${activePlugin}.`));
982
+ process.exit(1);
983
+ }
984
+ const envFile = findEnvFile(projectRoot);
985
+ const env = { ...process.env };
986
+ if (envFile) {
987
+ env.DOTENV_CONFIG_PATH = envFile;
988
+ }
989
+ try {
990
+ const isTs = pluginCli.endsWith(".ts");
991
+ if (isTs) {
992
+ const tsxBin = resolveTsx(projectRoot);
993
+ if (!tsxBin) {
994
+ console.error(chalk.red("✗ Could not find tsx binary."));
995
+ process.exit(1);
996
+ }
997
+ await execa(tsxBin, [pluginCli, ...rawArgs.slice(2)], {
998
+ cwd: backendDir,
999
+ stdio: "inherit",
1000
+ env
1001
+ });
1002
+ } else {
1003
+ await execa("node", [pluginCli, ...rawArgs.slice(2)], {
1004
+ cwd: backendDir,
1005
+ stdio: "inherit",
1006
+ env
1007
+ });
1008
+ }
1009
+ } catch {
1010
+ process.exit(1);
1011
+ }
1012
+ }
894
1013
  const __filename$1 = fileURLToPath(import.meta.url);
895
1014
  const __dirname$1 = path.dirname(__filename$1);
896
1015
  function getVersion() {
@@ -922,7 +1041,7 @@ async function entry(args) {
922
1041
  }
923
1042
  const command = parsedArgs._[0];
924
1043
  const subcommand = parsedArgs._[1];
925
- const namespacedCommands = ["schema", "db", "dev", "auth"];
1044
+ const namespacedCommands = ["schema", "db", "dev", "auth", "doctor"];
926
1045
  if (!command || parsedArgs["--help"] && !namespacedCommands.includes(command)) {
927
1046
  printHelp();
928
1047
  return;
@@ -932,7 +1051,7 @@ async function entry(args) {
932
1051
  case "init":
933
1052
  await createRebaseApp(args);
934
1053
  break;
935
- case "generate-sdk":
1054
+ case "generate-sdk": {
936
1055
  const sdkArgs = arg(
937
1056
  {
938
1057
  "--collections-dir": String,
@@ -946,11 +1065,12 @@ async function entry(args) {
946
1065
  }
947
1066
  );
948
1067
  await generateSdkCommand({
949
- collectionsDir: sdkArgs["--collections-dir"] || "./shared/collections",
1068
+ collectionsDir: sdkArgs["--collections-dir"] || "./config/collections",
950
1069
  output: sdkArgs["--output"] || "./generated/sdk",
951
1070
  cwd: process.cwd()
952
1071
  });
953
1072
  break;
1073
+ }
954
1074
  case "schema":
955
1075
  await schemaCommand(effectiveSubcommand, args);
956
1076
  break;
@@ -963,6 +1083,9 @@ async function entry(args) {
963
1083
  case "auth":
964
1084
  await authCommand(effectiveSubcommand, args);
965
1085
  break;
1086
+ case "doctor":
1087
+ await doctorCommand(args);
1088
+ break;
966
1089
  default:
967
1090
  console.log(chalk.red(`Unknown command: ${command}`));
968
1091
  console.log("");
@@ -999,6 +1122,9 @@ ${chalk.green.bold("Auth")}
999
1122
  ${chalk.blue.bold("auth reset-password")} Reset a user's password
1000
1123
  ${chalk.blue.bold("auth")} ${chalk.gray("--help")} Show auth command help
1001
1124
 
1125
+ ${chalk.green.bold("Diagnostics")}
1126
+ ${chalk.blue.bold("doctor")} Detect schema drift between collections, schema, and DB
1127
+
1002
1128
  ${chalk.green.bold("Options")}
1003
1129
  ${chalk.blue("--version, -v")} Show version number
1004
1130
  ${chalk.blue("--help, -h")} Show this help message