@mars-stack/cli 4.0.3 → 5.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/dist/index.js CHANGED
@@ -44,8 +44,8 @@ var init_logger = __esm({
44
44
  });
45
45
 
46
46
  // src/utils/rollback.ts
47
- import fs4 from "fs-extra";
48
- import path4 from "path";
47
+ import fs5 from "fs-extra";
48
+ import path5 from "path";
49
49
  import os from "os";
50
50
  function createRollbackContext() {
51
51
  const manifest = {
@@ -53,15 +53,15 @@ function createRollbackContext() {
53
53
  filesModified: [],
54
54
  depsInstalled: []
55
55
  };
56
- const backupDir = path4.join(os.tmpdir(), `mars-rollback-${Date.now()}`);
56
+ const backupDir = path5.join(os.tmpdir(), `mars-rollback-${Date.now()}`);
57
57
  function trackCreatedFile(filePath) {
58
58
  manifest.filesCreated.push(filePath);
59
59
  }
60
60
  async function trackModifiedFile(filePath) {
61
- if (!await fs4.pathExists(filePath)) return;
62
- await fs4.ensureDir(backupDir);
63
- const backupPath = path4.join(backupDir, `${manifest.filesModified.length}-${path4.basename(filePath)}`);
64
- await fs4.copy(filePath, backupPath);
61
+ if (!await fs5.pathExists(filePath)) return;
62
+ await fs5.ensureDir(backupDir);
63
+ const backupPath = path5.join(backupDir, `${manifest.filesModified.length}-${path5.basename(filePath)}`);
64
+ await fs5.copy(filePath, backupPath);
65
65
  manifest.filesModified.push({ path: filePath, backup: backupPath });
66
66
  }
67
67
  function trackInstalledDep(depName) {
@@ -70,13 +70,13 @@ function createRollbackContext() {
70
70
  async function rollback() {
71
71
  for (const filePath of manifest.filesCreated.reverse()) {
72
72
  try {
73
- await fs4.remove(filePath);
73
+ await fs5.remove(filePath);
74
74
  } catch {
75
75
  }
76
76
  }
77
77
  for (const { path: originalPath, backup } of manifest.filesModified) {
78
78
  try {
79
- await fs4.copy(backup, originalPath, { overwrite: true });
79
+ await fs5.copy(backup, originalPath, { overwrite: true });
80
80
  } catch {
81
81
  }
82
82
  }
@@ -86,9 +86,9 @@ function createRollbackContext() {
86
86
  await cleanupBackups();
87
87
  }
88
88
  async function cleanupBackups() {
89
- if (await fs4.pathExists(backupDir)) {
89
+ if (await fs5.pathExists(backupDir)) {
90
90
  try {
91
- await fs4.remove(backupDir);
91
+ await fs5.remove(backupDir);
92
92
  } catch {
93
93
  }
94
94
  }
@@ -108,15 +108,15 @@ var init_rollback = __esm({
108
108
  });
109
109
 
110
110
  // src/utils/dependencies.ts
111
- import fs5 from "fs-extra";
112
- import path5 from "path";
111
+ import fs6 from "fs-extra";
112
+ import path6 from "path";
113
113
  async function addDependencies(projectRoot, deps, dev = false) {
114
- const pkgPath = path5.join(projectRoot, "package.json");
115
- if (!await fs5.pathExists(pkgPath)) return;
116
- const pkg = await fs5.readJson(pkgPath);
114
+ const pkgPath = path6.join(projectRoot, "package.json");
115
+ if (!await fs6.pathExists(pkgPath)) return;
116
+ const pkg = await fs6.readJson(pkgPath);
117
117
  const key = dev ? "devDependencies" : "dependencies";
118
118
  pkg[key] = { ...pkg[key], ...deps };
119
- await fs5.writeJson(pkgPath, pkg, { spaces: 2 });
119
+ await fs6.writeJson(pkgPath, pkg, { spaces: 2 });
120
120
  }
121
121
  var init_dependencies = __esm({
122
122
  "src/utils/dependencies.ts"() {
@@ -125,12 +125,12 @@ var init_dependencies = __esm({
125
125
  });
126
126
 
127
127
  // src/utils/routes.ts
128
- import fs6 from "fs-extra";
129
- import path6 from "path";
128
+ import fs7 from "fs-extra";
129
+ import path7 from "path";
130
130
  async function registerRoute(projectRoot, key, routePath, ctx) {
131
- const routesFile = path6.join(projectRoot, "src", "config", "routes.ts");
132
- if (!await fs6.pathExists(routesFile)) return;
133
- let content = await fs6.readFile(routesFile, "utf-8");
131
+ const routesFile = path7.join(projectRoot, "src", "config", "routes.ts");
132
+ if (!await fs7.pathExists(routesFile)) return;
133
+ let content = await fs7.readFile(routesFile, "utf-8");
134
134
  if (content.includes(`${key}:`)) return;
135
135
  if (ctx) {
136
136
  await ctx.trackModifiedFile(routesFile);
@@ -140,7 +140,7 @@ async function registerRoute(projectRoot, key, routePath, ctx) {
140
140
  ` ${key}: '${routePath}',
141
141
  } as const;`
142
142
  );
143
- await fs6.writeFile(routesFile, content);
143
+ await fs7.writeFile(routesFile, content);
144
144
  }
145
145
  var init_routes = __esm({
146
146
  "src/utils/routes.ts"() {
@@ -153,11 +153,11 @@ var blog_exports = {};
153
153
  __export(blog_exports, {
154
154
  generateBlog: () => generateBlog
155
155
  });
156
- import fs7 from "fs-extra";
157
- import path7 from "path";
156
+ import fs8 from "fs-extra";
157
+ import path8 from "path";
158
158
  async function generateBlog(projectRoot) {
159
- const featureDir = path7.join(projectRoot, "src", "features", "blog");
160
- if (await fs7.pathExists(featureDir)) {
159
+ const featureDir = path8.join(projectRoot, "src", "features", "blog");
160
+ if (await fs8.pathExists(featureDir)) {
161
161
  log.error("Blog feature already exists at src/features/blog/");
162
162
  return;
163
163
  }
@@ -178,10 +178,10 @@ async function generateBlog(projectRoot) {
178
178
  };
179
179
  let count = 0;
180
180
  for (const [filePath, content] of Object.entries(files)) {
181
- const fullPath = path7.join(projectRoot, filePath);
181
+ const fullPath = path8.join(projectRoot, filePath);
182
182
  ctx.trackCreatedFile(fullPath);
183
- await fs7.ensureDir(path7.dirname(fullPath));
184
- await fs7.writeFile(fullPath, content);
183
+ await fs8.ensureDir(path8.dirname(fullPath));
184
+ await fs8.writeFile(fullPath, content);
185
185
  count++;
186
186
  }
187
187
  await addDependencies(projectRoot, {
@@ -208,12 +208,12 @@ async function generateBlog(projectRoot) {
208
208
  }
209
209
  }
210
210
  async function setConfigFlag(projectRoot, ctx) {
211
- const configPath = path7.join(projectRoot, "src", "config", "app.config.ts");
212
- if (!await fs7.pathExists(configPath)) return;
211
+ const configPath = path8.join(projectRoot, "src", "config", "app.config.ts");
212
+ if (!await fs8.pathExists(configPath)) return;
213
213
  await ctx.trackModifiedFile(configPath);
214
- const content = await fs7.readFile(configPath, "utf-8");
214
+ const content = await fs8.readFile(configPath, "utf-8");
215
215
  const updated = content.replace(/blog:\s*false/, "blog: true");
216
- await fs7.writeFile(configPath, updated);
216
+ await fs8.writeFile(configPath, updated);
217
217
  }
218
218
  function schemas() {
219
219
  return `${STAMP}
@@ -732,11 +732,11 @@ var dark_mode_exports = {};
732
732
  __export(dark_mode_exports, {
733
733
  generateDarkMode: () => generateDarkMode
734
734
  });
735
- import fs8 from "fs-extra";
736
- import path8 from "path";
735
+ import fs9 from "fs-extra";
736
+ import path9 from "path";
737
737
  async function generateDarkMode(projectRoot) {
738
- const featureDir = path8.join(projectRoot, "src", "features", "dark-mode");
739
- if (await fs8.pathExists(featureDir)) {
738
+ const featureDir = path9.join(projectRoot, "src", "features", "dark-mode");
739
+ if (await fs9.pathExists(featureDir)) {
740
740
  log.error("Dark mode feature already exists at src/features/dark-mode/");
741
741
  return;
742
742
  }
@@ -751,10 +751,10 @@ async function generateDarkMode(projectRoot) {
751
751
  };
752
752
  let count = 0;
753
753
  for (const [filePath, content] of Object.entries(files)) {
754
- const fullPath = path8.join(projectRoot, filePath);
754
+ const fullPath = path9.join(projectRoot, filePath);
755
755
  ctx.trackCreatedFile(fullPath);
756
- await fs8.ensureDir(path8.dirname(fullPath));
757
- await fs8.writeFile(fullPath, content);
756
+ await fs9.ensureDir(path9.dirname(fullPath));
757
+ await fs9.writeFile(fullPath, content);
758
758
  count++;
759
759
  }
760
760
  await wireLayout(projectRoot, ctx);
@@ -784,18 +784,18 @@ async function generateDarkMode(projectRoot) {
784
784
  }
785
785
  }
786
786
  async function setConfigFlag2(projectRoot, ctx) {
787
- const configPath = path8.join(projectRoot, "src", "config", "app.config.ts");
788
- if (!await fs8.pathExists(configPath)) return;
787
+ const configPath = path9.join(projectRoot, "src", "config", "app.config.ts");
788
+ if (!await fs9.pathExists(configPath)) return;
789
789
  await ctx.trackModifiedFile(configPath);
790
- const content = await fs8.readFile(configPath, "utf-8");
790
+ const content = await fs9.readFile(configPath, "utf-8");
791
791
  const updated = content.replace(/darkMode:\s*false/, "darkMode: true");
792
- await fs8.writeFile(configPath, updated);
792
+ await fs9.writeFile(configPath, updated);
793
793
  }
794
794
  async function wireLayout(projectRoot, ctx) {
795
- const layoutPath = path8.join(projectRoot, "src", "app", "layout.tsx");
796
- if (!await fs8.pathExists(layoutPath)) return;
795
+ const layoutPath = path9.join(projectRoot, "src", "app", "layout.tsx");
796
+ if (!await fs9.pathExists(layoutPath)) return;
797
797
  await ctx.trackModifiedFile(layoutPath);
798
- let content = await fs8.readFile(layoutPath, "utf-8");
798
+ let content = await fs9.readFile(layoutPath, "utf-8");
799
799
  if (!content.includes("getThemeScript")) {
800
800
  content = `import { getThemeScript } from '@/features/dark-mode';
801
801
  ${content}`;
@@ -809,13 +809,13 @@ ${content}`;
809
809
  "$1\n <head>\n <script dangerouslySetInnerHTML={{ __html: getThemeScript() }} />\n </head>\n$2"
810
810
  );
811
811
  }
812
- await fs8.writeFile(layoutPath, content);
812
+ await fs9.writeFile(layoutPath, content);
813
813
  }
814
814
  async function wireProviders(projectRoot, ctx) {
815
- const providersPath = path8.join(projectRoot, "src", "app", "providers.tsx");
816
- if (!await fs8.pathExists(providersPath)) return;
815
+ const providersPath = path9.join(projectRoot, "src", "app", "providers.tsx");
816
+ if (!await fs9.pathExists(providersPath)) return;
817
817
  await ctx.trackModifiedFile(providersPath);
818
- let content = await fs8.readFile(providersPath, "utf-8");
818
+ let content = await fs9.readFile(providersPath, "utf-8");
819
819
  if (content.includes("ThemeProvider")) return;
820
820
  content = insertImportAfterDirectives(
821
821
  content,
@@ -834,13 +834,13 @@ async function wireProviders(projectRoot, ctx) {
834
834
  );`;
835
835
  }
836
836
  );
837
- await fs8.writeFile(providersPath, content);
837
+ await fs9.writeFile(providersPath, content);
838
838
  }
839
839
  async function wireProtectedNav(projectRoot, ctx) {
840
- const navPath = path8.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
841
- if (!await fs8.pathExists(navPath)) return;
840
+ const navPath = path9.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
841
+ if (!await fs9.pathExists(navPath)) return;
842
842
  await ctx.trackModifiedFile(navPath);
843
- let content = await fs8.readFile(navPath, "utf-8");
843
+ let content = await fs9.readFile(navPath, "utf-8");
844
844
  if (content.includes("ThemeToggleSimple")) return;
845
845
  content = insertImportAfterDirectives(
846
846
  content,
@@ -855,19 +855,19 @@ async function wireProtectedNav(projectRoot, ctx) {
855
855
  /(<div className="border-t border-border-default px-4 py-3">\s*\n\s*<UserMenu \/>)/,
856
856
  '$1\n <div className="mt-2">\n <ThemeToggleSimple />\n </div>'
857
857
  );
858
- await fs8.writeFile(navPath, content);
858
+ await fs9.writeFile(navPath, content);
859
859
  }
860
860
  async function wireTailwindDarkMode(projectRoot, ctx) {
861
- const globalsPath = path8.join(projectRoot, "src", "styles", "globals.css");
862
- if (!await fs8.pathExists(globalsPath)) return;
861
+ const globalsPath = path9.join(projectRoot, "src", "styles", "globals.css");
862
+ if (!await fs9.pathExists(globalsPath)) return;
863
863
  await ctx.trackModifiedFile(globalsPath);
864
- let content = await fs8.readFile(globalsPath, "utf-8");
864
+ let content = await fs9.readFile(globalsPath, "utf-8");
865
865
  if (content.includes("@custom-variant dark")) return;
866
866
  content = content.replace(
867
867
  /@import 'tailwindcss';/,
868
868
  "@import 'tailwindcss';\n@custom-variant dark (&:where(.dark, .dark *));"
869
869
  );
870
- await fs8.writeFile(globalsPath, content);
870
+ await fs9.writeFile(globalsPath, content);
871
871
  }
872
872
  function themeProvider() {
873
873
  return `${STAMP2}
@@ -1147,19 +1147,19 @@ var init_dark_mode = __esm({
1147
1147
  });
1148
1148
 
1149
1149
  // src/utils/prisma.ts
1150
- import fs9 from "fs-extra";
1151
- import path9 from "path";
1150
+ import fs10 from "fs-extra";
1151
+ import path10 from "path";
1152
1152
  async function addUserRelation(projectRoot, field, ctx) {
1153
- const authPath = path9.join(projectRoot, "prisma", "schema", "auth.prisma");
1154
- if (!await fs9.pathExists(authPath)) return;
1153
+ const authPath = path10.join(projectRoot, "prisma", "schema", "auth.prisma");
1154
+ if (!await fs10.pathExists(authPath)) return;
1155
1155
  await ctx.trackModifiedFile(authPath);
1156
- let content = await fs9.readFile(authPath, "utf-8");
1156
+ let content = await fs10.readFile(authPath, "utf-8");
1157
1157
  if (content.includes(field)) return;
1158
1158
  const insertBefore = " @@index([email])";
1159
1159
  content = content.replace(insertBefore, ` ${field}
1160
1160
 
1161
1161
  ${insertBefore}`);
1162
- await fs9.writeFile(authPath, content);
1162
+ await fs10.writeFile(authPath, content);
1163
1163
  }
1164
1164
  var init_prisma = __esm({
1165
1165
  "src/utils/prisma.ts"() {
@@ -1172,11 +1172,11 @@ var notifications_exports = {};
1172
1172
  __export(notifications_exports, {
1173
1173
  generateNotifications: () => generateNotifications
1174
1174
  });
1175
- import fs10 from "fs-extra";
1176
- import path10 from "path";
1175
+ import fs11 from "fs-extra";
1176
+ import path11 from "path";
1177
1177
  async function generateNotifications(projectRoot) {
1178
- const featureDir = path10.join(projectRoot, "src", "features", "notifications");
1179
- if (await fs10.pathExists(featureDir)) {
1178
+ const featureDir = path11.join(projectRoot, "src", "features", "notifications");
1179
+ if (await fs11.pathExists(featureDir)) {
1180
1180
  log.error("Notifications feature already exists at src/features/notifications/");
1181
1181
  return;
1182
1182
  }
@@ -1199,10 +1199,10 @@ async function generateNotifications(projectRoot) {
1199
1199
  };
1200
1200
  let count = 0;
1201
1201
  for (const [filePath, content] of Object.entries(files)) {
1202
- const fullPath = path10.join(projectRoot, filePath);
1202
+ const fullPath = path11.join(projectRoot, filePath);
1203
1203
  ctx.trackCreatedFile(fullPath);
1204
- await fs10.ensureDir(path10.dirname(fullPath));
1205
- await fs10.writeFile(fullPath, content);
1204
+ await fs11.ensureDir(path11.dirname(fullPath));
1205
+ await fs11.writeFile(fullPath, content);
1206
1206
  count++;
1207
1207
  }
1208
1208
  await addUserRelation(projectRoot, "notifications Notification[]", ctx);
@@ -1229,10 +1229,10 @@ async function generateNotifications(projectRoot) {
1229
1229
  }
1230
1230
  }
1231
1231
  async function wireProtectedNav2(projectRoot, ctx) {
1232
- const navPath = path10.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
1233
- if (!await fs10.pathExists(navPath)) return;
1232
+ const navPath = path11.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
1233
+ if (!await fs11.pathExists(navPath)) return;
1234
1234
  await ctx.trackModifiedFile(navPath);
1235
- let content = await fs10.readFile(navPath, "utf-8");
1235
+ let content = await fs11.readFile(navPath, "utf-8");
1236
1236
  if (content.includes("NotificationBell")) return;
1237
1237
  content = insertImportAfterDirectives(
1238
1238
  content,
@@ -1243,15 +1243,15 @@ async function wireProtectedNav2(projectRoot, ctx) {
1243
1243
  /(<div className="flex items-center gap-3">)\s*\n(\s*)(<div className="hidden md:block">)/,
1244
1244
  "$1\n$2<NotificationBell />\n$2$3"
1245
1245
  );
1246
- await fs10.writeFile(navPath, content);
1246
+ await fs11.writeFile(navPath, content);
1247
1247
  }
1248
1248
  async function setConfigFlag3(projectRoot, ctx) {
1249
- const configPath = path10.join(projectRoot, "src", "config", "app.config.ts");
1250
- if (!await fs10.pathExists(configPath)) return;
1249
+ const configPath = path11.join(projectRoot, "src", "config", "app.config.ts");
1250
+ if (!await fs11.pathExists(configPath)) return;
1251
1251
  await ctx.trackModifiedFile(configPath);
1252
- const content = await fs10.readFile(configPath, "utf-8");
1252
+ const content = await fs11.readFile(configPath, "utf-8");
1253
1253
  const updated = content.replace(/notifications:\s*false/, "notifications: true");
1254
- await fs10.writeFile(configPath, updated);
1254
+ await fs11.writeFile(configPath, updated);
1255
1255
  }
1256
1256
  function prismaSchema() {
1257
1257
  return `// Generated by mars generate notifications (notifications@${GENERATOR_VERSION3})
@@ -1847,11 +1847,11 @@ var analytics_exports = {};
1847
1847
  __export(analytics_exports, {
1848
1848
  generateAnalytics: () => generateAnalytics
1849
1849
  });
1850
- import fs11 from "fs-extra";
1851
- import path11 from "path";
1850
+ import fs12 from "fs-extra";
1851
+ import path12 from "path";
1852
1852
  async function generateAnalytics(projectRoot) {
1853
- const featureDir = path11.join(projectRoot, "src", "features", "analytics");
1854
- if (await fs11.pathExists(featureDir)) {
1853
+ const featureDir = path12.join(projectRoot, "src", "features", "analytics");
1854
+ if (await fs12.pathExists(featureDir)) {
1855
1855
  log.error("Analytics feature already exists at src/features/analytics/");
1856
1856
  return;
1857
1857
  }
@@ -1866,10 +1866,10 @@ async function generateAnalytics(projectRoot) {
1866
1866
  };
1867
1867
  let count = 0;
1868
1868
  for (const [filePath, content] of Object.entries(files)) {
1869
- const fullPath = path11.join(projectRoot, filePath);
1869
+ const fullPath = path12.join(projectRoot, filePath);
1870
1870
  ctx.trackCreatedFile(fullPath);
1871
- await fs11.ensureDir(path11.dirname(fullPath));
1872
- await fs11.writeFile(fullPath, content);
1871
+ await fs12.ensureDir(path12.dirname(fullPath));
1872
+ await fs12.writeFile(fullPath, content);
1873
1873
  count++;
1874
1874
  }
1875
1875
  await wireProviders2(projectRoot, ctx);
@@ -1900,10 +1900,10 @@ async function generateAnalytics(projectRoot) {
1900
1900
  }
1901
1901
  }
1902
1902
  async function wireProviders2(projectRoot, ctx) {
1903
- const providersPath = path11.join(projectRoot, "src", "app", "providers.tsx");
1904
- if (!await fs11.pathExists(providersPath)) return;
1903
+ const providersPath = path12.join(projectRoot, "src", "app", "providers.tsx");
1904
+ if (!await fs12.pathExists(providersPath)) return;
1905
1905
  await ctx.trackModifiedFile(providersPath);
1906
- let content = await fs11.readFile(providersPath, "utf-8");
1906
+ let content = await fs12.readFile(providersPath, "utf-8");
1907
1907
  if (content.includes("AnalyticsProvider")) return;
1908
1908
  content = insertImportAfterDirectives(
1909
1909
  content,
@@ -1918,8 +1918,8 @@ async function wireProviders2(projectRoot, ctx) {
1918
1918
  );`;
1919
1919
  }
1920
1920
  );
1921
- await fs11.writeFile(providersPath, content);
1922
- const written = await fs11.readFile(providersPath, "utf-8");
1921
+ await fs12.writeFile(providersPath, content);
1922
+ const written = await fs12.readFile(providersPath, "utf-8");
1923
1923
  if (!written.includes("AnalyticsProvider")) {
1924
1924
  throw new Error(
1925
1925
  "wireProviders: AnalyticsProvider was not inserted into providers.tsx \u2014 the return statement pattern did not match. Review the template file structure."
@@ -1927,12 +1927,12 @@ async function wireProviders2(projectRoot, ctx) {
1927
1927
  }
1928
1928
  }
1929
1929
  async function setConfigFlag4(projectRoot, ctx) {
1930
- const configPath = path11.join(projectRoot, "src", "config", "app.config.ts");
1931
- if (!await fs11.pathExists(configPath)) return;
1930
+ const configPath = path12.join(projectRoot, "src", "config", "app.config.ts");
1931
+ if (!await fs12.pathExists(configPath)) return;
1932
1932
  await ctx.trackModifiedFile(configPath);
1933
- const content = await fs11.readFile(configPath, "utf-8");
1933
+ const content = await fs12.readFile(configPath, "utf-8");
1934
1934
  const updated = content.replace(/analytics:\s*false/, "analytics: true");
1935
- await fs11.writeFile(configPath, updated);
1935
+ await fs12.writeFile(configPath, updated);
1936
1936
  }
1937
1937
  function types3() {
1938
1938
  return `${STAMP4}
@@ -2291,11 +2291,11 @@ var command_palette_exports = {};
2291
2291
  __export(command_palette_exports, {
2292
2292
  generateCommandPalette: () => generateCommandPalette
2293
2293
  });
2294
- import fs12 from "fs-extra";
2295
- import path12 from "path";
2294
+ import fs13 from "fs-extra";
2295
+ import path13 from "path";
2296
2296
  async function generateCommandPalette(projectRoot) {
2297
- const featureDir = path12.join(projectRoot, "src", "features", "command-palette");
2298
- if (await fs12.pathExists(featureDir)) {
2297
+ const featureDir = path13.join(projectRoot, "src", "features", "command-palette");
2298
+ if (await fs13.pathExists(featureDir)) {
2299
2299
  log.error("Command palette feature already exists at src/features/command-palette/");
2300
2300
  return;
2301
2301
  }
@@ -2313,10 +2313,10 @@ async function generateCommandPalette(projectRoot) {
2313
2313
  };
2314
2314
  let count = 0;
2315
2315
  for (const [filePath, content] of Object.entries(files)) {
2316
- const fullPath = path12.join(projectRoot, filePath);
2316
+ const fullPath = path13.join(projectRoot, filePath);
2317
2317
  ctx.trackCreatedFile(fullPath);
2318
- await fs12.ensureDir(path12.dirname(fullPath));
2319
- await fs12.writeFile(fullPath, content);
2318
+ await fs13.ensureDir(path13.dirname(fullPath));
2319
+ await fs13.writeFile(fullPath, content);
2320
2320
  count++;
2321
2321
  }
2322
2322
  await addDependencies(projectRoot, { cmdk: "^1.0.0" });
@@ -2339,9 +2339,9 @@ async function generateCommandPalette(projectRoot) {
2339
2339
  }
2340
2340
  }
2341
2341
  async function wireLayout2(projectRoot, ctx) {
2342
- const layoutPath = path12.join(projectRoot, "src", "app", "layout.tsx");
2343
- if (!await fs12.pathExists(layoutPath)) return;
2344
- let content = await fs12.readFile(layoutPath, "utf-8");
2342
+ const layoutPath = path13.join(projectRoot, "src", "app", "layout.tsx");
2343
+ if (!await fs13.pathExists(layoutPath)) return;
2344
+ let content = await fs13.readFile(layoutPath, "utf-8");
2345
2345
  if (content.includes("CommandPalette")) return;
2346
2346
  await ctx.trackModifiedFile(layoutPath);
2347
2347
  content = `import { CommandPalette } from '@/features/command-palette';
@@ -2350,15 +2350,15 @@ ${content}`;
2350
2350
  /(\s*)(<Providers>[\s\S]*?<\/Providers>)/,
2351
2351
  "$1$2\n$1<CommandPalette />"
2352
2352
  );
2353
- await fs12.writeFile(layoutPath, content);
2353
+ await fs13.writeFile(layoutPath, content);
2354
2354
  }
2355
2355
  async function setConfigFlag5(projectRoot, ctx) {
2356
- const configPath = path12.join(projectRoot, "src", "config", "app.config.ts");
2357
- if (!await fs12.pathExists(configPath)) return;
2356
+ const configPath = path13.join(projectRoot, "src", "config", "app.config.ts");
2357
+ if (!await fs13.pathExists(configPath)) return;
2358
2358
  await ctx.trackModifiedFile(configPath);
2359
- const content = await fs12.readFile(configPath, "utf-8");
2359
+ const content = await fs13.readFile(configPath, "utf-8");
2360
2360
  const updated = content.replace(/commandPalette:\s*false/, "commandPalette: true");
2361
- await fs12.writeFile(configPath, updated);
2361
+ await fs13.writeFile(configPath, updated);
2362
2362
  }
2363
2363
  function types4() {
2364
2364
  return `${STAMP5}
@@ -2774,11 +2774,11 @@ var onboarding_exports = {};
2774
2774
  __export(onboarding_exports, {
2775
2775
  generateOnboarding: () => generateOnboarding
2776
2776
  });
2777
- import fs13 from "fs-extra";
2778
- import path13 from "path";
2777
+ import fs14 from "fs-extra";
2778
+ import path14 from "path";
2779
2779
  async function generateOnboarding(projectRoot) {
2780
- const featureDir = path13.join(projectRoot, "src", "features", "onboarding");
2781
- if (await fs13.pathExists(featureDir)) {
2780
+ const featureDir = path14.join(projectRoot, "src", "features", "onboarding");
2781
+ if (await fs14.pathExists(featureDir)) {
2782
2782
  log.error("Onboarding feature already exists at src/features/onboarding/");
2783
2783
  return;
2784
2784
  }
@@ -2800,10 +2800,10 @@ async function generateOnboarding(projectRoot) {
2800
2800
  };
2801
2801
  let count = 0;
2802
2802
  for (const [filePath, content] of Object.entries(files)) {
2803
- const fullPath = path13.join(projectRoot, filePath);
2803
+ const fullPath = path14.join(projectRoot, filePath);
2804
2804
  ctx.trackCreatedFile(fullPath);
2805
- await fs13.ensureDir(path13.dirname(fullPath));
2806
- await fs13.writeFile(fullPath, content);
2805
+ await fs14.ensureDir(path14.dirname(fullPath));
2806
+ await fs14.writeFile(fullPath, content);
2807
2807
  count++;
2808
2808
  }
2809
2809
  await addUserRelation(projectRoot, "onboardingProgress OnboardingProgress?", ctx);
@@ -2831,7 +2831,7 @@ async function generateOnboarding(projectRoot) {
2831
2831
  }
2832
2832
  }
2833
2833
  async function patchDashboardWithOnboardingRedirect(projectRoot, ctx) {
2834
- const dashboardPath = path13.join(
2834
+ const dashboardPath = path14.join(
2835
2835
  projectRoot,
2836
2836
  "src",
2837
2837
  "app",
@@ -2839,9 +2839,9 @@ async function patchDashboardWithOnboardingRedirect(projectRoot, ctx) {
2839
2839
  "dashboard",
2840
2840
  "page.tsx"
2841
2841
  );
2842
- if (!await fs13.pathExists(dashboardPath)) return;
2842
+ if (!await fs14.pathExists(dashboardPath)) return;
2843
2843
  await ctx.trackModifiedFile(dashboardPath);
2844
- let content = await fs13.readFile(dashboardPath, "utf-8");
2844
+ let content = await fs14.readFile(dashboardPath, "utf-8");
2845
2845
  if (content.includes("isOnboardingComplete")) return;
2846
2846
  const redirectImport = `import { redirect } from 'next/navigation';
2847
2847
  `;
@@ -2876,27 +2876,27 @@ async function patchDashboardWithOnboardingRedirect(projectRoot, ctx) {
2876
2876
  `;
2877
2877
  content = content.slice(0, insertAt) + redirectBlock + content.slice(insertAt);
2878
2878
  }
2879
- await fs13.writeFile(dashboardPath, content);
2879
+ await fs14.writeFile(dashboardPath, content);
2880
2880
  }
2881
2881
  async function patchProxyWithOnboardingRoute(projectRoot, ctx) {
2882
- const proxyPath = path13.join(projectRoot, "src", "proxy.ts");
2883
- if (!await fs13.pathExists(proxyPath)) return;
2882
+ const proxyPath = path14.join(projectRoot, "src", "proxy.ts");
2883
+ if (!await fs14.pathExists(proxyPath)) return;
2884
2884
  await ctx.trackModifiedFile(proxyPath);
2885
- let content = await fs13.readFile(proxyPath, "utf-8");
2885
+ let content = await fs14.readFile(proxyPath, "utf-8");
2886
2886
  if (content.includes("routes.onboarding")) return;
2887
2887
  content = content.replace(
2888
2888
  /const protectedRoutes = \[([^\]]*)\]/,
2889
2889
  "const protectedRoutes = [$1, routes.onboarding]"
2890
2890
  );
2891
- await fs13.writeFile(proxyPath, content);
2891
+ await fs14.writeFile(proxyPath, content);
2892
2892
  }
2893
2893
  async function setConfigFlag6(projectRoot, ctx) {
2894
- const configPath = path13.join(projectRoot, "src", "config", "app.config.ts");
2895
- if (!await fs13.pathExists(configPath)) return;
2894
+ const configPath = path14.join(projectRoot, "src", "config", "app.config.ts");
2895
+ if (!await fs14.pathExists(configPath)) return;
2896
2896
  await ctx.trackModifiedFile(configPath);
2897
- const content = await fs13.readFile(configPath, "utf-8");
2897
+ const content = await fs14.readFile(configPath, "utf-8");
2898
2898
  const updated = content.replace(/onboarding:\s*false/, "onboarding: true");
2899
- await fs13.writeFile(configPath, updated);
2899
+ await fs14.writeFile(configPath, updated);
2900
2900
  }
2901
2901
  function prismaSchema2() {
2902
2902
  return `// Generated by mars generate onboarding (onboarding@${GENERATOR_VERSION6})
@@ -3423,11 +3423,11 @@ var search_exports = {};
3423
3423
  __export(search_exports, {
3424
3424
  generateSearch: () => generateSearch
3425
3425
  });
3426
- import fs14 from "fs-extra";
3427
- import path14 from "path";
3426
+ import fs15 from "fs-extra";
3427
+ import path15 from "path";
3428
3428
  async function generateSearch(projectRoot) {
3429
- const featureDir = path14.join(projectRoot, "src", "features", "search");
3430
- if (await fs14.pathExists(featureDir)) {
3429
+ const featureDir = path15.join(projectRoot, "src", "features", "search");
3430
+ if (await fs15.pathExists(featureDir)) {
3431
3431
  log.error("Search feature already exists at src/features/search/");
3432
3432
  return;
3433
3433
  }
@@ -3447,10 +3447,10 @@ async function generateSearch(projectRoot) {
3447
3447
  };
3448
3448
  let count = 0;
3449
3449
  for (const [filePath, content] of Object.entries(files)) {
3450
- const fullPath = path14.join(projectRoot, filePath);
3450
+ const fullPath = path15.join(projectRoot, filePath);
3451
3451
  ctx.trackCreatedFile(fullPath);
3452
- await fs14.ensureDir(path14.dirname(fullPath));
3453
- await fs14.writeFile(fullPath, content);
3452
+ await fs15.ensureDir(path15.dirname(fullPath));
3453
+ await fs15.writeFile(fullPath, content);
3454
3454
  count++;
3455
3455
  }
3456
3456
  await wireProtectedNav3(projectRoot, ctx);
@@ -3478,9 +3478,9 @@ async function generateSearch(projectRoot) {
3478
3478
  }
3479
3479
  }
3480
3480
  async function wireProtectedNav3(projectRoot, ctx) {
3481
- const navPath = path14.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
3482
- if (!await fs14.pathExists(navPath)) return;
3483
- let content = await fs14.readFile(navPath, "utf-8");
3481
+ const navPath = path15.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
3482
+ if (!await fs15.pathExists(navPath)) return;
3483
+ let content = await fs15.readFile(navPath, "utf-8");
3484
3484
  if (content.includes("NavSearch")) return;
3485
3485
  await ctx.trackModifiedFile(navPath);
3486
3486
  content = insertImportAfterDirectives(
@@ -3492,15 +3492,15 @@ async function wireProtectedNav3(projectRoot, ctx) {
3492
3492
  /(<div className="flex items-center gap-3">)\s*\n(\s*)(<)/,
3493
3493
  "$1\n$2<NavSearch />\n$2$3"
3494
3494
  );
3495
- await fs14.writeFile(navPath, content);
3495
+ await fs15.writeFile(navPath, content);
3496
3496
  }
3497
3497
  async function setConfigFlag7(projectRoot, ctx) {
3498
- const configPath = path14.join(projectRoot, "src", "config", "app.config.ts");
3499
- if (!await fs14.pathExists(configPath)) return;
3498
+ const configPath = path15.join(projectRoot, "src", "config", "app.config.ts");
3499
+ if (!await fs15.pathExists(configPath)) return;
3500
3500
  await ctx.trackModifiedFile(configPath);
3501
- const content = await fs14.readFile(configPath, "utf-8");
3501
+ const content = await fs15.readFile(configPath, "utf-8");
3502
3502
  const updated = content.replace(/search:\s*false/, "search: true");
3503
- await fs14.writeFile(configPath, updated);
3503
+ await fs15.writeFile(configPath, updated);
3504
3504
  }
3505
3505
  function types6() {
3506
3506
  return `${STAMP7}
@@ -3854,11 +3854,11 @@ var realtime_exports = {};
3854
3854
  __export(realtime_exports, {
3855
3855
  generateRealtime: () => generateRealtime
3856
3856
  });
3857
- import fs15 from "fs-extra";
3858
- import path15 from "path";
3857
+ import fs16 from "fs-extra";
3858
+ import path16 from "path";
3859
3859
  async function generateRealtime(projectRoot) {
3860
- const featureDir = path15.join(projectRoot, "src", "features", "realtime");
3861
- if (await fs15.pathExists(featureDir)) {
3860
+ const featureDir = path16.join(projectRoot, "src", "features", "realtime");
3861
+ if (await fs16.pathExists(featureDir)) {
3862
3862
  log.error("Realtime feature already exists at src/features/realtime/");
3863
3863
  return;
3864
3864
  }
@@ -3875,10 +3875,10 @@ async function generateRealtime(projectRoot) {
3875
3875
  };
3876
3876
  let count = 0;
3877
3877
  for (const [filePath, content] of Object.entries(files)) {
3878
- const fullPath = path15.join(projectRoot, filePath);
3878
+ const fullPath = path16.join(projectRoot, filePath);
3879
3879
  ctx.trackCreatedFile(fullPath);
3880
- await fs15.ensureDir(path15.dirname(fullPath));
3881
- await fs15.writeFile(fullPath, content);
3880
+ await fs16.ensureDir(path16.dirname(fullPath));
3881
+ await fs16.writeFile(fullPath, content);
3882
3882
  count++;
3883
3883
  }
3884
3884
  await setConfigFlag8(projectRoot, ctx);
@@ -3903,12 +3903,12 @@ async function generateRealtime(projectRoot) {
3903
3903
  }
3904
3904
  }
3905
3905
  async function setConfigFlag8(projectRoot, ctx) {
3906
- const configPath = path15.join(projectRoot, "src", "config", "app.config.ts");
3907
- if (!await fs15.pathExists(configPath)) return;
3906
+ const configPath = path16.join(projectRoot, "src", "config", "app.config.ts");
3907
+ if (!await fs16.pathExists(configPath)) return;
3908
3908
  await ctx.trackModifiedFile(configPath);
3909
- const content = await fs15.readFile(configPath, "utf-8");
3909
+ const content = await fs16.readFile(configPath, "utf-8");
3910
3910
  const updated = content.replace(/realtime:\s*false/, "realtime: true");
3911
- await fs15.writeFile(configPath, updated);
3911
+ await fs16.writeFile(configPath, updated);
3912
3912
  }
3913
3913
  function types7() {
3914
3914
  return `${STAMP8}
@@ -4217,11 +4217,11 @@ var ai_exports = {};
4217
4217
  __export(ai_exports, {
4218
4218
  generateAI: () => generateAI
4219
4219
  });
4220
- import fs16 from "fs-extra";
4221
- import path16 from "path";
4220
+ import fs17 from "fs-extra";
4221
+ import path17 from "path";
4222
4222
  async function generateAI(projectRoot) {
4223
- const featureDir = path16.join(projectRoot, "src", "features", "ai");
4224
- if (await fs16.pathExists(featureDir)) {
4223
+ const featureDir = path17.join(projectRoot, "src", "features", "ai");
4224
+ if (await fs17.pathExists(featureDir)) {
4225
4225
  log.error("AI feature already exists at src/features/ai/");
4226
4226
  return;
4227
4227
  }
@@ -4244,10 +4244,10 @@ async function generateAI(projectRoot) {
4244
4244
  };
4245
4245
  let count = 0;
4246
4246
  for (const [filePath, content] of Object.entries(files)) {
4247
- const fullPath = path16.join(projectRoot, filePath);
4247
+ const fullPath = path17.join(projectRoot, filePath);
4248
4248
  ctx.trackCreatedFile(fullPath);
4249
- await fs16.ensureDir(path16.dirname(fullPath));
4250
- await fs16.writeFile(fullPath, content);
4249
+ await fs17.ensureDir(path17.dirname(fullPath));
4250
+ await fs17.writeFile(fullPath, content);
4251
4251
  count++;
4252
4252
  }
4253
4253
  await addDependencies(projectRoot, {
@@ -4275,12 +4275,12 @@ async function generateAI(projectRoot) {
4275
4275
  }
4276
4276
  }
4277
4277
  async function setConfigFlag9(projectRoot, ctx) {
4278
- const configPath = path16.join(projectRoot, "src", "config", "app.config.ts");
4279
- if (!await fs16.pathExists(configPath)) return;
4278
+ const configPath = path17.join(projectRoot, "src", "config", "app.config.ts");
4279
+ if (!await fs17.pathExists(configPath)) return;
4280
4280
  await ctx.trackModifiedFile(configPath);
4281
- const content = await fs16.readFile(configPath, "utf-8");
4281
+ const content = await fs17.readFile(configPath, "utf-8");
4282
4282
  const updated = content.replace(/ai:\s*false/, "ai: true");
4283
- await fs16.writeFile(configPath, updated);
4283
+ await fs17.writeFile(configPath, updated);
4284
4284
  }
4285
4285
  function types8() {
4286
4286
  return `${STAMP9}
@@ -4875,15 +4875,15 @@ export { ChatInput } from './ChatInput';
4875
4875
  `;
4876
4876
  }
4877
4877
  async function wireProtectedNav4(projectRoot, ctx) {
4878
- const layoutPath = path16.join(
4878
+ const layoutPath = path17.join(
4879
4879
  projectRoot,
4880
4880
  "src",
4881
4881
  "app",
4882
4882
  "(protected)",
4883
4883
  "layout.tsx"
4884
4884
  );
4885
- if (!await fs16.pathExists(layoutPath)) return;
4886
- let content = await fs16.readFile(layoutPath, "utf-8");
4885
+ if (!await fs17.pathExists(layoutPath)) return;
4886
+ let content = await fs17.readFile(layoutPath, "utf-8");
4887
4887
  if (content.includes("routes.ai") || content.includes("label: 'AI'")) return;
4888
4888
  await ctx.trackModifiedFile(layoutPath);
4889
4889
  const insertion = ` if (appConfig.features.ai) {
@@ -4902,7 +4902,7 @@ async function wireProtectedNav4(projectRoot, ctx) {
4902
4902
  "// AI nav link added by mars generate ai\n"
4903
4903
  );
4904
4904
  }
4905
- await fs16.writeFile(layoutPath, content);
4905
+ await fs17.writeFile(layoutPath, content);
4906
4906
  }
4907
4907
  function aiPage() {
4908
4908
  return `${STAMP9}
@@ -4991,11 +4991,11 @@ var cookie_consent_exports = {};
4991
4991
  __export(cookie_consent_exports, {
4992
4992
  generateCookieConsent: () => generateCookieConsent
4993
4993
  });
4994
- import fs17 from "fs-extra";
4995
- import path17 from "path";
4994
+ import fs18 from "fs-extra";
4995
+ import path18 from "path";
4996
4996
  async function generateCookieConsent(projectRoot) {
4997
- const featureDir = path17.join(projectRoot, "src", "features", "cookie-consent");
4998
- if (await fs17.pathExists(featureDir)) {
4997
+ const featureDir = path18.join(projectRoot, "src", "features", "cookie-consent");
4998
+ if (await fs18.pathExists(featureDir)) {
4999
4999
  log.error("Cookie consent feature already exists at src/features/cookie-consent/");
5000
5000
  return;
5001
5001
  }
@@ -5011,10 +5011,10 @@ async function generateCookieConsent(projectRoot) {
5011
5011
  };
5012
5012
  let count = 0;
5013
5013
  for (const [filePath, content] of Object.entries(files)) {
5014
- const fullPath = path17.join(projectRoot, filePath);
5014
+ const fullPath = path18.join(projectRoot, filePath);
5015
5015
  ctx.trackCreatedFile(fullPath);
5016
- await fs17.ensureDir(path17.dirname(fullPath));
5017
- await fs17.writeFile(fullPath, content);
5016
+ await fs18.ensureDir(path18.dirname(fullPath));
5017
+ await fs18.writeFile(fullPath, content);
5018
5018
  count++;
5019
5019
  }
5020
5020
  await wireLayout3(projectRoot, ctx);
@@ -5039,9 +5039,9 @@ async function generateCookieConsent(projectRoot) {
5039
5039
  }
5040
5040
  }
5041
5041
  async function wireLayout3(projectRoot, ctx) {
5042
- const layoutPath = path17.join(projectRoot, "src", "app", "layout.tsx");
5043
- if (!await fs17.pathExists(layoutPath)) return;
5044
- let content = await fs17.readFile(layoutPath, "utf-8");
5042
+ const layoutPath = path18.join(projectRoot, "src", "app", "layout.tsx");
5043
+ if (!await fs18.pathExists(layoutPath)) return;
5044
+ let content = await fs18.readFile(layoutPath, "utf-8");
5045
5045
  if (content.includes("CookieConsentBanner")) return;
5046
5046
  await ctx.trackModifiedFile(layoutPath);
5047
5047
  content = `import { CookieConsentBanner } from '@/features/cookie-consent';
@@ -5050,15 +5050,15 @@ ${content}`;
5050
5050
  /(\s*)(<Providers>[\s\S]*?<\/Providers>)/,
5051
5051
  "$1$2\n$1<CookieConsentBanner />"
5052
5052
  );
5053
- await fs17.writeFile(layoutPath, content);
5053
+ await fs18.writeFile(layoutPath, content);
5054
5054
  }
5055
5055
  async function setConfigFlag10(projectRoot, ctx) {
5056
- const configPath = path17.join(projectRoot, "src", "config", "app.config.ts");
5057
- if (!await fs17.pathExists(configPath)) return;
5056
+ const configPath = path18.join(projectRoot, "src", "config", "app.config.ts");
5057
+ if (!await fs18.pathExists(configPath)) return;
5058
5058
  await ctx.trackModifiedFile(configPath);
5059
- const content = await fs17.readFile(configPath, "utf-8");
5059
+ const content = await fs18.readFile(configPath, "utf-8");
5060
5060
  const updated = content.replace(/cookieConsent:\s*false/, "cookieConsent: true");
5061
- await fs17.writeFile(configPath, updated);
5061
+ await fs18.writeFile(configPath, updated);
5062
5062
  }
5063
5063
  function types9() {
5064
5064
  return `${STAMP10}
@@ -5343,11 +5343,11 @@ var coming_soon_exports = {};
5343
5343
  __export(coming_soon_exports, {
5344
5344
  generateComingSoon: () => generateComingSoon
5345
5345
  });
5346
- import fs18 from "fs-extra";
5347
- import path18 from "path";
5346
+ import fs19 from "fs-extra";
5347
+ import path19 from "path";
5348
5348
  async function generateComingSoon(projectRoot) {
5349
- const featureDir = path18.join(projectRoot, "src", "features", "coming-soon");
5350
- if (await fs18.pathExists(featureDir)) {
5349
+ const featureDir = path19.join(projectRoot, "src", "features", "coming-soon");
5350
+ if (await fs19.pathExists(featureDir)) {
5351
5351
  log.error("Coming soon feature already exists at src/features/coming-soon/");
5352
5352
  return;
5353
5353
  }
@@ -5364,10 +5364,10 @@ async function generateComingSoon(projectRoot) {
5364
5364
  };
5365
5365
  let count = 0;
5366
5366
  for (const [filePath, content] of Object.entries(files)) {
5367
- const fullPath = path18.join(projectRoot, filePath);
5367
+ const fullPath = path19.join(projectRoot, filePath);
5368
5368
  ctx.trackCreatedFile(fullPath);
5369
- await fs18.ensureDir(path18.dirname(fullPath));
5370
- await fs18.writeFile(fullPath, content);
5369
+ await fs19.ensureDir(path19.dirname(fullPath));
5370
+ await fs19.writeFile(fullPath, content);
5371
5371
  count++;
5372
5372
  }
5373
5373
  await registerRoute(projectRoot, "comingSoon", "/coming-soon", ctx);
@@ -5389,12 +5389,12 @@ async function generateComingSoon(projectRoot) {
5389
5389
  }
5390
5390
  }
5391
5391
  async function setConfigFlag11(projectRoot, ctx) {
5392
- const configPath = path18.join(projectRoot, "src", "config", "app.config.ts");
5393
- if (!await fs18.pathExists(configPath)) return;
5392
+ const configPath = path19.join(projectRoot, "src", "config", "app.config.ts");
5393
+ if (!await fs19.pathExists(configPath)) return;
5394
5394
  await ctx.trackModifiedFile(configPath);
5395
- const content = await fs18.readFile(configPath, "utf-8");
5395
+ const content = await fs19.readFile(configPath, "utf-8");
5396
5396
  const updated = content.replace(/comingSoon:\s*false/, "comingSoon: true");
5397
- await fs18.writeFile(configPath, updated);
5397
+ await fs19.writeFile(configPath, updated);
5398
5398
  }
5399
5399
  function types10() {
5400
5400
  return `${STAMP11}
@@ -5582,11 +5582,11 @@ var sentry_exports = {};
5582
5582
  __export(sentry_exports, {
5583
5583
  generateSentry: () => generateSentry
5584
5584
  });
5585
- import fs19 from "fs-extra";
5586
- import path19 from "path";
5585
+ import fs20 from "fs-extra";
5586
+ import path20 from "path";
5587
5587
  async function generateSentry(projectRoot) {
5588
- const featureDir = path19.join(projectRoot, "src", "features", "sentry");
5589
- if (await fs19.pathExists(featureDir)) {
5588
+ const featureDir = path20.join(projectRoot, "src", "features", "sentry");
5589
+ if (await fs20.pathExists(featureDir)) {
5590
5590
  log.error("Sentry feature already exists at src/features/sentry/");
5591
5591
  return;
5592
5592
  }
@@ -5602,10 +5602,10 @@ async function generateSentry(projectRoot) {
5602
5602
  };
5603
5603
  let count = 0;
5604
5604
  for (const [filePath, content] of Object.entries(files)) {
5605
- const fullPath = path19.join(projectRoot, filePath);
5605
+ const fullPath = path20.join(projectRoot, filePath);
5606
5606
  ctx.trackCreatedFile(fullPath);
5607
- await fs19.ensureDir(path19.dirname(fullPath));
5608
- await fs19.writeFile(fullPath, content);
5607
+ await fs20.ensureDir(path20.dirname(fullPath));
5608
+ await fs20.writeFile(fullPath, content);
5609
5609
  count++;
5610
5610
  }
5611
5611
  await addDependencies(projectRoot, { "@sentry/nextjs": "^8.0.0" });
@@ -5631,9 +5631,9 @@ async function generateSentry(projectRoot) {
5631
5631
  }
5632
5632
  }
5633
5633
  async function wireRootLayout(projectRoot, ctx) {
5634
- const layoutPath = path19.join(projectRoot, "src", "app", "layout.tsx");
5635
- if (!await fs19.pathExists(layoutPath)) return;
5636
- let content = await fs19.readFile(layoutPath, "utf-8");
5634
+ const layoutPath = path20.join(projectRoot, "src", "app", "layout.tsx");
5635
+ if (!await fs20.pathExists(layoutPath)) return;
5636
+ let content = await fs20.readFile(layoutPath, "utf-8");
5637
5637
  if (content.includes("ErrorBoundary")) return;
5638
5638
  await ctx.trackModifiedFile(layoutPath);
5639
5639
  content = `import { ErrorBoundary } from '@/features/sentry';
@@ -5642,15 +5642,15 @@ ${content}`;
5642
5642
  /(<Providers>)\{children\}(<\/Providers>)/,
5643
5643
  "$1<ErrorBoundary>{children}</ErrorBoundary>$2"
5644
5644
  );
5645
- await fs19.writeFile(layoutPath, content);
5645
+ await fs20.writeFile(layoutPath, content);
5646
5646
  }
5647
5647
  async function setConfigFlag12(projectRoot, ctx) {
5648
- const configPath = path19.join(projectRoot, "src", "config", "app.config.ts");
5649
- if (!await fs19.pathExists(configPath)) return;
5648
+ const configPath = path20.join(projectRoot, "src", "config", "app.config.ts");
5649
+ if (!await fs20.pathExists(configPath)) return;
5650
5650
  await ctx.trackModifiedFile(configPath);
5651
- const content = await fs19.readFile(configPath, "utf-8");
5651
+ const content = await fs20.readFile(configPath, "utf-8");
5652
5652
  const updated = content.replace(/sentry:\s*false/, "sentry: true");
5653
- await fs19.writeFile(configPath, updated);
5653
+ await fs20.writeFile(configPath, updated);
5654
5654
  }
5655
5655
  function types11() {
5656
5656
  return `${STAMP12}
@@ -5795,11 +5795,11 @@ var feature_flags_exports = {};
5795
5795
  __export(feature_flags_exports, {
5796
5796
  generateFeatureFlags: () => generateFeatureFlags
5797
5797
  });
5798
- import fs20 from "fs-extra";
5799
- import path20 from "path";
5798
+ import fs21 from "fs-extra";
5799
+ import path21 from "path";
5800
5800
  async function generateFeatureFlags(projectRoot) {
5801
- const featureDir = path20.join(projectRoot, "src", "features", "feature-flags");
5802
- if (await fs20.pathExists(featureDir)) {
5801
+ const featureDir = path21.join(projectRoot, "src", "features", "feature-flags");
5802
+ if (await fs21.pathExists(featureDir)) {
5803
5803
  log.error("Feature flags feature already exists at src/features/feature-flags/");
5804
5804
  return;
5805
5805
  }
@@ -5818,10 +5818,10 @@ async function generateFeatureFlags(projectRoot) {
5818
5818
  };
5819
5819
  let count = 0;
5820
5820
  for (const [filePath, content] of Object.entries(files)) {
5821
- const fullPath = path20.join(projectRoot, filePath);
5821
+ const fullPath = path21.join(projectRoot, filePath);
5822
5822
  ctx.trackCreatedFile(fullPath);
5823
- await fs20.ensureDir(path20.dirname(fullPath));
5824
- await fs20.writeFile(fullPath, content);
5823
+ await fs21.ensureDir(path21.dirname(fullPath));
5824
+ await fs21.writeFile(fullPath, content);
5825
5825
  count++;
5826
5826
  }
5827
5827
  await setConfigFlag13(projectRoot, ctx);
@@ -5841,12 +5841,12 @@ async function generateFeatureFlags(projectRoot) {
5841
5841
  }
5842
5842
  }
5843
5843
  async function setConfigFlag13(projectRoot, ctx) {
5844
- const configPath = path20.join(projectRoot, "src", "config", "app.config.ts");
5845
- if (!await fs20.pathExists(configPath)) return;
5844
+ const configPath = path21.join(projectRoot, "src", "config", "app.config.ts");
5845
+ if (!await fs21.pathExists(configPath)) return;
5846
5846
  await ctx.trackModifiedFile(configPath);
5847
- const content = await fs20.readFile(configPath, "utf-8");
5847
+ const content = await fs21.readFile(configPath, "utf-8");
5848
5848
  const updated = content.replace(/featureFlags:\s*false/, "featureFlags: true");
5849
- await fs20.writeFile(configPath, updated);
5849
+ await fs21.writeFile(configPath, updated);
5850
5850
  }
5851
5851
  function types12() {
5852
5852
  return `${STAMP13}
@@ -6113,8 +6113,8 @@ function getCliVersion() {
6113
6113
 
6114
6114
  // src/commands/create.ts
6115
6115
  init_logger();
6116
- import fs22 from "fs-extra";
6117
- import path22 from "path";
6116
+ import fs23 from "fs-extra";
6117
+ import path23 from "path";
6118
6118
  import os3 from "os";
6119
6119
  import ora from "ora";
6120
6120
  import pc2 from "picocolors";
@@ -6540,9 +6540,50 @@ async function promptTheme() {
6540
6540
 
6541
6541
  // src/generators/scaffold.ts
6542
6542
  init_logger();
6543
+ import fs4 from "fs-extra";
6544
+ import path4 from "path";
6545
+ import { generateBrandCss } from "@mars-stack/ui/utils";
6546
+
6547
+ // src/utils/fingerprint.ts
6548
+ import { createHash } from "crypto";
6543
6549
  import fs3 from "fs-extra";
6544
6550
  import path3 from "path";
6545
- import { generateBrandCss } from "@mars-stack/ui/utils";
6551
+ init_logger();
6552
+ async function checksumFile(filePath) {
6553
+ const content = await fs3.readFile(filePath);
6554
+ return createHash("sha256").update(content).digest("hex");
6555
+ }
6556
+ var PLUMBING_FILES_TO_CHECKSUM = [
6557
+ "scripts/ensure-db.mjs",
6558
+ "src/middleware.ts",
6559
+ "src/app/(auth)/sign-in/page.tsx",
6560
+ "src/app/(auth)/register/page.tsx",
6561
+ "src/app/(auth)/verify/page.tsx",
6562
+ "src/lib/mars.ts"
6563
+ ];
6564
+ async function writeScaffoldFingerprint(projectDir) {
6565
+ try {
6566
+ const checksums = {};
6567
+ for (const relativePath of PLUMBING_FILES_TO_CHECKSUM) {
6568
+ const fullPath = path3.join(projectDir, relativePath);
6569
+ if (await fs3.pathExists(fullPath)) {
6570
+ checksums[relativePath] = await checksumFile(fullPath);
6571
+ }
6572
+ }
6573
+ const fingerprint = {
6574
+ schemaVersion: 1,
6575
+ cliVersion: getCliVersion(),
6576
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
6577
+ template: "default",
6578
+ checksums
6579
+ };
6580
+ const marsDir = path3.join(projectDir, ".mars");
6581
+ await fs3.ensureDir(marsDir);
6582
+ await fs3.writeJson(path3.join(marsDir, "scaffold.json"), fingerprint, { spaces: 2 });
6583
+ } catch (error) {
6584
+ log.warn("Could not write .mars/scaffold.json \u2014 scaffold continues without fingerprint.");
6585
+ }
6586
+ }
6546
6587
 
6547
6588
  // src/generators/app-config.ts
6548
6589
  function escapeQuotes(value) {
@@ -6725,17 +6766,17 @@ var IGNORED_FILES = [
6725
6766
  async function copyTemplateFiles(templateDir, targetDir) {
6726
6767
  let fileCount = 0;
6727
6768
  async function walkAndCopy(src, dest) {
6728
- const entries = await fs3.readdir(src, { withFileTypes: true });
6769
+ const entries = await fs4.readdir(src, { withFileTypes: true });
6729
6770
  for (const entry of entries) {
6730
- const srcPath = path3.join(src, entry.name);
6731
- const destPath = path3.join(dest, entry.name);
6771
+ const srcPath = path4.join(src, entry.name);
6772
+ const destPath = path4.join(dest, entry.name);
6732
6773
  if (entry.isDirectory()) {
6733
6774
  if (IGNORED_DIRS.includes(entry.name)) continue;
6734
- await fs3.ensureDir(destPath);
6775
+ await fs4.ensureDir(destPath);
6735
6776
  await walkAndCopy(srcPath, destPath);
6736
6777
  } else {
6737
6778
  if (IGNORED_FILES.includes(entry.name)) continue;
6738
- await fs3.copy(srcPath, destPath);
6779
+ await fs4.copy(srcPath, destPath);
6739
6780
  fileCount++;
6740
6781
  }
6741
6782
  }
@@ -6746,19 +6787,19 @@ async function copyTemplateFiles(templateDir, targetDir) {
6746
6787
  function resolveMarsPackageRange(packageName, templateDir) {
6747
6788
  const shortName = packageName.replace("@mars-stack/", "");
6748
6789
  const candidates = [
6749
- path3.resolve(templateDir, "..", "packages", shortName, "package.json"),
6750
- path3.resolve(templateDir, "..", "..", "packages", shortName, "package.json")
6790
+ path4.resolve(templateDir, "..", "packages", shortName, "package.json"),
6791
+ path4.resolve(templateDir, "..", "..", "packages", shortName, "package.json")
6751
6792
  ];
6752
6793
  for (const candidate of candidates) {
6753
- if (fs3.existsSync(candidate)) {
6754
- const pkg = fs3.readJsonSync(candidate);
6794
+ if (fs4.existsSync(candidate)) {
6795
+ const pkg = fs4.readJsonSync(candidate);
6755
6796
  if (typeof pkg.version === "string") {
6756
6797
  const [major, minor] = pkg.version.split(".");
6757
6798
  return `^${major}.${minor}.0`;
6758
6799
  }
6759
6800
  }
6760
6801
  }
6761
- const templatePkg = fs3.readJsonSync(path3.join(templateDir, "package.json"));
6802
+ const templatePkg = fs4.readJsonSync(path4.join(templateDir, "package.json"));
6762
6803
  const existing = templatePkg.dependencies?.[packageName];
6763
6804
  if (existing && existing !== "*") {
6764
6805
  if (/^[\^~>=]/.test(existing)) return existing;
@@ -6769,8 +6810,8 @@ function resolveMarsPackageRange(packageName, templateDir) {
6769
6810
  }
6770
6811
  function generatePackageJson(config) {
6771
6812
  const templateDir = getTemplatePath();
6772
- const templatePkg = path3.join(templateDir, "package.json");
6773
- const pkg = fs3.readJsonSync(templatePkg);
6813
+ const templatePkg = path4.join(templateDir, "package.json");
6814
+ const pkg = fs4.readJsonSync(templatePkg);
6774
6815
  pkg.name = config.name;
6775
6816
  pkg.version = "0.1.0";
6776
6817
  pkg.private = true;
@@ -6920,8 +6961,9 @@ dist/
6920
6961
  .env.development.local
6921
6962
  .env.test
6922
6963
 
6923
- # local database
6924
- .mars/
6964
+ # local database (keep scaffold.json for project metadata)
6965
+ .mars/*
6966
+ !.mars/scaffold.json
6925
6967
 
6926
6968
  # misc
6927
6969
  .DS_Store
@@ -6971,9 +7013,9 @@ async function pruneDisabledFeatures(features, targetDir) {
6971
7013
  const flagValue = features[feature];
6972
7014
  if (flagValue !== false) continue;
6973
7015
  for (const relativePath of paths) {
6974
- const fullPath = path3.join(targetDir, relativePath);
6975
- if (await fs3.pathExists(fullPath)) {
6976
- await fs3.remove(fullPath);
7016
+ const fullPath = path4.join(targetDir, relativePath);
7017
+ if (await fs4.pathExists(fullPath)) {
7018
+ await fs4.remove(fullPath);
6977
7019
  log.step(`Pruned disabled feature path: ${relativePath}`);
6978
7020
  }
6979
7021
  }
@@ -6982,43 +7024,44 @@ async function pruneDisabledFeatures(features, targetDir) {
6982
7024
  }
6983
7025
  }
6984
7026
  if (relationsToRemove.length > 0) {
6985
- const authPath = path3.join(targetDir, "prisma", "schema", "auth.prisma");
6986
- if (await fs3.pathExists(authPath)) {
6987
- let content = await fs3.readFile(authPath, "utf-8");
7027
+ const authPath = path4.join(targetDir, "prisma", "schema", "auth.prisma");
7028
+ if (await fs4.pathExists(authPath)) {
7029
+ let content = await fs4.readFile(authPath, "utf-8");
6988
7030
  for (const field of relationsToRemove) {
6989
7031
  content = content.replace(new RegExp(`\\s*${field}\\s+\\S+.*\\n`, "g"), "\n");
6990
7032
  }
6991
- await fs3.writeFile(authPath, content);
7033
+ await fs4.writeFile(authPath, content);
6992
7034
  }
6993
7035
  }
6994
7036
  }
6995
7037
  async function scaffoldProject(targetDir, config) {
6996
7038
  const templateDir = getTemplatePath();
6997
- if (!await fs3.pathExists(templateDir)) {
7039
+ if (!await fs4.pathExists(templateDir)) {
6998
7040
  throw new Error(`Template directory not found: ${templateDir}`);
6999
7041
  }
7000
- await fs3.ensureDir(targetDir);
7042
+ await fs4.ensureDir(targetDir);
7001
7043
  const fileCount = await copyTemplateFiles(templateDir, targetDir);
7002
7044
  await pruneDisabledFeatures(config.features, targetDir);
7003
- await fs3.writeFile(path3.join(targetDir, "package.json"), generatePackageJson(config));
7004
- await fs3.writeFile(
7005
- path3.join(targetDir, "src", "config", "app.config.ts"),
7045
+ await fs4.writeFile(path4.join(targetDir, "package.json"), generatePackageJson(config));
7046
+ await fs4.writeFile(
7047
+ path4.join(targetDir, "src", "config", "app.config.ts"),
7006
7048
  generateAppConfig(config)
7007
7049
  );
7008
- await fs3.writeFile(path3.join(targetDir, "docker-compose.yml"), generateDockerCompose(config));
7009
- await fs3.writeFile(path3.join(targetDir, "src", "app", "layout.tsx"), generateLayout(config));
7010
- await fs3.writeFile(path3.join(targetDir, ".env"), generateEnvFile(config));
7011
- await fs3.writeFile(path3.join(targetDir, ".env.example"), generateEnvExample(config));
7012
- await fs3.writeFile(path3.join(targetDir, ".gitignore"), generateGitignore());
7050
+ await fs4.writeFile(path4.join(targetDir, "docker-compose.yml"), generateDockerCompose(config));
7051
+ await fs4.writeFile(path4.join(targetDir, "src", "app", "layout.tsx"), generateLayout(config));
7052
+ await fs4.writeFile(path4.join(targetDir, ".env"), generateEnvFile(config));
7053
+ await fs4.writeFile(path4.join(targetDir, ".env.example"), generateEnvExample(config));
7054
+ await fs4.writeFile(path4.join(targetDir, ".gitignore"), generateGitignore());
7013
7055
  await patchGlobalsCssForScaffoldedProject(targetDir, config);
7014
7056
  await generateBrandCssForProject(targetDir, config);
7057
+ await writeScaffoldFingerprint(targetDir);
7015
7058
  return { fileCount };
7016
7059
  }
7017
7060
  async function generateBrandCssForProject(targetDir, config) {
7018
- const brandPath = path3.join(targetDir, "src", "styles", "brand.css");
7019
- if (!await fs3.pathExists(brandPath)) return;
7061
+ const brandPath = path4.join(targetDir, "src", "styles", "brand.css");
7062
+ if (!await fs4.pathExists(brandPath)) return;
7020
7063
  const css = generateBrandCss(config.theme.primaryColor);
7021
- await fs3.writeFile(brandPath, css);
7064
+ await fs4.writeFile(brandPath, css);
7022
7065
  }
7023
7066
  var VALID_DESIGN_DIRECTIONS = [
7024
7067
  "modern-saas",
@@ -7028,9 +7071,9 @@ var VALID_DESIGN_DIRECTIONS = [
7028
7071
  "dashboard"
7029
7072
  ];
7030
7073
  async function patchGlobalsCssForScaffoldedProject(targetDir, config) {
7031
- const globalsPath = path3.join(targetDir, "src", "styles", "globals.css");
7032
- if (!await fs3.pathExists(globalsPath)) return;
7033
- let content = await fs3.readFile(globalsPath, "utf-8");
7074
+ const globalsPath = path4.join(targetDir, "src", "styles", "globals.css");
7075
+ if (!await fs4.pathExists(globalsPath)) return;
7076
+ let content = await fs4.readFile(globalsPath, "utf-8");
7034
7077
  content = content.replace(
7035
7078
  '@source "../../../node_modules/@mars-stack/ui/dist/**/*.js";',
7036
7079
  '@source "../../node_modules/@mars-stack/ui/dist/**/*.js";'
@@ -7049,7 +7092,7 @@ async function patchGlobalsCssForScaffoldedProject(targetDir, config) {
7049
7092
  ${newImport}`
7050
7093
  );
7051
7094
  }
7052
- await fs3.writeFile(globalsPath, content);
7095
+ await fs4.writeFile(globalsPath, content);
7053
7096
  }
7054
7097
 
7055
7098
  // src/generators/generate-selected.ts
@@ -7165,13 +7208,13 @@ async function generateSelectedFeatures(projectRoot, features) {
7165
7208
  }
7166
7209
 
7167
7210
  // src/utils/telemetry.ts
7168
- import fs21 from "fs-extra";
7169
- import path21 from "path";
7211
+ import fs22 from "fs-extra";
7212
+ import path22 from "path";
7170
7213
  import os2 from "os";
7171
- var RC_PATH = path21.join(os2.homedir(), ".marsrc");
7214
+ var RC_PATH = path22.join(os2.homedir(), ".marsrc");
7172
7215
  function isTelemetryEnabled() {
7173
7216
  try {
7174
- const config = fs21.readJsonSync(RC_PATH);
7217
+ const config = fs22.readJsonSync(RC_PATH);
7175
7218
  return config.enabled === true;
7176
7219
  } catch {
7177
7220
  return false;
@@ -7180,16 +7223,16 @@ function isTelemetryEnabled() {
7180
7223
  function enableTelemetry() {
7181
7224
  const config = loadOrCreateConfig();
7182
7225
  config.enabled = true;
7183
- fs21.writeJsonSync(RC_PATH, config, { spaces: 2 });
7226
+ fs22.writeJsonSync(RC_PATH, config, { spaces: 2 });
7184
7227
  }
7185
7228
  function disableTelemetry() {
7186
7229
  const config = loadOrCreateConfig();
7187
7230
  config.enabled = false;
7188
- fs21.writeJsonSync(RC_PATH, config, { spaces: 2 });
7231
+ fs22.writeJsonSync(RC_PATH, config, { spaces: 2 });
7189
7232
  }
7190
7233
  function loadOrCreateConfig() {
7191
7234
  try {
7192
- return fs21.readJsonSync(RC_PATH);
7235
+ return fs22.readJsonSync(RC_PATH);
7193
7236
  } catch {
7194
7237
  return { enabled: false, anonymousId: crypto.randomUUID() };
7195
7238
  }
@@ -7209,13 +7252,13 @@ function trackEvent(event, properties) {
7209
7252
  },
7210
7253
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
7211
7254
  };
7212
- const logPath = path21.join(os2.homedir(), ".mars-telemetry.log");
7213
- fs21.appendFile(logPath, JSON.stringify(payload) + "\n").catch(() => {
7255
+ const logPath = path22.join(os2.homedir(), ".mars-telemetry.log");
7256
+ fs22.appendFile(logPath, JSON.stringify(payload) + "\n").catch(() => {
7214
7257
  });
7215
7258
  }
7216
7259
 
7217
7260
  // src/commands/create.ts
7218
- var RC_PATH2 = path22.join(os3.homedir(), ".marsrc");
7261
+ var RC_PATH2 = path23.join(os3.homedir(), ".marsrc");
7219
7262
  var PROJECT_NAME_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
7220
7263
  var MAX_PROJECT_NAME_LENGTH = 214;
7221
7264
  async function createCommand(projectName, options) {
@@ -7242,8 +7285,8 @@ async function createCommand(projectName, options) {
7242
7285
  const projectInfo = useDefaults ? getDefaultProjectInfo(projectName) : await promptProjectInfo(projectName);
7243
7286
  if (!projectInfo) return;
7244
7287
  const targetDir = resolveProjectPath(projectInfo.name);
7245
- if (await fs22.pathExists(targetDir)) {
7246
- const entries = await fs22.readdir(targetDir);
7288
+ if (await fs23.pathExists(targetDir)) {
7289
+ const entries = await fs23.readdir(targetDir);
7247
7290
  if (entries.length > 0) {
7248
7291
  log.error(`Directory "${projectInfo.name}" already exists and is not empty.`);
7249
7292
  return;
@@ -7284,7 +7327,7 @@ async function createCommand(projectName, options) {
7284
7327
  log.info("Scaffolding cancelled.");
7285
7328
  process.exit(0);
7286
7329
  }
7287
- if (!fs22.pathExistsSync(RC_PATH2)) {
7330
+ if (!fs23.pathExistsSync(RC_PATH2)) {
7288
7331
  const { telemetryOptIn } = await prompts5(
7289
7332
  {
7290
7333
  type: "confirm",
@@ -7323,10 +7366,10 @@ async function createCommand(projectName, options) {
7323
7366
  } catch (err) {
7324
7367
  spinner.fail("Failed to scaffold project");
7325
7368
  log.error(err instanceof Error ? err.message : String(err));
7326
- if (await fs22.pathExists(targetDir)) {
7369
+ if (await fs23.pathExists(targetDir)) {
7327
7370
  const cleanupSpinner = ora("Cleaning up...").start();
7328
7371
  try {
7329
- await fs22.remove(targetDir);
7372
+ await fs23.remove(targetDir);
7330
7373
  cleanupSpinner.succeed("Cleaned up partial scaffold");
7331
7374
  } catch {
7332
7375
  cleanupSpinner.warn(`Could not clean up ${targetDir}. You may need to remove it manually.`);
@@ -7341,8 +7384,8 @@ function countEnabled(flags) {
7341
7384
 
7342
7385
  // src/commands/doctor.ts
7343
7386
  init_logger();
7344
- import fs23 from "fs-extra";
7345
- import path23 from "path";
7387
+ import fs24 from "fs-extra";
7388
+ import path24 from "path";
7346
7389
  import { execSync } from "child_process";
7347
7390
  function commandExists(cmd) {
7348
7391
  try {
@@ -7362,12 +7405,12 @@ function getVersion(cmd) {
7362
7405
  async function checkForUpgrades() {
7363
7406
  log.blank();
7364
7407
  log.title("Upgrade Check");
7365
- const packageJsonPath = path23.join(process.cwd(), "package.json");
7366
- if (!fs23.pathExistsSync(packageJsonPath)) {
7408
+ const packageJsonPath = path24.join(process.cwd(), "package.json");
7409
+ if (!fs24.pathExistsSync(packageJsonPath)) {
7367
7410
  log.warn("No package.json found \u2014 skipping upgrade check.");
7368
7411
  return;
7369
7412
  }
7370
- const packageJson = fs23.readJsonSync(packageJsonPath);
7413
+ const packageJson = fs24.readJsonSync(packageJsonPath);
7371
7414
  const deps = packageJson.dependencies ?? {};
7372
7415
  const devDeps = packageJson.devDependencies ?? {};
7373
7416
  const currentRaw = deps["@mars-stack/core"] ?? devDeps["@mars-stack/core"];
@@ -7429,25 +7472,25 @@ async function doctorCommand(options) {
7429
7472
  },
7430
7473
  {
7431
7474
  name: "package.json exists",
7432
- check: () => fs23.pathExistsSync(path23.join(process.cwd(), "package.json"))
7475
+ check: () => fs24.pathExistsSync(path24.join(process.cwd(), "package.json"))
7433
7476
  },
7434
7477
  {
7435
7478
  name: ".env file exists",
7436
- check: () => fs23.pathExistsSync(path23.join(process.cwd(), ".env"))
7479
+ check: () => fs24.pathExistsSync(path24.join(process.cwd(), ".env"))
7437
7480
  },
7438
7481
  {
7439
7482
  name: "Prisma schema exists",
7440
- check: () => fs23.pathExistsSync(path23.join(process.cwd(), "prisma", "schema")) || fs23.pathExistsSync(path23.join(process.cwd(), "prisma", "schema.prisma"))
7483
+ check: () => fs24.pathExistsSync(path24.join(process.cwd(), "prisma", "schema")) || fs24.pathExistsSync(path24.join(process.cwd(), "prisma", "schema.prisma"))
7441
7484
  },
7442
7485
  {
7443
7486
  name: "node_modules installed",
7444
- check: () => fs23.pathExistsSync(path23.join(process.cwd(), "node_modules"))
7487
+ check: () => fs24.pathExistsSync(path24.join(process.cwd(), "node_modules"))
7445
7488
  },
7446
7489
  {
7447
7490
  name: "DATABASE_URL set",
7448
7491
  check: () => {
7449
7492
  try {
7450
- const envContent = fs23.readFileSync(path23.join(process.cwd(), ".env"), "utf-8");
7493
+ const envContent = fs24.readFileSync(path24.join(process.cwd(), ".env"), "utf-8");
7451
7494
  const match = envContent.match(/^DATABASE_URL=(.+)$/m);
7452
7495
  return match ? match[1] !== "" : false;
7453
7496
  } catch {
@@ -7459,7 +7502,7 @@ async function doctorCommand(options) {
7459
7502
  name: "JWT_SECRET set",
7460
7503
  check: () => {
7461
7504
  try {
7462
- const envContent = fs23.readFileSync(path23.join(process.cwd(), ".env"), "utf-8");
7505
+ const envContent = fs24.readFileSync(path24.join(process.cwd(), ".env"), "utf-8");
7463
7506
  const match = envContent.match(/^JWT_SECRET=(.+)$/m);
7464
7507
  if (!match || !match[1]) return false;
7465
7508
  return match[1].length >= 32 ? true : "set but too short (need >=32 chars)";
@@ -7504,16 +7547,16 @@ async function doctorCommand(options) {
7504
7547
 
7505
7548
  // src/commands/add.ts
7506
7549
  init_logger();
7507
- import fs25 from "fs-extra";
7508
- import path25 from "path";
7550
+ import fs26 from "fs-extra";
7551
+ import path26 from "path";
7509
7552
  import pc3 from "picocolors";
7510
7553
 
7511
7554
  // src/utils/doc-updater.ts
7512
- import fs24 from "fs-extra";
7513
- import path24 from "path";
7555
+ import fs25 from "fs-extra";
7556
+ import path25 from "path";
7514
7557
  async function appendToDbSchema(projectDir, modelName, fields) {
7515
- const filePath = path24.join(projectDir, "docs", "generated", "db-schema.md");
7516
- if (!await fs24.pathExists(filePath)) return;
7558
+ const filePath = path25.join(projectDir, "docs", "generated", "db-schema.md");
7559
+ if (!await fs25.pathExists(filePath)) return;
7517
7560
  const entry = [
7518
7561
  "",
7519
7562
  `### ${modelName}`,
@@ -7523,12 +7566,12 @@ async function appendToDbSchema(projectDir, modelName, fields) {
7523
7566
  ...fields.map((f) => `| ${f} | |`),
7524
7567
  ""
7525
7568
  ].join("\n");
7526
- await fs24.appendFile(filePath, entry);
7569
+ await fs25.appendFile(filePath, entry);
7527
7570
  }
7528
7571
  async function updateQualityScore(projectDir, domain, grade) {
7529
- const filePath = path24.join(projectDir, "docs", "QUALITY_SCORE.md");
7530
- if (!await fs24.pathExists(filePath)) return;
7531
- const content = await fs24.readFile(filePath, "utf-8");
7572
+ const filePath = path25.join(projectDir, "docs", "QUALITY_SCORE.md");
7573
+ if (!await fs25.pathExists(filePath)) return;
7574
+ const content = await fs25.readFile(filePath, "utf-8");
7532
7575
  const pattern = new RegExp(`^\\|\\s*${escapeRegex(domain)}\\s*\\|`, "m");
7533
7576
  if (pattern.test(content)) {
7534
7577
  const updated = content.replace(pattern, (match) => {
@@ -7536,11 +7579,11 @@ async function updateQualityScore(projectDir, domain, grade) {
7536
7579
  parts[2] = ` ${grade} `;
7537
7580
  return parts.join("|");
7538
7581
  });
7539
- await fs24.writeFile(filePath, updated);
7582
+ await fs25.writeFile(filePath, updated);
7540
7583
  } else {
7541
7584
  const row = `| ${domain} | ${grade} | |
7542
7585
  `;
7543
- await fs24.appendFile(filePath, row);
7586
+ await fs25.appendFile(filePath, row);
7544
7587
  }
7545
7588
  }
7546
7589
  function escapeRegex(str) {
@@ -7551,8 +7594,8 @@ function escapeRegex(str) {
7551
7594
  init_rollback();
7552
7595
  function ensureInProject() {
7553
7596
  const cwd = process.cwd();
7554
- const configPath = path25.join(cwd, "src", "config", "app.config.ts");
7555
- if (!fs25.pathExistsSync(configPath)) {
7597
+ const configPath = path26.join(cwd, "src", "config", "app.config.ts");
7598
+ if (!fs26.pathExistsSync(configPath)) {
7556
7599
  log.error("Not inside a MARS project. Run this command from the project root.");
7557
7600
  process.exit(1);
7558
7601
  }
@@ -7578,15 +7621,98 @@ function pluralize(word) {
7578
7621
  return word + "s";
7579
7622
  }
7580
7623
  function stripBrackets(segment) {
7581
- return segment.replace(/[\[\]]/g, "");
7624
+ return segment.replace(/[\[\]\.]/g, "");
7625
+ }
7626
+ function parseDynamicParams(routePath) {
7627
+ const segments = routePath.split("/").filter(Boolean);
7628
+ const params = [];
7629
+ for (const segment of segments) {
7630
+ if (/^\[/.test(segment)) {
7631
+ const catchAll = /^\[\.\.\./.test(segment);
7632
+ const name = segment.replace(/^\[(?:\.\.\.)?/, "").replace(/\]$/, "");
7633
+ params.push({ name, catchAll });
7634
+ }
7635
+ }
7636
+ return params;
7637
+ }
7638
+ function buildPageContent(pageName, dynamicParams) {
7639
+ const hasParams = dynamicParams.length > 0;
7640
+ if (!hasParams) {
7641
+ return `import { Card, CardHeader, CardBody } from '@mars-stack/ui';
7642
+ import type { Metadata } from 'next';
7643
+
7644
+ export const metadata: Metadata = {
7645
+ title: '${pageName}',
7646
+ };
7647
+
7648
+ export default function ${pageName}Page() {
7649
+ return (
7650
+ <div className="mx-auto max-w-4xl space-y-6 p-6">
7651
+ <div>
7652
+ <h1 className="text-4xl font-bold text-text-primary mb-4">${pageName}</h1>
7653
+ <p className="text-base text-text-secondary mb-4">
7654
+ Description goes here.
7655
+ </p>
7656
+ </div>
7657
+
7658
+ <Card>
7659
+ <CardHeader>
7660
+ <h2 className="text-lg font-semibold text-text-primary">${pageName}</h2>
7661
+ </CardHeader>
7662
+ <CardBody>
7663
+ {/* Content */}
7664
+ </CardBody>
7665
+ </Card>
7666
+ </div>
7667
+ );
7668
+ }
7669
+ `;
7670
+ }
7671
+ const paramFields = dynamicParams.map((p) => `${p.name}: ${p.catchAll ? "string[]" : "string"}`).join("; ");
7672
+ const destructuredNames = dynamicParams.map((p) => p.name).join(", ");
7673
+ return `import { Card, CardHeader, CardBody } from '@mars-stack/ui';
7674
+ import type { Metadata } from 'next';
7675
+
7676
+ export const metadata: Metadata = {
7677
+ title: '${pageName}',
7678
+ };
7679
+
7680
+ interface Props {
7681
+ params: Promise<{ ${paramFields} }>;
7682
+ }
7683
+
7684
+ export default async function ${pageName}Page({ params }: Props) {
7685
+ const { ${destructuredNames} } = await params;
7686
+
7687
+ return (
7688
+ <div className="mx-auto max-w-4xl space-y-6 p-6">
7689
+ <div>
7690
+ <h1 className="text-4xl font-bold text-text-primary mb-4">{${dynamicParams[0].name}}</h1>
7691
+ <p className="text-base text-text-secondary mb-4">
7692
+ Description goes here.
7693
+ </p>
7694
+ </div>
7695
+
7696
+ <Card>
7697
+ <CardHeader>
7698
+ <h2 className="text-lg font-semibold text-text-primary">${pageName}</h2>
7699
+ </CardHeader>
7700
+ <CardBody>
7701
+ {/* Content */}
7702
+ </CardBody>
7703
+ </Card>
7704
+ </div>
7705
+ );
7706
+ }
7707
+ `;
7582
7708
  }
7583
7709
  async function addFeatureCommand(name) {
7584
7710
  const root = ensureInProject();
7585
7711
  const kebab = toKebab(name);
7586
7712
  const pascal = toPascal(name);
7587
7713
  const camel = toCamel(name);
7588
- const featureDir = path25.join(root, "src", "features", kebab);
7589
- if (await fs25.pathExists(featureDir)) {
7714
+ const featureDir = path26.join(root, "src", "features", kebab);
7715
+ if (await fs26.pathExists(featureDir)) {
7590
7716
  log.error(`Feature "${kebab}" already exists at src/features/${kebab}/`);
7591
7717
  return;
7592
7718
  }
@@ -7651,9 +7777,9 @@ export type Update${pascal}Input = z.infer<typeof ${camel}Schemas.update>;
7651
7777
  ctx.trackCreatedFile(featureDir);
7652
7778
  let count = 0;
7653
7779
  for (const [filePath, content] of Object.entries(files)) {
7654
- const fullPath = path25.join(featureDir, filePath);
7655
- await fs25.ensureDir(path25.dirname(fullPath));
7656
- await fs25.writeFile(fullPath, content);
7780
+ const fullPath = path26.join(featureDir, filePath);
7781
+ await fs26.ensureDir(path26.dirname(fullPath));
7782
+ await fs26.writeFile(fullPath, content);
7657
7783
  count++;
7658
7784
  }
7659
7785
  await ctx.commit();
@@ -7674,42 +7800,15 @@ async function addPageCommand(routePath, options) {
7674
7800
  const root = ensureInProject();
7675
7801
  const cleanPath = routePath.startsWith("/") ? routePath.slice(1) : routePath;
7676
7802
  const group = options.protected ? "(protected)" : "(public)";
7677
- const pageDir = path25.join(root, "src", "app", group, cleanPath);
7678
- if (await fs25.pathExists(path25.join(pageDir, "page.tsx"))) {
7803
+ const pageDir = path26.join(root, "src", "app", group, cleanPath);
7804
+ if (await fs26.pathExists(path26.join(pageDir, "page.tsx"))) {
7679
7805
  log.error(`Page already exists at src/app/${group}/${cleanPath}/page.tsx`);
7680
7806
  return;
7681
7807
  }
7682
7808
  const lastSegment = cleanPath.split("/").pop() || "page";
7683
7809
  const pageName = toPascal(stripBrackets(lastSegment));
7684
- const pageContent = `import { Card, CardHeader, CardBody } from '@mars-stack/ui';
7685
- import type { Metadata } from 'next';
7686
-
7687
- export const metadata: Metadata = {
7688
- title: '${pageName}',
7689
- };
7690
-
7691
- export default function ${pageName}Page() {
7692
- return (
7693
- <div className="mx-auto max-w-4xl space-y-6 p-6">
7694
- <div>
7695
- <h1 className="text-4xl font-bold text-text-primary mb-4">${pageName}</h1>
7696
- <p className="text-base text-text-secondary mb-4">
7697
- Description goes here.
7698
- </p>
7699
- </div>
7700
-
7701
- <Card>
7702
- <CardHeader>
7703
- <h2 className="text-lg font-semibold text-text-primary">${pageName}</h2>
7704
- </CardHeader>
7705
- <CardBody>
7706
- {/* Content */}
7707
- </CardBody>
7708
- </Card>
7709
- </div>
7710
- );
7711
- }
7712
- `;
7810
+ const dynamicParams = parseDynamicParams(cleanPath);
7811
+ const pageContent = buildPageContent(pageName, dynamicParams);
7713
7812
  const loadingContent = `import { Spinner } from '@mars-stack/ui';
7714
7813
 
7715
7814
  export default function Loading() {
@@ -7723,9 +7822,9 @@ export default function Loading() {
7723
7822
  const ctx = createRollbackContext();
7724
7823
  try {
7725
7824
  ctx.trackCreatedFile(pageDir);
7726
- await fs25.ensureDir(pageDir);
7727
- await fs25.writeFile(path25.join(pageDir, "page.tsx"), pageContent);
7728
- await fs25.writeFile(path25.join(pageDir, "loading.tsx"), loadingContent);
7825
+ await fs26.ensureDir(pageDir);
7826
+ await fs26.writeFile(path26.join(pageDir, "page.tsx"), pageContent);
7827
+ await fs26.writeFile(path26.join(pageDir, "loading.tsx"), loadingContent);
7729
7828
  await ctx.commit();
7730
7829
  log.success(`Created page at ${pc3.bold(`src/app/${group}/${cleanPath}/`)}`);
7731
7830
  trackEvent("add", { type: "page" });
@@ -7742,9 +7841,9 @@ async function addModelCommand(name) {
7742
7841
  const pascal = toPascal(name);
7743
7842
  const camel = toCamel(name);
7744
7843
  const kebab = toKebab(name);
7745
- const schemaDir = path25.join(root, "prisma", "schema");
7746
- const schemaFile = path25.join(schemaDir, `${kebab}.prisma`);
7747
- if (await fs25.pathExists(schemaFile)) {
7844
+ const schemaDir = path26.join(root, "prisma", "schema");
7845
+ const schemaFile = path26.join(schemaDir, `${kebab}.prisma`);
7846
+ if (await fs26.pathExists(schemaFile)) {
7748
7847
  log.error(`Schema file already exists: prisma/schema/${kebab}.prisma`);
7749
7848
  return;
7750
7849
  }
@@ -7762,8 +7861,8 @@ async function addModelCommand(name) {
7762
7861
  const ctx = createRollbackContext();
7763
7862
  try {
7764
7863
  ctx.trackCreatedFile(schemaFile);
7765
- await fs25.ensureDir(schemaDir);
7766
- await fs25.writeFile(schemaFile, content);
7864
+ await fs26.ensureDir(schemaDir);
7865
+ await fs26.writeFile(schemaFile, content);
7767
7866
  await ctx.commit();
7768
7867
  log.success(`Created model ${pc3.bold(pascal)} at prisma/schema/${kebab}.prisma`);
7769
7868
  trackEvent("add", { type: "model" });
@@ -7785,9 +7884,9 @@ async function addEmailCommand(name) {
7785
7884
  const kebab = toKebab(name);
7786
7885
  const pascal = toPascal(name);
7787
7886
  const camel = toCamel(name);
7788
- const templatesDir = path25.join(root, "src", "lib", "core", "email", "templates");
7789
- const filePath = path25.join(templatesDir, `${kebab}-email.ts`);
7790
- if (await fs25.pathExists(filePath)) {
7887
+ const templatesDir = path26.join(root, "src", "lib", "core", "email", "templates");
7888
+ const filePath = path26.join(templatesDir, `${kebab}-email.ts`);
7889
+ if (await fs26.pathExists(filePath)) {
7791
7890
  log.error(`Email template already exists: src/lib/core/email/templates/${kebab}-email.ts`);
7792
7891
  return;
7793
7892
  }
@@ -7831,25 +7930,25 @@ export function ${functionName}({ appName, actionUrl, userName }: ${pascal}Email
7831
7930
  }
7832
7931
  `;
7833
7932
  const ctx = createRollbackContext();
7834
- const indexPath = path25.join(templatesDir, "index.ts");
7933
+ const indexPath = path26.join(templatesDir, "index.ts");
7835
7934
  try {
7836
7935
  ctx.trackCreatedFile(filePath);
7837
- if (await fs25.pathExists(indexPath)) {
7936
+ if (await fs26.pathExists(indexPath)) {
7838
7937
  await ctx.trackModifiedFile(indexPath);
7839
7938
  } else {
7840
7939
  ctx.trackCreatedFile(indexPath);
7841
7940
  }
7842
- await fs25.ensureDir(templatesDir);
7843
- await fs25.writeFile(filePath, content);
7941
+ await fs26.ensureDir(templatesDir);
7942
+ await fs26.writeFile(filePath, content);
7844
7943
  const exportLine = `export { ${functionName} } from './${kebab}-email';
7845
7944
  `;
7846
- if (await fs25.pathExists(indexPath)) {
7847
- const existing = await fs25.readFile(indexPath, "utf-8");
7945
+ if (await fs26.pathExists(indexPath)) {
7946
+ const existing = await fs26.readFile(indexPath, "utf-8");
7848
7947
  if (!existing.includes(functionName)) {
7849
- await fs25.appendFile(indexPath, exportLine);
7948
+ await fs26.appendFile(indexPath, exportLine);
7850
7949
  }
7851
7950
  } else {
7852
- await fs25.writeFile(indexPath, exportLine);
7951
+ await fs26.writeFile(indexPath, exportLine);
7853
7952
  }
7854
7953
  await ctx.commit();
7855
7954
  log.success(`Created email template ${pc3.bold(kebab)} at src/lib/core/email/templates/${kebab}-email.ts`);
@@ -7872,10 +7971,10 @@ async function addComponentCommand(name, options) {
7872
7971
  log.error(`Invalid type "${type}". Use: ${validTypes.join(", ")}`);
7873
7972
  return;
7874
7973
  }
7875
- const dir = type === "primitive" ? path25.join(root, "src", "components", "primitives") : path25.join(root, "src", "components", "patterns");
7876
- await fs25.ensureDir(dir);
7877
- const filePath = path25.join(dir, `${pascal}.tsx`);
7878
- if (await fs25.pathExists(filePath)) {
7974
+ const dir = type === "primitive" ? path26.join(root, "src", "components", "primitives") : path26.join(root, "src", "components", "patterns");
7975
+ await fs26.ensureDir(dir);
7976
+ const filePath = path26.join(dir, `${pascal}.tsx`);
7977
+ if (await fs26.pathExists(filePath)) {
7879
7978
  log.error(`Component already exists: ${pascal}.tsx`);
7880
7979
  return;
7881
7980
  }
@@ -7924,9 +8023,9 @@ export function ${pascal}({ children, className }: ${pascal}Props) {
7924
8023
  const ctx = createRollbackContext();
7925
8024
  try {
7926
8025
  ctx.trackCreatedFile(filePath);
7927
- await fs25.writeFile(filePath, content);
8026
+ await fs26.writeFile(filePath, content);
7928
8027
  await ctx.commit();
7929
- log.success(`Created ${type} component ${pc3.bold(pascal)} at ${path25.relative(root, filePath)}`);
8028
+ log.success(`Created ${type} component ${pc3.bold(pascal)} at ${path26.relative(root, filePath)}`);
7930
8029
  trackEvent("add", { type: "component", componentType: type });
7931
8030
  log.blank();
7932
8031
  log.warn(`Remember to add the export to the barrel file:`);
@@ -7941,14 +8040,14 @@ export function ${pascal}({ children, className }: ${pascal}Props) {
7941
8040
  // src/commands/configure.ts
7942
8041
  init_logger();
7943
8042
  import { execSync as execSync2 } from "child_process";
7944
- import fs26 from "fs-extra";
7945
- import path26 from "path";
8043
+ import fs27 from "fs-extra";
8044
+ import path27 from "path";
7946
8045
  import pc4 from "picocolors";
7947
8046
  import prompts6 from "prompts";
7948
8047
  function ensureInProject2() {
7949
8048
  const cwd = process.cwd();
7950
- const configPath = path26.join(cwd, "src", "config", "app.config.ts");
7951
- if (!fs26.pathExistsSync(configPath)) {
8049
+ const configPath = path27.join(cwd, "src", "config", "app.config.ts");
8050
+ if (!fs27.pathExistsSync(configPath)) {
7952
8051
  log.error("Not inside a MARS project. Run this command from the project root.");
7953
8052
  process.exit(1);
7954
8053
  }
@@ -7962,8 +8061,8 @@ var PROVIDER_DEPENDENCIES = {
7962
8061
  "storage:s3": ["@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner"]
7963
8062
  };
7964
8063
  function updateAppConfig(projectDir, serviceKey, provider, featureKey) {
7965
- const configPath = path26.join(projectDir, "src", "config", "app.config.ts");
7966
- let content = fs26.readFileSync(configPath, "utf-8");
8064
+ const configPath = path27.join(projectDir, "src", "config", "app.config.ts");
8065
+ let content = fs27.readFileSync(configPath, "utf-8");
7967
8066
  if (serviceKey === "auth") {
7968
8067
  const boolValue = provider === "google" ? "true" : "false";
7969
8068
  const featureRegex = new RegExp(`(googleOAuth\\s*:\\s*)(?:true|false)`);
@@ -7978,10 +8077,10 @@ function updateAppConfig(projectDir, serviceKey, provider, featureKey) {
7978
8077
  content = content.replace(featureRegex, `$1true`);
7979
8078
  }
7980
8079
  }
7981
- fs26.writeFileSync(configPath, content);
8080
+ fs27.writeFileSync(configPath, content);
7982
8081
  }
7983
8082
  function detectPackageManager(projectDir) {
7984
- if (fs26.existsSync(path26.join(projectDir, "yarn.lock"))) return "yarn";
8083
+ if (fs27.existsSync(path27.join(projectDir, "yarn.lock"))) return "yarn";
7985
8084
  return "npm";
7986
8085
  }
7987
8086
  function installDependencies(projectDir, deps) {
@@ -8091,15 +8190,15 @@ async function configureCommand(service) {
8091
8190
  log.step(`Manually set ${pc4.bold(`services.${serviceConfig.configKey}.provider`)} to ${pc4.bold(`'${provider}'`)} in ${pc4.dim("src/config/app.config.ts")}`);
8092
8191
  }
8093
8192
  if (selectedProvider.envVars.length > 0) {
8094
- const envPath = path26.join(root, ".env");
8095
- if (await fs26.pathExists(envPath)) {
8096
- const envContent = await fs26.readFile(envPath, "utf-8");
8193
+ const envPath = path27.join(root, ".env");
8194
+ if (await fs27.pathExists(envPath)) {
8195
+ const envContent = await fs27.readFile(envPath, "utf-8");
8097
8196
  const missingVars = selectedProvider.envVars.filter(
8098
8197
  (v) => !envContent.includes(v)
8099
8198
  );
8100
8199
  if (missingVars.length > 0) {
8101
8200
  const additions = missingVars.map((v) => `${v}=""`).join("\n");
8102
- await fs26.appendFile(envPath, `
8201
+ await fs27.appendFile(envPath, `
8103
8202
  # ${selectedService} (${provider})
8104
8203
  ${additions}
8105
8204
  `);
@@ -8128,15 +8227,15 @@ ${additions}
8128
8227
 
8129
8228
  // src/commands/deploy.ts
8130
8229
  init_logger();
8131
- import fs27 from "fs-extra";
8132
- import path27 from "path";
8230
+ import fs28 from "fs-extra";
8231
+ import path28 from "path";
8133
8232
  import { execSync as execSync3 } from "child_process";
8134
8233
  import pc5 from "picocolors";
8135
8234
  import prompts7 from "prompts";
8136
8235
  function ensureInProject3() {
8137
8236
  const cwd = process.cwd();
8138
- const configPath = path27.join(cwd, "src", "config", "app.config.ts");
8139
- if (!fs27.pathExistsSync(configPath)) {
8237
+ const configPath = path28.join(cwd, "src", "config", "app.config.ts");
8238
+ if (!fs28.pathExistsSync(configPath)) {
8140
8239
  log.error("Not inside a MARS project. Run this command from the project root.");
8141
8240
  process.exit(1);
8142
8241
  }
@@ -8175,8 +8274,8 @@ async function deployCommand() {
8175
8274
  return;
8176
8275
  }
8177
8276
  }
8178
- const vercelDir = path27.join(root, ".vercel");
8179
- if (!fs27.existsSync(vercelDir)) {
8277
+ const vercelDir = path28.join(root, ".vercel");
8278
+ if (!fs28.existsSync(vercelDir)) {
8180
8279
  log.step("Linking project to Vercel...");
8181
8280
  try {
8182
8281
  execSync3("vercel link", { cwd: root, stdio: "inherit" });
@@ -8193,8 +8292,8 @@ async function deployCommand() {
8193
8292
  log.blank();
8194
8293
  const requiredVars = ["JWT_SECRET", "DATABASE_URL"];
8195
8294
  log.step(`Core: ${pc5.dim(requiredVars.join(", "))}`);
8196
- const configPath = path27.join(root, "src", "config", "app.config.ts");
8197
- const configContent = await fs27.readFile(configPath, "utf-8");
8295
+ const configPath = path28.join(root, "src", "config", "app.config.ts");
8296
+ const configContent = await fs28.readFile(configPath, "utf-8");
8198
8297
  if (configContent.includes("email: { provider: 'sendgrid'")) {
8199
8298
  log.step(`Email (SendGrid): ${pc5.dim("SENDGRID_API_KEY, SENDGRID_FROM_EMAIL")}`);
8200
8299
  } else if (configContent.includes("email: { provider: 'resend'")) {
@@ -8255,8 +8354,8 @@ async function deployCommand() {
8255
8354
 
8256
8355
  // src/commands/upgrade.ts
8257
8356
  init_logger();
8258
- import fs28 from "fs-extra";
8259
- import path28 from "path";
8357
+ import fs29 from "fs-extra";
8358
+ import path29 from "path";
8260
8359
  import { execSync as execSync4 } from "child_process";
8261
8360
  var MARS_PACKAGES = ["@mars-stack/core", "@mars-stack/ui"];
8262
8361
  var CHANGELOG_URL = "https://github.com/greaveselliott/mars/blob/main/CHANGELOG.md";
@@ -8279,7 +8378,7 @@ function readCurrentVersion(packageJson, packageName) {
8279
8378
  return version ? version.replace(/^[\^~]/, "") : null;
8280
8379
  }
8281
8380
  function detectPackageManager2(projectDir) {
8282
- if (fs28.pathExistsSync(path28.join(projectDir, "yarn.lock"))) {
8381
+ if (fs29.pathExistsSync(path29.join(projectDir, "yarn.lock"))) {
8283
8382
  return "yarn";
8284
8383
  }
8285
8384
  return "npm";
@@ -8343,101 +8442,104 @@ function updatePackageJsonVersions(packageJsonPath, packageJson, versions) {
8343
8442
  if (updated.length > 0) {
8344
8443
  packageJson.dependencies = deps;
8345
8444
  packageJson.devDependencies = devDeps;
8346
- fs28.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
8445
+ fs29.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
8347
8446
  }
8348
8447
  return updated;
8349
8448
  }
8350
- function upgradeCommand(program2) {
8351
- program2.command("upgrade").description(
8352
- "Upgrade @mars-stack/core, @mars-stack/ui, and @mars-stack/cli to the latest versions"
8353
- ).option("--dry-run", "Show what would be updated without making changes").action(async (options) => {
8354
- log.title("MARS Upgrade");
8355
- const projectDir = process.cwd();
8356
- const packageJsonPath = path28.join(projectDir, "package.json");
8357
- if (!fs28.pathExistsSync(packageJsonPath)) {
8358
- log.error(
8359
- "No package.json found. Are you in a Mars project directory?"
8360
- );
8361
- process.exitCode = 1;
8362
- return;
8363
- }
8364
- const packageJson = fs28.readJsonSync(packageJsonPath);
8365
- const hasMarsPackage = MARS_PACKAGES.some(
8366
- (name) => readCurrentVersion(packageJson, name) !== null
8449
+ async function runUpgrade(options) {
8450
+ log.title("MARS Upgrade");
8451
+ const projectDir = process.cwd();
8452
+ const packageJsonPath = path29.join(projectDir, "package.json");
8453
+ if (!fs29.pathExistsSync(packageJsonPath)) {
8454
+ log.error(
8455
+ "No package.json found. Are you in a Mars project directory?"
8367
8456
  );
8368
- if (!hasMarsPackage) {
8369
- log.error(
8370
- "No @mars-stack packages found in dependencies. This does not appear to be a Mars project."
8371
- );
8372
- process.exitCode = 1;
8373
- return;
8374
- }
8375
- log.step("Checking for updates...");
8376
- const versions = await gatherVersionInfo(packageJson);
8377
- const registryErrors = versions.filter(
8378
- (v) => v.current !== null && v.latest === null
8457
+ process.exitCode = 1;
8458
+ return;
8459
+ }
8460
+ const packageJson = fs29.readJsonSync(packageJsonPath);
8461
+ const hasMarsPackage = MARS_PACKAGES.some(
8462
+ (name) => readCurrentVersion(packageJson, name) !== null
8463
+ );
8464
+ if (!hasMarsPackage) {
8465
+ log.error(
8466
+ "No @mars-stack packages found in dependencies. This does not appear to be a Mars project."
8379
8467
  );
8380
- if (registryErrors.length > 0) {
8381
- log.warn(
8382
- "Could not reach the npm registry for some packages. Check your network connection."
8383
- );
8384
- }
8385
- printVersionTable(versions);
8386
- const updatesAvailable = versions.some((v) => v.updateAvailable);
8387
- if (!updatesAvailable) {
8388
- log.success("All Mars packages are up to date.");
8389
- return;
8390
- }
8391
- if (options.dryRun) {
8392
- log.info("Dry run \u2014 no changes were made.");
8393
- log.info(`Run ${`mars upgrade`} without --dry-run to apply updates.`);
8394
- return;
8395
- }
8396
- log.step("Updating package.json...");
8397
- const updated = updatePackageJsonVersions(
8398
- packageJsonPath,
8399
- packageJson,
8400
- versions
8468
+ process.exitCode = 1;
8469
+ return;
8470
+ }
8471
+ log.step("Checking for updates...");
8472
+ const versions = await gatherVersionInfo(packageJson);
8473
+ const registryErrors = versions.filter(
8474
+ (v) => v.current !== null && v.latest === null
8475
+ );
8476
+ if (registryErrors.length > 0) {
8477
+ log.warn(
8478
+ "Could not reach the npm registry for some packages. Check your network connection."
8401
8479
  );
8402
- for (const change of updated) {
8403
- log.success(change);
8404
- }
8405
- const pm = detectPackageManager2(projectDir);
8406
- const installCmd = getInstallCommand(pm);
8407
- log.blank();
8408
- log.step(`Running ${installCmd}...`);
8409
- try {
8410
- execSync4(installCmd, {
8411
- cwd: projectDir,
8412
- stdio: "inherit"
8413
- });
8414
- } catch {
8415
- log.error(`${installCmd} failed. You may need to run it manually.`);
8416
- process.exitCode = 1;
8417
- return;
8418
- }
8419
- log.blank();
8420
- log.step("Running mars doctor to verify...");
8421
- try {
8422
- execSync4("npx mars doctor", {
8423
- cwd: projectDir,
8424
- stdio: "inherit"
8425
- });
8426
- } catch {
8427
- log.warn(
8428
- "mars doctor reported issues. Review the output above."
8429
- );
8430
- }
8431
- log.blank();
8432
- log.title("Upgrade Summary");
8433
- for (const change of updated) {
8434
- log.success(change);
8435
- }
8436
- log.blank();
8437
- log.info(`Changelog: ${CHANGELOG_URL}`);
8438
- log.info(
8439
- "Review the changelog for any breaking changes or migration notes."
8480
+ }
8481
+ printVersionTable(versions);
8482
+ const updatesAvailable = versions.some((v) => v.updateAvailable);
8483
+ if (!updatesAvailable) {
8484
+ log.success("All Mars packages are up to date.");
8485
+ return;
8486
+ }
8487
+ if (options?.dryRun) {
8488
+ log.info("Dry run \u2014 no changes were made.");
8489
+ log.info(`Run ${`mars upgrade`} without --dry-run to apply updates.`);
8490
+ return;
8491
+ }
8492
+ log.step("Updating package.json...");
8493
+ const updated = updatePackageJsonVersions(
8494
+ packageJsonPath,
8495
+ packageJson,
8496
+ versions
8497
+ );
8498
+ for (const change of updated) {
8499
+ log.success(change);
8500
+ }
8501
+ const pm = detectPackageManager2(projectDir);
8502
+ const installCmd = getInstallCommand(pm);
8503
+ log.blank();
8504
+ log.step(`Running ${installCmd}...`);
8505
+ try {
8506
+ execSync4(installCmd, {
8507
+ cwd: projectDir,
8508
+ stdio: "inherit"
8509
+ });
8510
+ } catch {
8511
+ log.error(`${installCmd} failed. You may need to run it manually.`);
8512
+ process.exitCode = 1;
8513
+ return;
8514
+ }
8515
+ log.blank();
8516
+ log.step("Running mars doctor to verify...");
8517
+ try {
8518
+ execSync4("npx mars doctor", {
8519
+ cwd: projectDir,
8520
+ stdio: "inherit"
8521
+ });
8522
+ } catch {
8523
+ log.warn(
8524
+ "mars doctor reported issues. Review the output above."
8440
8525
  );
8526
+ }
8527
+ log.blank();
8528
+ log.title("Upgrade Summary");
8529
+ for (const change of updated) {
8530
+ log.success(change);
8531
+ }
8532
+ log.blank();
8533
+ log.info(`Changelog: ${CHANGELOG_URL}`);
8534
+ log.info(
8535
+ "Review the changelog for any breaking changes or migration notes."
8536
+ );
8537
+ }
8538
+ function upgradeCommand(program2) {
8539
+ program2.command("upgrade").description(
8540
+ "Upgrade @mars-stack/core, @mars-stack/ui, and @mars-stack/cli to the latest versions"
8541
+ ).option("--dry-run", "Show what would be updated without making changes").action(async (options) => {
8542
+ await runUpgrade(options);
8441
8543
  });
8442
8544
  }
8443
8545
 
@@ -8457,10 +8559,7 @@ init_sentry();
8457
8559
  init_feature_flags();
8458
8560
  init_logger();
8459
8561
  var program = new Command();
8460
- program.name("mars").description("MARS CLI: scaffold, configure, and maintain SaaS apps").version(getCliVersion()).option("-v, --verbose", "Enable verbose output for debugging");
8461
- function isVerbose() {
8462
- return program.opts().verbose === true;
8463
- }
8562
+ 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");
8464
8563
  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) => {
8465
8564
  await createCommand(name, options);
8466
8565
  });
@@ -8541,8 +8640,17 @@ program.command("telemetry").description("Manage anonymous usage telemetry").arg
8541
8640
  log.error("Usage: mars telemetry <enable|disable>");
8542
8641
  }
8543
8642
  });
8544
- program.parse();
8545
- export {
8546
- isVerbose
8547
- };
8643
+ program.hook("preAction", (thisCommand) => {
8644
+ const globalOpts = program.opts();
8645
+ if (globalOpts.upgrade && thisCommand !== program) {
8646
+ log.error('Cannot combine --upgrade with other commands. Use "mars -u" or "mars upgrade" alone.');
8647
+ process.exit(1);
8648
+ }
8649
+ });
8650
+ await program.parseAsync();
8651
+ var opts = program.opts();
8652
+ if (opts.upgrade && program.args.length === 0) {
8653
+ await runUpgrade();
8654
+ process.exit(0);
8655
+ }
8548
8656
  //# sourceMappingURL=index.js.map