@mars-stack/cli 7.0.6 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,11 +26,21 @@ Scaffold a new project. Walks through four interactive prompt groups:
26
26
 
27
27
  Outputs a complete Next.js application with auth, design tokens, database schema, agent infrastructure (AGENTS.md, skills, rules, docs), and a working dev environment.
28
28
 
29
+ **Non-interactive shortcuts**
30
+
31
+ - `mars create my-app --defaults` — same choices as the default interactive path (minimal feature set, local/console providers).
32
+ - `mars create my-app --starter saas` — deterministic **SaaS** preset: enables common product flags (billing, notifications, search, analytics, etc.) with local/`console`/`none` stubs so you are not prompted for every toggle. Use `mars create --help` for the current `--starter` list.
33
+
34
+ `--defaults` and `--starter` cannot be combined.
35
+
29
36
  ```bash
30
37
  mars create my-app
31
38
  # → interactive prompts
32
39
  # → scaffolds ~200 files
33
40
  # → prints next steps
41
+
42
+ mars create my-app --starter saas
43
+ # → same scaffold path, preset feature + service choices (no feature/theme prompts)
34
44
  ```
35
45
 
36
46
  ### `mars add feature <name>`
package/dist/index.js CHANGED
@@ -6538,6 +6538,94 @@ async function promptTheme() {
6538
6538
  };
6539
6539
  }
6540
6540
 
6541
+ // src/prompts/starter-presets.ts
6542
+ var BUILTIN_STARTER_NAMES = ["minimal", "saas"];
6543
+ function minimalPreset() {
6544
+ const features = getDefaultFeatures();
6545
+ return {
6546
+ name: "minimal",
6547
+ displayName: "Minimal",
6548
+ description: "Same defaults as interactive blank setup (matches --defaults)",
6549
+ features,
6550
+ services: getDefaultServices(features),
6551
+ theme: getDefaultTheme()
6552
+ };
6553
+ }
6554
+ function saasPreset() {
6555
+ const features = {
6556
+ auth: true,
6557
+ googleOAuth: false,
6558
+ emailVerification: true,
6559
+ magicLinks: false,
6560
+ twoFactor: false,
6561
+ admin: true,
6562
+ darkMode: true,
6563
+ notifications: true,
6564
+ onboarding: true,
6565
+ multiTenancy: false,
6566
+ teams: false,
6567
+ billing: true,
6568
+ blog: false,
6569
+ seo: true,
6570
+ comingSoon: false,
6571
+ cookieConsent: true,
6572
+ analytics: true,
6573
+ sentry: true,
6574
+ ai: false,
6575
+ fileUpload: true,
6576
+ search: true,
6577
+ realtime: true,
6578
+ commandPalette: true,
6579
+ featureFlags: true
6580
+ };
6581
+ const services = {
6582
+ email: { provider: "console" },
6583
+ storage: { provider: "local" },
6584
+ database: { provider: "local" },
6585
+ payments: { provider: "stripe" },
6586
+ analytics: { provider: "vercel" },
6587
+ monitoring: { provider: "sentry" },
6588
+ ai: { provider: "none" },
6589
+ search: { provider: "postgres" },
6590
+ realtime: { provider: "sse" },
6591
+ jobs: { provider: "none" }
6592
+ };
6593
+ return {
6594
+ name: "saas",
6595
+ displayName: "SaaS",
6596
+ description: "Common product features: billing, notifications, search, analytics, and more (local/console stubs)",
6597
+ features,
6598
+ services,
6599
+ theme: getDefaultTheme()
6600
+ };
6601
+ }
6602
+ var BUILTIN = {
6603
+ minimal: minimalPreset(),
6604
+ saas: saasPreset()
6605
+ };
6606
+ function listBuiltinStarterNames() {
6607
+ return BUILTIN_STARTER_NAMES;
6608
+ }
6609
+ function getBuiltinStarterDefinition(name) {
6610
+ if (name === "minimal" || name === "saas") {
6611
+ return BUILTIN[name];
6612
+ }
6613
+ return void 0;
6614
+ }
6615
+ function formatUnknownStarterMessage(name) {
6616
+ const valid = listBuiltinStarterNames().join(", ");
6617
+ return `Unknown starter "${name}". Valid starters: ${valid}`;
6618
+ }
6619
+ function applyBuiltinStarterToConfig(base, starterName) {
6620
+ const def = BUILTIN[starterName];
6621
+ return {
6622
+ ...base,
6623
+ features: { ...def.features },
6624
+ services: structuredClone(def.services),
6625
+ theme: { ...def.theme }
6626
+ };
6627
+ }
6628
+
6541
6629
  // src/generators/scaffold.ts
6542
6630
  init_logger();
6543
6631
  import fs4 from "fs-extra";
@@ -7263,7 +7351,13 @@ var PROJECT_NAME_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
7263
7351
  var MAX_PROJECT_NAME_LENGTH = 214;
7264
7352
  async function createCommand(projectName, options) {
7265
7353
  const useDefaults = options?.defaults === true;
7354
+ const starterArg = options?.starter?.trim();
7355
+ const useStarter = Boolean(starterArg);
7266
7356
  log.banner();
7357
+ if (useDefaults && useStarter) {
7358
+ log.error("Cannot combine --defaults with --starter. Use one preset path.");
7359
+ return;
7360
+ }
7267
7361
  if (projectName) {
7268
7362
  if (!PROJECT_NAME_REGEX.test(projectName) && projectName.length > 1) {
7269
7363
  log.error("Project name must be lowercase letters, numbers, and hyphens only.");
@@ -7282,7 +7376,21 @@ async function createCommand(projectName, options) {
7282
7376
  log.error("Project name is required when using --defaults.");
7283
7377
  return;
7284
7378
  }
7285
- const projectInfo = useDefaults ? getDefaultProjectInfo(projectName) : await promptProjectInfo(projectName);
7379
+ if (useStarter && starterArg) {
7380
+ const preset = getBuiltinStarterDefinition(starterArg);
7381
+ if (!preset) {
7382
+ log.error(formatUnknownStarterMessage(starterArg));
7383
+ log.info(`Valid starters: ${listBuiltinStarterNames().join(", ")}`);
7384
+ return;
7385
+ }
7386
+ }
7387
+ if (useStarter && !projectName) {
7388
+ log.error(
7389
+ "Project name is required when using --starter (non-interactive). Example: mars create my-app --starter saas"
7390
+ );
7391
+ return;
7392
+ }
7393
+ const projectInfo = useDefaults || useStarter ? getDefaultProjectInfo(projectName) : await promptProjectInfo(projectName);
7286
7394
  if (!projectInfo) return;
7287
7395
  const targetDir = resolveProjectPath(projectInfo.name);
7288
7396
  if (await fs23.pathExists(targetDir)) {
@@ -7292,28 +7400,44 @@ async function createCommand(projectName, options) {
7292
7400
  return;
7293
7401
  }
7294
7402
  }
7295
- const features = useDefaults ? getDefaultFeatures() : await promptFeatures();
7296
- if (!features) return;
7297
- const services = useDefaults ? getDefaultServices(features) : await promptServices(features);
7298
- if (!services) return;
7299
- const theme = useDefaults ? getDefaultTheme() : await promptTheme();
7300
- if (!theme) return;
7301
- const config = {
7302
- ...projectInfo,
7303
- features,
7304
- services,
7305
- theme
7306
- };
7403
+ let config;
7404
+ if (useStarter && starterArg) {
7405
+ const preset = getBuiltinStarterDefinition(starterArg);
7406
+ config = applyBuiltinStarterToConfig(projectInfo, preset.name);
7407
+ } else if (useDefaults) {
7408
+ const features = getDefaultFeatures();
7409
+ config = {
7410
+ ...projectInfo,
7411
+ features,
7412
+ services: getDefaultServices(features),
7413
+ theme: getDefaultTheme()
7414
+ };
7415
+ } else {
7416
+ const features = await promptFeatures();
7417
+ if (!features) return;
7418
+ const services = await promptServices(features);
7419
+ if (!services) return;
7420
+ const theme = await promptTheme();
7421
+ if (!theme) return;
7422
+ config = {
7423
+ ...projectInfo,
7424
+ features,
7425
+ services,
7426
+ theme
7427
+ };
7428
+ }
7307
7429
  log.blank();
7308
7430
  log.title("Summary");
7309
7431
  log.info(`Project: ${pc2.bold(config.displayName)} (${config.name})`);
7310
7432
  log.info(`URL: ${config.url}`);
7311
- log.info(`Features: ${countEnabled(features)} enabled`);
7312
- log.info(`Database: ${services.database.provider}`);
7313
- log.info(`Email: ${services.email.provider}`);
7314
- log.info(`Theme: ${theme.primaryColor}, ${theme.font}, ${theme.designDirection}`);
7433
+ log.info(`Features: ${countEnabled(config.features)} enabled`);
7434
+ log.info(`Database: ${config.services.database.provider}`);
7435
+ log.info(`Email: ${config.services.email.provider}`);
7436
+ log.info(
7437
+ `Theme: ${config.theme.primaryColor}, ${config.theme.font}, ${config.theme.designDirection}`
7438
+ );
7315
7439
  log.blank();
7316
- if (!useDefaults) {
7440
+ if (!useDefaults && !useStarter) {
7317
7441
  const { confirmed } = await prompts5(
7318
7442
  {
7319
7443
  type: "confirm",
@@ -7350,10 +7474,12 @@ async function createCommand(projectName, options) {
7350
7474
  spinner.succeed(`Scaffolded ${pc2.bold(String(fileCount))} files`);
7351
7475
  const generatedFeatures = await generateSelectedFeatures(targetDir, config.features);
7352
7476
  if (generatedFeatures.length > 0) {
7353
- log.success(`Generated ${generatedFeatures.length} additional feature(s): ${generatedFeatures.join(", ")}`);
7477
+ log.success(
7478
+ `Generated ${generatedFeatures.length} additional feature(s): ${generatedFeatures.join(", ")}`
7479
+ );
7354
7480
  }
7355
7481
  trackEvent("create", {
7356
- features: Object.keys(features).filter((f) => features[f]).join(",")
7482
+ features: Object.keys(config.features).filter((f) => config.features[f]).join(",")
7357
7483
  });
7358
7484
  log.blank();
7359
7485
  log.title("Next Steps");
@@ -8771,7 +8897,10 @@ init_feature_flags();
8771
8897
  init_logger();
8772
8898
  var program = new Command();
8773
8899
  program.name("mars").description("MARS CLI: scaffold, configure, and maintain SaaS apps").version(getCliVersion(), "-v, --version", "Print the current mars CLI version").option("-u, --upgrade", "Upgrade Mars packages to latest versions");
8774
- program.command("create").description("Create a new MARS project").argument("[name]", "Project name (kebab-case)").option("--defaults", "Skip prompts, use default configuration").action(async (name, options) => {
8900
+ program.command("create").description("Create a new MARS project").argument("[name]", "Project name (kebab-case)").option("--defaults", "Skip prompts, use default configuration").option(
8901
+ "--starter <name>",
8902
+ "Use a named preset (minimal, saas). Skips feature/service/theme prompts; requires project name"
8903
+ ).action(async (name, options) => {
8775
8904
  await createCommand(name, options);
8776
8905
  });
8777
8906
  var add = program.command("add").description("Add a feature, page, model, component, or email template to your project");
@@ -8790,32 +8919,50 @@ add.command("component").description("Create a new UI component").argument("<nam
8790
8919
  add.command("email").description("Create a new email template").argument("<name>", "Template name in kebab-case (e.g., invoice, team-invite)").action(async (name) => {
8791
8920
  await addEmailCommand(name);
8792
8921
  });
8793
- var generate = program.command("generate").description("Generate a feature with full scaffolding (pages, components, server logic, config)");
8922
+ var generate = program.command("generate").description(
8923
+ "Generate a feature with full scaffolding (pages, components, server logic, config)"
8924
+ );
8794
8925
  generate.command("blog").description("Add an MDX-based blog with listing, post pages, RSS feed, and SEO").action(async () => {
8795
8926
  await generateBlog(process.cwd());
8796
8927
  });
8797
- generate.command("dark-mode").description("Add dark mode with ThemeProvider, theme toggle, system preference detection, and FOUC prevention").action(async () => {
8928
+ generate.command("dark-mode").description(
8929
+ "Add dark mode with ThemeProvider, theme toggle, system preference detection, and FOUC prevention"
8930
+ ).action(async () => {
8798
8931
  await generateDarkMode(process.cwd());
8799
8932
  });
8800
- generate.command("notifications").description("Add in-app notifications with Prisma model, API routes, bell component, and polling hook").action(async () => {
8933
+ generate.command("notifications").description(
8934
+ "Add in-app notifications with Prisma model, API routes, bell component, and polling hook"
8935
+ ).action(async () => {
8801
8936
  await generateNotifications(process.cwd());
8802
8937
  });
8803
- generate.command("analytics").description("Add analytics with Vercel/PostHog/Google providers, consent banner, and unified tracking API").action(async () => {
8938
+ generate.command("analytics").description(
8939
+ "Add analytics with Vercel/PostHog/Google providers, consent banner, and unified tracking API"
8940
+ ).action(async () => {
8804
8941
  await generateAnalytics(process.cwd());
8805
8942
  });
8806
- generate.command("command-palette").description("Add a Cmd+K command palette with fuzzy search, action registry, and keyboard navigation").action(async () => {
8943
+ generate.command("command-palette").description(
8944
+ "Add a Cmd+K command palette with fuzzy search, action registry, and keyboard navigation"
8945
+ ).action(async () => {
8807
8946
  await generateCommandPalette(process.cwd());
8808
8947
  });
8809
- generate.command("onboarding").description("Add multi-step user onboarding with progress tracking, skip/complete, and redirect logic").action(async () => {
8948
+ generate.command("onboarding").description(
8949
+ "Add multi-step user onboarding with progress tracking, skip/complete, and redirect logic"
8950
+ ).action(async () => {
8810
8951
  await generateOnboarding(process.cwd());
8811
8952
  });
8812
- generate.command("search").description("Add search with Postgres full-text, Algolia, or Meilisearch providers and debounced hook").action(async () => {
8953
+ generate.command("search").description(
8954
+ "Add search with Postgres full-text, Algolia, or Meilisearch providers and debounced hook"
8955
+ ).action(async () => {
8813
8956
  await generateSearch(process.cwd());
8814
8957
  });
8815
- generate.command("realtime").description("Add realtime updates with SSE (default), Pusher, or Ably providers and client hooks").action(async () => {
8958
+ generate.command("realtime").description(
8959
+ "Add realtime updates with SSE (default), Pusher, or Ably providers and client hooks"
8960
+ ).action(async () => {
8816
8961
  await generateRealtime(process.cwd());
8817
8962
  });
8818
- generate.command("ai").description("Add AI integration with OpenAI/Anthropic providers, streaming chat API, and useChat hook").action(async () => {
8963
+ generate.command("ai").description(
8964
+ "Add AI integration with OpenAI/Anthropic providers, streaming chat API, and useChat hook"
8965
+ ).action(async () => {
8819
8966
  await generateAI(process.cwd());
8820
8967
  });
8821
8968
  generate.command("cookie-consent").description("Add a cookie consent banner with preferences dialog and useConsent hook").action(async () => {
@@ -8827,10 +8974,15 @@ generate.command("coming-soon").description("Add a coming soon landing page with
8827
8974
  generate.command("sentry").description("Add Sentry error tracking with client/server init and ErrorBoundary component").action(async () => {
8828
8975
  await generateSentry(process.cwd());
8829
8976
  });
8830
- generate.command("feature-flags").description("Add feature flags with local JSON provider, server getFlag, client hook, and FeatureGate component").action(async () => {
8977
+ generate.command("feature-flags").description(
8978
+ "Add feature flags with local JSON provider, server getFlag, client hook, and FeatureGate component"
8979
+ ).action(async () => {
8831
8980
  await generateFeatureFlags(process.cwd());
8832
8981
  });
8833
- program.command("configure").description("Configure a service provider (email, payments, storage, etc.)").argument("[service]", "Service to configure (email, payments, storage, analytics, monitoring, auth)").action(async (service) => {
8982
+ program.command("configure").description("Configure a service provider (email, payments, storage, etc.)").argument(
8983
+ "[service]",
8984
+ "Service to configure (email, payments, storage, analytics, monitoring, auth)"
8985
+ ).action(async (service) => {
8834
8986
  await configureCommand(service);
8835
8987
  });
8836
8988
  program.command("deploy").description("Deploy your project to Vercel").action(async () => {
@@ -8855,7 +9007,9 @@ program.command("telemetry").description("Manage anonymous usage telemetry").arg
8855
9007
  program.hook("preAction", (thisCommand) => {
8856
9008
  const globalOpts = program.opts();
8857
9009
  if (globalOpts.upgrade && thisCommand !== program) {
8858
- log.error('Cannot combine --upgrade with other commands. Use "mars -u" or "mars upgrade" alone.');
9010
+ log.error(
9011
+ 'Cannot combine --upgrade with other commands. Use "mars -u" or "mars upgrade" alone.'
9012
+ );
8859
9013
  process.exit(1);
8860
9014
  }
8861
9015
  });