@mars-stack/cli 1.0.2 → 2.0.1

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
@@ -45,7 +45,7 @@ var init_logger = __esm({
45
45
 
46
46
  // src/utils/rollback.ts
47
47
  import fs3 from "fs-extra";
48
- import path3 from "path";
48
+ import path4 from "path";
49
49
  import os from "os";
50
50
  function createRollbackContext() {
51
51
  const manifest = {
@@ -53,14 +53,14 @@ function createRollbackContext() {
53
53
  filesModified: [],
54
54
  depsInstalled: []
55
55
  };
56
- const backupDir = path3.join(os.tmpdir(), `mars-rollback-${Date.now()}`);
56
+ const backupDir = path4.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
61
  if (!await fs3.pathExists(filePath)) return;
62
62
  await fs3.ensureDir(backupDir);
63
- const backupPath = path3.join(backupDir, `${manifest.filesModified.length}-${path3.basename(filePath)}`);
63
+ const backupPath = path4.join(backupDir, `${manifest.filesModified.length}-${path4.basename(filePath)}`);
64
64
  await fs3.copy(filePath, backupPath);
65
65
  manifest.filesModified.push({ path: filePath, backup: backupPath });
66
66
  }
@@ -109,9 +109,9 @@ var init_rollback = __esm({
109
109
 
110
110
  // src/utils/dependencies.ts
111
111
  import fs4 from "fs-extra";
112
- import path4 from "path";
112
+ import path5 from "path";
113
113
  async function addDependencies(projectRoot, deps, dev = false) {
114
- const pkgPath = path4.join(projectRoot, "package.json");
114
+ const pkgPath = path5.join(projectRoot, "package.json");
115
115
  if (!await fs4.pathExists(pkgPath)) return;
116
116
  const pkg = await fs4.readJson(pkgPath);
117
117
  const key = dev ? "devDependencies" : "dependencies";
@@ -124,16 +124,40 @@ var init_dependencies = __esm({
124
124
  }
125
125
  });
126
126
 
127
+ // src/utils/routes.ts
128
+ import fs5 from "fs-extra";
129
+ import path6 from "path";
130
+ async function registerRoute(projectRoot, key, routePath, ctx) {
131
+ const routesFile = path6.join(projectRoot, "src", "config", "routes.ts");
132
+ if (!await fs5.pathExists(routesFile)) return;
133
+ let content = await fs5.readFile(routesFile, "utf-8");
134
+ if (content.includes(`${key}:`)) return;
135
+ if (ctx) {
136
+ await ctx.trackModifiedFile(routesFile);
137
+ }
138
+ content = content.replace(
139
+ /} as const;/,
140
+ ` ${key}: '${routePath}',
141
+ } as const;`
142
+ );
143
+ await fs5.writeFile(routesFile, content);
144
+ }
145
+ var init_routes = __esm({
146
+ "src/utils/routes.ts"() {
147
+ "use strict";
148
+ }
149
+ });
150
+
127
151
  // src/generators/features/blog.ts
128
152
  var blog_exports = {};
129
153
  __export(blog_exports, {
130
154
  generateBlog: () => generateBlog
131
155
  });
132
- import fs5 from "fs-extra";
133
- import path5 from "path";
156
+ import fs6 from "fs-extra";
157
+ import path7 from "path";
134
158
  async function generateBlog(projectRoot) {
135
- const featureDir = path5.join(projectRoot, "src", "features", "blog");
136
- if (await fs5.pathExists(featureDir)) {
159
+ const featureDir = path7.join(projectRoot, "src", "features", "blog");
160
+ if (await fs6.pathExists(featureDir)) {
137
161
  log.error("Blog feature already exists at src/features/blog/");
138
162
  return;
139
163
  }
@@ -154,10 +178,10 @@ async function generateBlog(projectRoot) {
154
178
  };
155
179
  let count = 0;
156
180
  for (const [filePath, content] of Object.entries(files)) {
157
- const fullPath = path5.join(projectRoot, filePath);
181
+ const fullPath = path7.join(projectRoot, filePath);
158
182
  ctx.trackCreatedFile(fullPath);
159
- await fs5.ensureDir(path5.dirname(fullPath));
160
- await fs5.writeFile(fullPath, content);
183
+ await fs6.ensureDir(path7.dirname(fullPath));
184
+ await fs6.writeFile(fullPath, content);
161
185
  count++;
162
186
  }
163
187
  await addDependencies(projectRoot, {
@@ -168,6 +192,7 @@ async function generateBlog(projectRoot) {
168
192
  "remark-gfm": "^4.0.0"
169
193
  });
170
194
  await addDependencies(projectRoot, { "@types/mdx": "^2.0.0" }, true);
195
+ await registerRoute(projectRoot, "blog", "/blog", ctx);
171
196
  await setConfigFlag(projectRoot, ctx);
172
197
  await ctx.commit();
173
198
  log.success(`Generated blog feature with ${count} files`);
@@ -183,12 +208,12 @@ async function generateBlog(projectRoot) {
183
208
  }
184
209
  }
185
210
  async function setConfigFlag(projectRoot, ctx) {
186
- const configPath = path5.join(projectRoot, "src", "config", "app.config.ts");
187
- if (!await fs5.pathExists(configPath)) return;
211
+ const configPath = path7.join(projectRoot, "src", "config", "app.config.ts");
212
+ if (!await fs6.pathExists(configPath)) return;
188
213
  await ctx.trackModifiedFile(configPath);
189
- const content = await fs5.readFile(configPath, "utf-8");
214
+ const content = await fs6.readFile(configPath, "utf-8");
190
215
  const updated = content.replace(/blog:\s*false/, "blog: true");
191
- await fs5.writeFile(configPath, updated);
216
+ await fs6.writeFile(configPath, updated);
192
217
  }
193
218
  function schemas() {
194
219
  return `${STAMP}
@@ -681,6 +706,7 @@ var init_blog = __esm({
681
706
  init_logger();
682
707
  init_rollback();
683
708
  init_dependencies();
709
+ init_routes();
684
710
  GENERATOR_VERSION = "0.1.0";
685
711
  STAMP = `// @mars-generated blog@${GENERATOR_VERSION}`;
686
712
  }
@@ -706,11 +732,11 @@ var dark_mode_exports = {};
706
732
  __export(dark_mode_exports, {
707
733
  generateDarkMode: () => generateDarkMode
708
734
  });
709
- import fs6 from "fs-extra";
710
- import path6 from "path";
735
+ import fs7 from "fs-extra";
736
+ import path8 from "path";
711
737
  async function generateDarkMode(projectRoot) {
712
- const featureDir = path6.join(projectRoot, "src", "features", "dark-mode");
713
- if (await fs6.pathExists(featureDir)) {
738
+ const featureDir = path8.join(projectRoot, "src", "features", "dark-mode");
739
+ if (await fs7.pathExists(featureDir)) {
714
740
  log.error("Dark mode feature already exists at src/features/dark-mode/");
715
741
  return;
716
742
  }
@@ -725,10 +751,10 @@ async function generateDarkMode(projectRoot) {
725
751
  };
726
752
  let count = 0;
727
753
  for (const [filePath, content] of Object.entries(files)) {
728
- const fullPath = path6.join(projectRoot, filePath);
754
+ const fullPath = path8.join(projectRoot, filePath);
729
755
  ctx.trackCreatedFile(fullPath);
730
- await fs6.ensureDir(path6.dirname(fullPath));
731
- await fs6.writeFile(fullPath, content);
756
+ await fs7.ensureDir(path8.dirname(fullPath));
757
+ await fs7.writeFile(fullPath, content);
732
758
  count++;
733
759
  }
734
760
  await wireLayout(projectRoot, ctx);
@@ -758,18 +784,18 @@ async function generateDarkMode(projectRoot) {
758
784
  }
759
785
  }
760
786
  async function setConfigFlag2(projectRoot, ctx) {
761
- const configPath = path6.join(projectRoot, "src", "config", "app.config.ts");
762
- if (!await fs6.pathExists(configPath)) return;
787
+ const configPath = path8.join(projectRoot, "src", "config", "app.config.ts");
788
+ if (!await fs7.pathExists(configPath)) return;
763
789
  await ctx.trackModifiedFile(configPath);
764
- const content = await fs6.readFile(configPath, "utf-8");
790
+ const content = await fs7.readFile(configPath, "utf-8");
765
791
  const updated = content.replace(/darkMode:\s*false/, "darkMode: true");
766
- await fs6.writeFile(configPath, updated);
792
+ await fs7.writeFile(configPath, updated);
767
793
  }
768
794
  async function wireLayout(projectRoot, ctx) {
769
- const layoutPath = path6.join(projectRoot, "src", "app", "layout.tsx");
770
- if (!await fs6.pathExists(layoutPath)) return;
795
+ const layoutPath = path8.join(projectRoot, "src", "app", "layout.tsx");
796
+ if (!await fs7.pathExists(layoutPath)) return;
771
797
  await ctx.trackModifiedFile(layoutPath);
772
- let content = await fs6.readFile(layoutPath, "utf-8");
798
+ let content = await fs7.readFile(layoutPath, "utf-8");
773
799
  if (!content.includes("getThemeScript")) {
774
800
  content = `import { getThemeScript } from '@/features/dark-mode';
775
801
  ${content}`;
@@ -783,13 +809,13 @@ ${content}`;
783
809
  "$1\n <head>\n <script dangerouslySetInnerHTML={{ __html: getThemeScript() }} />\n </head>\n$2"
784
810
  );
785
811
  }
786
- await fs6.writeFile(layoutPath, content);
812
+ await fs7.writeFile(layoutPath, content);
787
813
  }
788
814
  async function wireProviders(projectRoot, ctx) {
789
- const providersPath = path6.join(projectRoot, "src", "app", "providers.tsx");
790
- if (!await fs6.pathExists(providersPath)) return;
815
+ const providersPath = path8.join(projectRoot, "src", "app", "providers.tsx");
816
+ if (!await fs7.pathExists(providersPath)) return;
791
817
  await ctx.trackModifiedFile(providersPath);
792
- let content = await fs6.readFile(providersPath, "utf-8");
818
+ let content = await fs7.readFile(providersPath, "utf-8");
793
819
  if (content.includes("ThemeProvider")) return;
794
820
  content = insertImportAfterDirectives(
795
821
  content,
@@ -808,13 +834,13 @@ async function wireProviders(projectRoot, ctx) {
808
834
  );`;
809
835
  }
810
836
  );
811
- await fs6.writeFile(providersPath, content);
837
+ await fs7.writeFile(providersPath, content);
812
838
  }
813
839
  async function wireProtectedNav(projectRoot, ctx) {
814
- const navPath = path6.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
815
- if (!await fs6.pathExists(navPath)) return;
840
+ const navPath = path8.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
841
+ if (!await fs7.pathExists(navPath)) return;
816
842
  await ctx.trackModifiedFile(navPath);
817
- let content = await fs6.readFile(navPath, "utf-8");
843
+ let content = await fs7.readFile(navPath, "utf-8");
818
844
  if (content.includes("ThemeToggleSimple")) return;
819
845
  content = insertImportAfterDirectives(
820
846
  content,
@@ -829,19 +855,19 @@ async function wireProtectedNav(projectRoot, ctx) {
829
855
  /(<div className="border-t border-border-default px-4 py-3">\s*\n\s*<UserMenu \/>)/,
830
856
  '$1\n <div className="mt-2">\n <ThemeToggleSimple />\n </div>'
831
857
  );
832
- await fs6.writeFile(navPath, content);
858
+ await fs7.writeFile(navPath, content);
833
859
  }
834
860
  async function wireTailwindDarkMode(projectRoot, ctx) {
835
- const globalsPath = path6.join(projectRoot, "src", "styles", "globals.css");
836
- if (!await fs6.pathExists(globalsPath)) return;
861
+ const globalsPath = path8.join(projectRoot, "src", "styles", "globals.css");
862
+ if (!await fs7.pathExists(globalsPath)) return;
837
863
  await ctx.trackModifiedFile(globalsPath);
838
- let content = await fs6.readFile(globalsPath, "utf-8");
864
+ let content = await fs7.readFile(globalsPath, "utf-8");
839
865
  if (content.includes("@custom-variant dark")) return;
840
866
  content = content.replace(
841
867
  /@import 'tailwindcss';/,
842
868
  "@import 'tailwindcss';\n@custom-variant dark (&:where(.dark, .dark *));"
843
869
  );
844
- await fs6.writeFile(globalsPath, content);
870
+ await fs7.writeFile(globalsPath, content);
845
871
  }
846
872
  function themeProvider() {
847
873
  return `${STAMP2}
@@ -1121,19 +1147,19 @@ var init_dark_mode = __esm({
1121
1147
  });
1122
1148
 
1123
1149
  // src/utils/prisma.ts
1124
- import fs7 from "fs-extra";
1125
- import path7 from "path";
1150
+ import fs8 from "fs-extra";
1151
+ import path9 from "path";
1126
1152
  async function addUserRelation(projectRoot, field, ctx) {
1127
- const authPath = path7.join(projectRoot, "prisma", "schema", "auth.prisma");
1128
- if (!await fs7.pathExists(authPath)) return;
1153
+ const authPath = path9.join(projectRoot, "prisma", "schema", "auth.prisma");
1154
+ if (!await fs8.pathExists(authPath)) return;
1129
1155
  await ctx.trackModifiedFile(authPath);
1130
- let content = await fs7.readFile(authPath, "utf-8");
1156
+ let content = await fs8.readFile(authPath, "utf-8");
1131
1157
  if (content.includes(field)) return;
1132
1158
  const insertBefore = " @@index([email])";
1133
1159
  content = content.replace(insertBefore, ` ${field}
1134
1160
 
1135
1161
  ${insertBefore}`);
1136
- await fs7.writeFile(authPath, content);
1162
+ await fs8.writeFile(authPath, content);
1137
1163
  }
1138
1164
  var init_prisma = __esm({
1139
1165
  "src/utils/prisma.ts"() {
@@ -1146,11 +1172,11 @@ var notifications_exports = {};
1146
1172
  __export(notifications_exports, {
1147
1173
  generateNotifications: () => generateNotifications
1148
1174
  });
1149
- import fs8 from "fs-extra";
1150
- import path8 from "path";
1175
+ import fs9 from "fs-extra";
1176
+ import path10 from "path";
1151
1177
  async function generateNotifications(projectRoot) {
1152
- const featureDir = path8.join(projectRoot, "src", "features", "notifications");
1153
- if (await fs8.pathExists(featureDir)) {
1178
+ const featureDir = path10.join(projectRoot, "src", "features", "notifications");
1179
+ if (await fs9.pathExists(featureDir)) {
1154
1180
  log.error("Notifications feature already exists at src/features/notifications/");
1155
1181
  return;
1156
1182
  }
@@ -1165,6 +1191,7 @@ async function generateNotifications(projectRoot) {
1165
1191
  "src/features/notifications/components/NotificationBell.tsx": notificationBell(),
1166
1192
  "src/features/notifications/components/NotificationPanel.tsx": notificationPanel(),
1167
1193
  "src/features/notifications/components/index.ts": componentIndex2(),
1194
+ "src/features/notifications/index.ts": featureIndex(),
1168
1195
  "src/app/api/protected/notifications/route.ts": listRoute(),
1169
1196
  "src/app/api/protected/notifications/[id]/read/route.ts": markReadRoute(),
1170
1197
  "src/app/api/protected/notifications/read-all/route.ts": markAllReadRoute(),
@@ -1172,25 +1199,28 @@ async function generateNotifications(projectRoot) {
1172
1199
  };
1173
1200
  let count = 0;
1174
1201
  for (const [filePath, content] of Object.entries(files)) {
1175
- const fullPath = path8.join(projectRoot, filePath);
1202
+ const fullPath = path10.join(projectRoot, filePath);
1176
1203
  ctx.trackCreatedFile(fullPath);
1177
- await fs8.ensureDir(path8.dirname(fullPath));
1178
- await fs8.writeFile(fullPath, content);
1204
+ await fs9.ensureDir(path10.dirname(fullPath));
1205
+ await fs9.writeFile(fullPath, content);
1179
1206
  count++;
1180
1207
  }
1181
1208
  await addUserRelation(projectRoot, "notifications Notification[]", ctx);
1209
+ await wireProtectedNav2(projectRoot, ctx);
1182
1210
  await setConfigFlag3(projectRoot, ctx);
1183
1211
  await ctx.commit();
1184
- log.success(`Generated notifications feature with ${count} files`);
1212
+ log.success(`Generated and wired notifications feature (${count} files created)`);
1185
1213
  log.blank();
1186
1214
  log.step("prisma/schema/notification.prisma \u2014 Notification model");
1187
1215
  log.step("src/features/notifications/ \u2014 types, validation, server logic, hooks, components");
1188
1216
  log.step("src/app/api/protected/notifications/ \u2014 CRUD API routes");
1189
1217
  log.blank();
1218
+ log.step("Wired automatically:");
1219
+ log.step(" \u2713 NotificationBell added to navigation bar");
1220
+ log.blank();
1190
1221
  log.warn("Next steps:");
1191
1222
  log.step("1. Run yarn db:push to sync the Prisma schema");
1192
- log.step("2. Add <NotificationBell /> to your navbar/header");
1193
- log.step("3. Import createNotification from server to trigger notifications from other features:");
1223
+ log.step("2. Import createNotification from server to trigger notifications from other features:");
1194
1224
  log.step(' import { createNotification } from "@/features/notifications/server"');
1195
1225
  log.blank();
1196
1226
  } catch (error) {
@@ -1198,13 +1228,30 @@ async function generateNotifications(projectRoot) {
1198
1228
  throw error;
1199
1229
  }
1200
1230
  }
1231
+ async function wireProtectedNav2(projectRoot, ctx) {
1232
+ const navPath = path10.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
1233
+ if (!await fs9.pathExists(navPath)) return;
1234
+ await ctx.trackModifiedFile(navPath);
1235
+ let content = await fs9.readFile(navPath, "utf-8");
1236
+ if (content.includes("NotificationBell")) return;
1237
+ content = insertImportAfterDirectives(
1238
+ content,
1239
+ `import { NotificationBell } from '@/features/notifications';
1240
+ `
1241
+ );
1242
+ content = content.replace(
1243
+ /(<div className="flex items-center gap-3">)\s*\n(\s*)(<div className="hidden md:block">)/,
1244
+ "$1\n$2<NotificationBell />\n$2$3"
1245
+ );
1246
+ await fs9.writeFile(navPath, content);
1247
+ }
1201
1248
  async function setConfigFlag3(projectRoot, ctx) {
1202
- const configPath = path8.join(projectRoot, "src", "config", "app.config.ts");
1203
- if (!await fs8.pathExists(configPath)) return;
1249
+ const configPath = path10.join(projectRoot, "src", "config", "app.config.ts");
1250
+ if (!await fs9.pathExists(configPath)) return;
1204
1251
  await ctx.trackModifiedFile(configPath);
1205
- const content = await fs8.readFile(configPath, "utf-8");
1252
+ const content = await fs9.readFile(configPath, "utf-8");
1206
1253
  const updated = content.replace(/notifications:\s*false/, "notifications: true");
1207
- await fs8.writeFile(configPath, updated);
1254
+ await fs9.writeFile(configPath, updated);
1208
1255
  }
1209
1256
  function prismaSchema() {
1210
1257
  return `// Generated by mars generate notifications (notifications@${GENERATOR_VERSION3})
@@ -1681,6 +1728,11 @@ export function NotificationPanel({
1681
1728
  }
1682
1729
  `;
1683
1730
  }
1731
+ function featureIndex() {
1732
+ return `${STAMP3}
1733
+ export { NotificationBell, NotificationPanel } from './components';
1734
+ `;
1735
+ }
1684
1736
  function componentIndex2() {
1685
1737
  return `${STAMP3}
1686
1738
  export { NotificationBell } from './NotificationBell';
@@ -1781,6 +1833,7 @@ var GENERATOR_VERSION3, STAMP3;
1781
1833
  var init_notifications = __esm({
1782
1834
  "src/generators/features/notifications.ts"() {
1783
1835
  "use strict";
1836
+ init_client_file_patch();
1784
1837
  init_logger();
1785
1838
  init_rollback();
1786
1839
  init_prisma();
@@ -1794,11 +1847,11 @@ var analytics_exports = {};
1794
1847
  __export(analytics_exports, {
1795
1848
  generateAnalytics: () => generateAnalytics
1796
1849
  });
1797
- import fs9 from "fs-extra";
1798
- import path9 from "path";
1850
+ import fs10 from "fs-extra";
1851
+ import path11 from "path";
1799
1852
  async function generateAnalytics(projectRoot) {
1800
- const featureDir = path9.join(projectRoot, "src", "features", "analytics");
1801
- if (await fs9.pathExists(featureDir)) {
1853
+ const featureDir = path11.join(projectRoot, "src", "features", "analytics");
1854
+ if (await fs10.pathExists(featureDir)) {
1802
1855
  log.error("Analytics feature already exists at src/features/analytics/");
1803
1856
  return;
1804
1857
  }
@@ -1813,21 +1866,25 @@ async function generateAnalytics(projectRoot) {
1813
1866
  };
1814
1867
  let count = 0;
1815
1868
  for (const [filePath, content] of Object.entries(files)) {
1816
- const fullPath = path9.join(projectRoot, filePath);
1869
+ const fullPath = path11.join(projectRoot, filePath);
1817
1870
  ctx.trackCreatedFile(fullPath);
1818
- await fs9.ensureDir(path9.dirname(fullPath));
1819
- await fs9.writeFile(fullPath, content);
1871
+ await fs10.ensureDir(path11.dirname(fullPath));
1872
+ await fs10.writeFile(fullPath, content);
1820
1873
  count++;
1821
1874
  }
1875
+ await wireProviders2(projectRoot, ctx);
1822
1876
  await setConfigFlag4(projectRoot, ctx);
1823
1877
  await ctx.commit();
1824
- log.success(`Generated analytics feature with ${count} files`);
1878
+ log.success(`Generated and wired analytics feature (${count} files created)`);
1825
1879
  log.blank();
1826
1880
  log.step("src/features/analytics/ \u2014 types and barrel exports");
1827
1881
  log.step("src/lib/shared/components/providers/AnalyticsProvider.tsx \u2014 provider wrapper");
1828
1882
  log.step("src/lib/shared/utils/analytics.ts \u2014 unified tracking API");
1829
1883
  log.step("src/lib/shared/components/patterns/ConsentBanner.tsx \u2014 cookie consent banner");
1830
1884
  log.blank();
1885
+ log.step("Wired automatically:");
1886
+ log.step(" \u2713 AnalyticsProvider wrapping app in providers.tsx");
1887
+ log.blank();
1831
1888
  log.warn("Install dependencies for your chosen provider:");
1832
1889
  log.step("Vercel: yarn add @vercel/analytics @vercel/speed-insights");
1833
1890
  log.step("PostHog: yarn add posthog-js");
@@ -1836,20 +1893,46 @@ async function generateAnalytics(projectRoot) {
1836
1893
  log.warn("Next steps:");
1837
1894
  log.step("Set the provider in appConfig.services.analytics.provider");
1838
1895
  log.step("Set required environment variables for the chosen provider");
1839
- log.step("Add <AnalyticsProvider> and <ConsentBanner /> to your root layout");
1840
1896
  log.blank();
1841
1897
  } catch (error) {
1842
1898
  await ctx.rollback();
1843
1899
  throw error;
1844
1900
  }
1845
1901
  }
1902
+ async function wireProviders2(projectRoot, ctx) {
1903
+ const providersPath = path11.join(projectRoot, "src", "app", "providers.tsx");
1904
+ if (!await fs10.pathExists(providersPath)) return;
1905
+ await ctx.trackModifiedFile(providersPath);
1906
+ let content = await fs10.readFile(providersPath, "utf-8");
1907
+ if (content.includes("AnalyticsProvider")) return;
1908
+ content = insertImportAfterDirectives(
1909
+ content,
1910
+ `import { AnalyticsProvider } from '@/lib/shared/components/providers/AnalyticsProvider';
1911
+ `
1912
+ );
1913
+ content = content.replace(
1914
+ /return\s+(?:\(\s*)?((?:<\w+[^>]*>)[\s\S]*?\{children\}[\s\S]*?(?:<\/\w+>))\s*(?:\)\s*)?;/,
1915
+ (_match, jsx) => {
1916
+ return `return (
1917
+ <AnalyticsProvider>${jsx}</AnalyticsProvider>
1918
+ );`;
1919
+ }
1920
+ );
1921
+ await fs10.writeFile(providersPath, content);
1922
+ const written = await fs10.readFile(providersPath, "utf-8");
1923
+ if (!written.includes("AnalyticsProvider")) {
1924
+ throw new Error(
1925
+ "wireProviders: AnalyticsProvider was not inserted into providers.tsx \u2014 the return statement pattern did not match. Review the template file structure."
1926
+ );
1927
+ }
1928
+ }
1846
1929
  async function setConfigFlag4(projectRoot, ctx) {
1847
- const configPath = path9.join(projectRoot, "src", "config", "app.config.ts");
1848
- if (!await fs9.pathExists(configPath)) return;
1930
+ const configPath = path11.join(projectRoot, "src", "config", "app.config.ts");
1931
+ if (!await fs10.pathExists(configPath)) return;
1849
1932
  await ctx.trackModifiedFile(configPath);
1850
- const content = await fs9.readFile(configPath, "utf-8");
1933
+ const content = await fs10.readFile(configPath, "utf-8");
1851
1934
  const updated = content.replace(/analytics:\s*false/, "analytics: true");
1852
- await fs9.writeFile(configPath, updated);
1935
+ await fs10.writeFile(configPath, updated);
1853
1936
  }
1854
1937
  function types3() {
1855
1938
  return `${STAMP4}
@@ -1872,7 +1955,7 @@ function analyticsProvider() {
1872
1955
  return `${STAMP4}
1873
1956
  'use client';
1874
1957
 
1875
- import { useEffect, useState } from 'react';
1958
+ import { Suspense, useEffect, useState } from 'react';
1876
1959
  import { usePathname, useSearchParams } from 'next/navigation';
1877
1960
  import Script from 'next/script';
1878
1961
  import { appConfig } from '@/config/app.config';
@@ -1895,7 +1978,9 @@ export function AnalyticsProvider({ children }: AnalyticsProviderProps) {
1895
1978
  {provider === 'vercel' && <VercelAnalyticsProvider />}
1896
1979
  {provider === 'posthog' && <PostHogAnalyticsProvider />}
1897
1980
  {provider === 'google' && <GoogleAnalyticsProvider />}
1898
- <PageViewTracker />
1981
+ <Suspense>
1982
+ <PageViewTracker />
1983
+ </Suspense>
1899
1984
  {children}
1900
1985
  </>
1901
1986
  );
@@ -2059,22 +2144,18 @@ export function trackEvent(
2059
2144
 
2060
2145
  switch (provider) {
2061
2146
  case 'vercel': {
2062
- try {
2063
- const { track } = require('@vercel/analytics');
2064
- track(eventName, properties);
2065
- } catch {
2066
- // @vercel/analytics not available
2067
- }
2147
+ const vercelSpec = '@vercel/analytics';
2148
+ import(/* webpackIgnore: true */ vercelSpec)
2149
+ .then((mod) => mod.track(eventName, properties))
2150
+ .catch(() => {});
2068
2151
  break;
2069
2152
  }
2070
2153
 
2071
2154
  case 'posthog': {
2072
- try {
2073
- const posthog = require('posthog-js').default;
2074
- posthog.capture(eventName, properties);
2075
- } catch {
2076
- // posthog-js not available
2077
- }
2155
+ const phSpec = 'posthog-js';
2156
+ import(/* webpackIgnore: true */ phSpec)
2157
+ .then((mod) => mod.default.capture(eventName, properties))
2158
+ .catch(() => {});
2078
2159
  break;
2079
2160
  }
2080
2161
 
@@ -2105,12 +2186,10 @@ export function identifyUser(
2105
2186
  break;
2106
2187
 
2107
2188
  case 'posthog': {
2108
- try {
2109
- const posthog = require('posthog-js').default;
2110
- posthog.identify(userId, traits);
2111
- } catch {
2112
- // posthog-js not available
2113
- }
2189
+ const phSpec = 'posthog-js';
2190
+ import(/* webpackIgnore: true */ phSpec)
2191
+ .then((mod) => mod.default.identify(userId, traits))
2192
+ .catch(() => {});
2114
2193
  break;
2115
2194
  }
2116
2195
 
@@ -2199,6 +2278,7 @@ var GENERATOR_VERSION4, STAMP4;
2199
2278
  var init_analytics = __esm({
2200
2279
  "src/generators/features/analytics.ts"() {
2201
2280
  "use strict";
2281
+ init_client_file_patch();
2202
2282
  init_logger();
2203
2283
  init_rollback();
2204
2284
  GENERATOR_VERSION4 = "0.1.0";
@@ -2211,11 +2291,11 @@ var command_palette_exports = {};
2211
2291
  __export(command_palette_exports, {
2212
2292
  generateCommandPalette: () => generateCommandPalette
2213
2293
  });
2214
- import fs10 from "fs-extra";
2215
- import path10 from "path";
2294
+ import fs11 from "fs-extra";
2295
+ import path12 from "path";
2216
2296
  async function generateCommandPalette(projectRoot) {
2217
- const featureDir = path10.join(projectRoot, "src", "features", "command-palette");
2218
- if (await fs10.pathExists(featureDir)) {
2297
+ const featureDir = path12.join(projectRoot, "src", "features", "command-palette");
2298
+ if (await fs11.pathExists(featureDir)) {
2219
2299
  log.error("Command palette feature already exists at src/features/command-palette/");
2220
2300
  return;
2221
2301
  }
@@ -2229,26 +2309,28 @@ async function generateCommandPalette(projectRoot) {
2229
2309
  "src/features/command-palette/components/CommandPalette.tsx": commandPalette(),
2230
2310
  "src/features/command-palette/components/CommandTrigger.tsx": commandTrigger(),
2231
2311
  "src/features/command-palette/components/index.ts": componentIndex3(),
2232
- "src/features/command-palette/index.ts": featureIndex()
2312
+ "src/features/command-palette/index.ts": featureIndex2()
2233
2313
  };
2234
2314
  let count = 0;
2235
2315
  for (const [filePath, content] of Object.entries(files)) {
2236
- const fullPath = path10.join(projectRoot, filePath);
2316
+ const fullPath = path12.join(projectRoot, filePath);
2237
2317
  ctx.trackCreatedFile(fullPath);
2238
- await fs10.ensureDir(path10.dirname(fullPath));
2239
- await fs10.writeFile(fullPath, content);
2318
+ await fs11.ensureDir(path12.dirname(fullPath));
2319
+ await fs11.writeFile(fullPath, content);
2240
2320
  count++;
2241
2321
  }
2242
2322
  await addDependencies(projectRoot, { cmdk: "^1.0.0" });
2323
+ await wireLayout2(projectRoot, ctx);
2243
2324
  await setConfigFlag5(projectRoot, ctx);
2244
2325
  await ctx.commit();
2245
- log.success(`Generated command palette feature with ${count} files`);
2326
+ log.success(`Generated and wired command palette feature (${count} files created)`);
2246
2327
  log.blank();
2247
2328
  log.step("src/features/command-palette/ \u2014 types, actions, registry, recent, components");
2248
2329
  log.blank();
2249
- log.warn("Integration steps:");
2250
- log.step("Add <CommandPalette /> to your protected layout");
2251
- log.step("Optionally add <CommandTrigger /> to your navbar for discoverability");
2330
+ log.step("Wired automatically:");
2331
+ log.step(" \u2713 CommandPalette overlay mounted in root layout");
2332
+ log.step(" \u2713 Press \u2318K / Ctrl+K to open");
2333
+ log.blank();
2252
2334
  log.step("Register custom actions with registerAction() from any feature");
2253
2335
  log.blank();
2254
2336
  } catch (error) {
@@ -2256,13 +2338,27 @@ async function generateCommandPalette(projectRoot) {
2256
2338
  throw error;
2257
2339
  }
2258
2340
  }
2341
+ async function wireLayout2(projectRoot, ctx) {
2342
+ const layoutPath = path12.join(projectRoot, "src", "app", "layout.tsx");
2343
+ if (!await fs11.pathExists(layoutPath)) return;
2344
+ let content = await fs11.readFile(layoutPath, "utf-8");
2345
+ if (content.includes("CommandPalette")) return;
2346
+ await ctx.trackModifiedFile(layoutPath);
2347
+ content = `import { CommandPalette } from '@/features/command-palette';
2348
+ ${content}`;
2349
+ content = content.replace(
2350
+ /(\s*)(<Providers>[\s\S]*?<\/Providers>)/,
2351
+ "$1$2\n$1<CommandPalette />"
2352
+ );
2353
+ await fs11.writeFile(layoutPath, content);
2354
+ }
2259
2355
  async function setConfigFlag5(projectRoot, ctx) {
2260
- const configPath = path10.join(projectRoot, "src", "config", "app.config.ts");
2261
- if (!await fs10.pathExists(configPath)) return;
2356
+ const configPath = path12.join(projectRoot, "src", "config", "app.config.ts");
2357
+ if (!await fs11.pathExists(configPath)) return;
2262
2358
  await ctx.trackModifiedFile(configPath);
2263
- const content = await fs10.readFile(configPath, "utf-8");
2359
+ const content = await fs11.readFile(configPath, "utf-8");
2264
2360
  const updated = content.replace(/commandPalette:\s*false/, "commandPalette: true");
2265
- await fs10.writeFile(configPath, updated);
2361
+ await fs11.writeFile(configPath, updated);
2266
2362
  }
2267
2363
  function types4() {
2268
2364
  return `${STAMP5}
@@ -2652,7 +2748,7 @@ export { CommandPalette } from './CommandPalette';
2652
2748
  export { CommandTrigger } from './CommandTrigger';
2653
2749
  `;
2654
2750
  }
2655
- function featureIndex() {
2751
+ function featureIndex2() {
2656
2752
  return `${STAMP5}
2657
2753
  export { CommandPalette, CommandTrigger } from './components';
2658
2754
  export { registerAction, getCustomActions } from './registry';
@@ -2678,11 +2774,11 @@ var onboarding_exports = {};
2678
2774
  __export(onboarding_exports, {
2679
2775
  generateOnboarding: () => generateOnboarding
2680
2776
  });
2681
- import fs11 from "fs-extra";
2682
- import path11 from "path";
2777
+ import fs12 from "fs-extra";
2778
+ import path13 from "path";
2683
2779
  async function generateOnboarding(projectRoot) {
2684
- const featureDir = path11.join(projectRoot, "src", "features", "onboarding");
2685
- if (await fs11.pathExists(featureDir)) {
2780
+ const featureDir = path13.join(projectRoot, "src", "features", "onboarding");
2781
+ if (await fs12.pathExists(featureDir)) {
2686
2782
  log.error("Onboarding feature already exists at src/features/onboarding/");
2687
2783
  return;
2688
2784
  }
@@ -2700,17 +2796,18 @@ async function generateOnboarding(projectRoot) {
2700
2796
  "src/app/(protected)/onboarding/page.tsx": onboardingPage(),
2701
2797
  "src/app/api/protected/onboarding/route.ts": getProgressRoute(),
2702
2798
  "src/app/api/protected/onboarding/step/route.ts": stepActionRoute(),
2703
- "src/features/onboarding/index.ts": featureIndex2()
2799
+ "src/features/onboarding/index.ts": featureIndex3()
2704
2800
  };
2705
2801
  let count = 0;
2706
2802
  for (const [filePath, content] of Object.entries(files)) {
2707
- const fullPath = path11.join(projectRoot, filePath);
2803
+ const fullPath = path13.join(projectRoot, filePath);
2708
2804
  ctx.trackCreatedFile(fullPath);
2709
- await fs11.ensureDir(path11.dirname(fullPath));
2710
- await fs11.writeFile(fullPath, content);
2805
+ await fs12.ensureDir(path13.dirname(fullPath));
2806
+ await fs12.writeFile(fullPath, content);
2711
2807
  count++;
2712
2808
  }
2713
2809
  await addUserRelation(projectRoot, "onboardingProgress OnboardingProgress?", ctx);
2810
+ await registerRoute(projectRoot, "onboarding", "/onboarding", ctx);
2714
2811
  await setConfigFlag6(projectRoot, ctx);
2715
2812
  await ctx.commit();
2716
2813
  log.success(`Generated onboarding feature with ${count} files`);
@@ -2731,12 +2828,12 @@ async function generateOnboarding(projectRoot) {
2731
2828
  }
2732
2829
  }
2733
2830
  async function setConfigFlag6(projectRoot, ctx) {
2734
- const configPath = path11.join(projectRoot, "src", "config", "app.config.ts");
2735
- if (!await fs11.pathExists(configPath)) return;
2831
+ const configPath = path13.join(projectRoot, "src", "config", "app.config.ts");
2832
+ if (!await fs12.pathExists(configPath)) return;
2736
2833
  await ctx.trackModifiedFile(configPath);
2737
- const content = await fs11.readFile(configPath, "utf-8");
2834
+ const content = await fs12.readFile(configPath, "utf-8");
2738
2835
  const updated = content.replace(/onboarding:\s*false/, "onboarding: true");
2739
- await fs11.writeFile(configPath, updated);
2836
+ await fs12.writeFile(configPath, updated);
2740
2837
  }
2741
2838
  function prismaSchema2() {
2742
2839
  return `// Generated by mars generate onboarding (onboarding@${GENERATOR_VERSION6})
@@ -3231,7 +3328,7 @@ export const POST = withAuthNoParams(async (request: AuthenticatedRequest) => {
3231
3328
  });
3232
3329
  `;
3233
3330
  }
3234
- function featureIndex2() {
3331
+ function featureIndex3() {
3235
3332
  return `${STAMP6}
3236
3333
  export { ONBOARDING_STEPS } from './config';
3237
3334
  export type { OnboardingStep } from './config';
@@ -3252,6 +3349,7 @@ var init_onboarding = __esm({
3252
3349
  init_logger();
3253
3350
  init_rollback();
3254
3351
  init_prisma();
3352
+ init_routes();
3255
3353
  GENERATOR_VERSION6 = "0.1.0";
3256
3354
  STAMP6 = `// @mars-generated onboarding@${GENERATOR_VERSION6}`;
3257
3355
  }
@@ -3262,11 +3360,11 @@ var search_exports = {};
3262
3360
  __export(search_exports, {
3263
3361
  generateSearch: () => generateSearch
3264
3362
  });
3265
- import fs12 from "fs-extra";
3266
- import path12 from "path";
3363
+ import fs13 from "fs-extra";
3364
+ import path14 from "path";
3267
3365
  async function generateSearch(projectRoot) {
3268
- const featureDir = path12.join(projectRoot, "src", "features", "search");
3269
- if (await fs12.pathExists(featureDir)) {
3366
+ const featureDir = path14.join(projectRoot, "src", "features", "search");
3367
+ if (await fs13.pathExists(featureDir)) {
3270
3368
  log.error("Search feature already exists at src/features/search/");
3271
3369
  return;
3272
3370
  }
@@ -3279,25 +3377,30 @@ async function generateSearch(projectRoot) {
3279
3377
  "src/features/search/server/index.ts": serverIndex(),
3280
3378
  "src/features/search/hooks/use-search.ts": useSearchHook(),
3281
3379
  "src/features/search/components/SearchInput.tsx": searchInput(),
3380
+ "src/features/search/components/NavSearch.tsx": navSearch(),
3282
3381
  "src/features/search/components/index.ts": componentIndex5(),
3283
- "src/features/search/index.ts": featureIndex3(),
3382
+ "src/features/search/index.ts": featureIndex4(),
3284
3383
  "src/app/api/protected/search/route.ts": searchRoute()
3285
3384
  };
3286
3385
  let count = 0;
3287
3386
  for (const [filePath, content] of Object.entries(files)) {
3288
- const fullPath = path12.join(projectRoot, filePath);
3387
+ const fullPath = path14.join(projectRoot, filePath);
3289
3388
  ctx.trackCreatedFile(fullPath);
3290
- await fs12.ensureDir(path12.dirname(fullPath));
3291
- await fs12.writeFile(fullPath, content);
3389
+ await fs13.ensureDir(path14.dirname(fullPath));
3390
+ await fs13.writeFile(fullPath, content);
3292
3391
  count++;
3293
3392
  }
3393
+ await wireProtectedNav3(projectRoot, ctx);
3294
3394
  await setConfigFlag7(projectRoot, ctx);
3295
3395
  await ctx.commit();
3296
- log.success(`Generated search feature with ${count} files`);
3396
+ log.success(`Generated and wired search feature (${count} files created)`);
3297
3397
  log.blank();
3298
3398
  log.step("src/features/search/ \u2014 types, validation, server logic, hooks, components");
3299
3399
  log.step("src/app/api/protected/search/ \u2014 authenticated search endpoint");
3300
3400
  log.blank();
3401
+ log.step("Wired automatically:");
3402
+ log.step(" \u2713 NavSearch added to navigation bar");
3403
+ log.blank();
3301
3404
  log.warn("Search uses Postgres full-text search by default (no extra setup).");
3302
3405
  log.blank();
3303
3406
  log.step("For Algolia: yarn add algoliasearch + set ALGOLIA_APP_ID, ALGOLIA_API_KEY env vars");
@@ -3311,13 +3414,30 @@ async function generateSearch(projectRoot) {
3311
3414
  throw error;
3312
3415
  }
3313
3416
  }
3417
+ async function wireProtectedNav3(projectRoot, ctx) {
3418
+ const navPath = path14.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
3419
+ if (!await fs13.pathExists(navPath)) return;
3420
+ let content = await fs13.readFile(navPath, "utf-8");
3421
+ if (content.includes("NavSearch")) return;
3422
+ await ctx.trackModifiedFile(navPath);
3423
+ content = insertImportAfterDirectives(
3424
+ content,
3425
+ `import { NavSearch } from '@/features/search';
3426
+ `
3427
+ );
3428
+ content = content.replace(
3429
+ /(<div className="flex items-center gap-3">)\s*\n(\s*)(<)/,
3430
+ "$1\n$2<NavSearch />\n$2$3"
3431
+ );
3432
+ await fs13.writeFile(navPath, content);
3433
+ }
3314
3434
  async function setConfigFlag7(projectRoot, ctx) {
3315
- const configPath = path12.join(projectRoot, "src", "config", "app.config.ts");
3316
- if (!await fs12.pathExists(configPath)) return;
3435
+ const configPath = path14.join(projectRoot, "src", "config", "app.config.ts");
3436
+ if (!await fs13.pathExists(configPath)) return;
3317
3437
  await ctx.trackModifiedFile(configPath);
3318
- const content = await fs12.readFile(configPath, "utf-8");
3438
+ const content = await fs13.readFile(configPath, "utf-8");
3319
3439
  const updated = content.replace(/search:\s*false/, "search: true");
3320
- await fs12.writeFile(configPath, updated);
3440
+ await fs13.writeFile(configPath, updated);
3321
3441
  }
3322
3442
  function types6() {
3323
3443
  return `${STAMP7}
@@ -3587,17 +3707,39 @@ export function SearchInput({
3587
3707
  }
3588
3708
  `;
3589
3709
  }
3710
+ function navSearch() {
3711
+ return `${STAMP7}
3712
+ 'use client';
3713
+
3714
+ import { useState } from 'react';
3715
+ import { SearchInput } from './SearchInput';
3716
+
3717
+ export function NavSearch() {
3718
+ const [query, setQuery] = useState('');
3719
+
3720
+ return (
3721
+ <SearchInput
3722
+ value={query}
3723
+ onQueryChange={setQuery}
3724
+ placeholder="Search\u2026"
3725
+ className="w-48 lg:w-64"
3726
+ />
3727
+ );
3728
+ }
3729
+ `;
3730
+ }
3590
3731
  function componentIndex5() {
3591
3732
  return `${STAMP7}
3592
3733
  export { SearchInput } from './SearchInput';
3734
+ export { NavSearch } from './NavSearch';
3593
3735
  `;
3594
3736
  }
3595
- function featureIndex3() {
3737
+ function featureIndex4() {
3596
3738
  return `${STAMP7}
3597
3739
  export type { SearchResult, SearchOptions, SearchProvider } from './types';
3598
3740
  export { searchParamsSchema } from './validation/schemas';
3599
3741
  export type { SearchParams } from './validation/schemas';
3600
- export { SearchInput } from './components';
3742
+ export { SearchInput, NavSearch } from './components';
3601
3743
  export { useSearch } from './hooks/use-search';
3602
3744
  `;
3603
3745
  }
@@ -3636,6 +3778,7 @@ var GENERATOR_VERSION7, STAMP7;
3636
3778
  var init_search = __esm({
3637
3779
  "src/generators/features/search.ts"() {
3638
3780
  "use strict";
3781
+ init_client_file_patch();
3639
3782
  init_logger();
3640
3783
  init_rollback();
3641
3784
  GENERATOR_VERSION7 = "0.1.0";
@@ -3648,11 +3791,11 @@ var realtime_exports = {};
3648
3791
  __export(realtime_exports, {
3649
3792
  generateRealtime: () => generateRealtime
3650
3793
  });
3651
- import fs13 from "fs-extra";
3652
- import path13 from "path";
3794
+ import fs14 from "fs-extra";
3795
+ import path15 from "path";
3653
3796
  async function generateRealtime(projectRoot) {
3654
- const featureDir = path13.join(projectRoot, "src", "features", "realtime");
3655
- if (await fs13.pathExists(featureDir)) {
3797
+ const featureDir = path15.join(projectRoot, "src", "features", "realtime");
3798
+ if (await fs14.pathExists(featureDir)) {
3656
3799
  log.error("Realtime feature already exists at src/features/realtime/");
3657
3800
  return;
3658
3801
  }
@@ -3664,15 +3807,15 @@ async function generateRealtime(projectRoot) {
3664
3807
  "src/features/realtime/server/sse.ts": sseProvider(),
3665
3808
  "src/features/realtime/hooks/use-event-source.ts": useEventSourceHook(),
3666
3809
  "src/features/realtime/hooks/index.ts": hooksIndex(),
3667
- "src/features/realtime/index.ts": featureIndex4(),
3810
+ "src/features/realtime/index.ts": featureIndex5(),
3668
3811
  "src/app/api/protected/realtime/stream/route.ts": sseStreamRoute()
3669
3812
  };
3670
3813
  let count = 0;
3671
3814
  for (const [filePath, content] of Object.entries(files)) {
3672
- const fullPath = path13.join(projectRoot, filePath);
3815
+ const fullPath = path15.join(projectRoot, filePath);
3673
3816
  ctx.trackCreatedFile(fullPath);
3674
- await fs13.ensureDir(path13.dirname(fullPath));
3675
- await fs13.writeFile(fullPath, content);
3817
+ await fs14.ensureDir(path15.dirname(fullPath));
3818
+ await fs14.writeFile(fullPath, content);
3676
3819
  count++;
3677
3820
  }
3678
3821
  await setConfigFlag8(projectRoot, ctx);
@@ -3697,12 +3840,12 @@ async function generateRealtime(projectRoot) {
3697
3840
  }
3698
3841
  }
3699
3842
  async function setConfigFlag8(projectRoot, ctx) {
3700
- const configPath = path13.join(projectRoot, "src", "config", "app.config.ts");
3701
- if (!await fs13.pathExists(configPath)) return;
3843
+ const configPath = path15.join(projectRoot, "src", "config", "app.config.ts");
3844
+ if (!await fs14.pathExists(configPath)) return;
3702
3845
  await ctx.trackModifiedFile(configPath);
3703
- const content = await fs13.readFile(configPath, "utf-8");
3846
+ const content = await fs14.readFile(configPath, "utf-8");
3704
3847
  const updated = content.replace(/realtime:\s*false/, "realtime: true");
3705
- await fs13.writeFile(configPath, updated);
3848
+ await fs14.writeFile(configPath, updated);
3706
3849
  }
3707
3850
  function types7() {
3708
3851
  return `${STAMP8}
@@ -3912,7 +4055,7 @@ function hooksIndex() {
3912
4055
  export { useEventSource } from './use-event-source';
3913
4056
  `;
3914
4057
  }
3915
- function featureIndex4() {
4058
+ function featureIndex5() {
3916
4059
  return `${STAMP8}
3917
4060
  export type {
3918
4061
  RealtimeProvider,
@@ -4011,11 +4154,11 @@ var ai_exports = {};
4011
4154
  __export(ai_exports, {
4012
4155
  generateAI: () => generateAI
4013
4156
  });
4014
- import fs14 from "fs-extra";
4015
- import path14 from "path";
4157
+ import fs15 from "fs-extra";
4158
+ import path16 from "path";
4016
4159
  async function generateAI(projectRoot) {
4017
- const featureDir = path14.join(projectRoot, "src", "features", "ai");
4018
- if (await fs14.pathExists(featureDir)) {
4160
+ const featureDir = path16.join(projectRoot, "src", "features", "ai");
4161
+ if (await fs15.pathExists(featureDir)) {
4019
4162
  log.error("AI feature already exists at src/features/ai/");
4020
4163
  return;
4021
4164
  }
@@ -4033,10 +4176,10 @@ async function generateAI(projectRoot) {
4033
4176
  };
4034
4177
  let count = 0;
4035
4178
  for (const [filePath, content] of Object.entries(files)) {
4036
- const fullPath = path14.join(projectRoot, filePath);
4179
+ const fullPath = path16.join(projectRoot, filePath);
4037
4180
  ctx.trackCreatedFile(fullPath);
4038
- await fs14.ensureDir(path14.dirname(fullPath));
4039
- await fs14.writeFile(fullPath, content);
4181
+ await fs15.ensureDir(path16.dirname(fullPath));
4182
+ await fs15.writeFile(fullPath, content);
4040
4183
  count++;
4041
4184
  }
4042
4185
  await addDependencies(projectRoot, {
@@ -4062,12 +4205,12 @@ async function generateAI(projectRoot) {
4062
4205
  }
4063
4206
  }
4064
4207
  async function setConfigFlag9(projectRoot, ctx) {
4065
- const configPath = path14.join(projectRoot, "src", "config", "app.config.ts");
4066
- if (!await fs14.pathExists(configPath)) return;
4208
+ const configPath = path16.join(projectRoot, "src", "config", "app.config.ts");
4209
+ if (!await fs15.pathExists(configPath)) return;
4067
4210
  await ctx.trackModifiedFile(configPath);
4068
- const content = await fs14.readFile(configPath, "utf-8");
4211
+ const content = await fs15.readFile(configPath, "utf-8");
4069
4212
  const updated = content.replace(/ai:\s*false/, "ai: true");
4070
- await fs14.writeFile(configPath, updated);
4213
+ await fs15.writeFile(configPath, updated);
4071
4214
  }
4072
4215
  function types8() {
4073
4216
  return `${STAMP9}
@@ -4506,11 +4649,11 @@ var cookie_consent_exports = {};
4506
4649
  __export(cookie_consent_exports, {
4507
4650
  generateCookieConsent: () => generateCookieConsent
4508
4651
  });
4509
- import fs15 from "fs-extra";
4510
- import path15 from "path";
4652
+ import fs16 from "fs-extra";
4653
+ import path17 from "path";
4511
4654
  async function generateCookieConsent(projectRoot) {
4512
- const featureDir = path15.join(projectRoot, "src", "features", "cookie-consent");
4513
- if (await fs15.pathExists(featureDir)) {
4655
+ const featureDir = path17.join(projectRoot, "src", "features", "cookie-consent");
4656
+ if (await fs16.pathExists(featureDir)) {
4514
4657
  log.error("Cookie consent feature already exists at src/features/cookie-consent/");
4515
4658
  return;
4516
4659
  }
@@ -4522,27 +4665,29 @@ async function generateCookieConsent(projectRoot) {
4522
4665
  "src/features/cookie-consent/components/CookieConsentBanner.tsx": cookieConsentBanner(),
4523
4666
  "src/features/cookie-consent/components/CookiePreferencesDialog.tsx": cookiePreferencesDialog(),
4524
4667
  "src/features/cookie-consent/components/index.ts": componentsIndex(),
4525
- "src/features/cookie-consent/index.ts": featureIndex5()
4668
+ "src/features/cookie-consent/index.ts": featureIndex6()
4526
4669
  };
4527
4670
  let count = 0;
4528
4671
  for (const [filePath, content] of Object.entries(files)) {
4529
- const fullPath = path15.join(projectRoot, filePath);
4672
+ const fullPath = path17.join(projectRoot, filePath);
4530
4673
  ctx.trackCreatedFile(fullPath);
4531
- await fs15.ensureDir(path15.dirname(fullPath));
4532
- await fs15.writeFile(fullPath, content);
4674
+ await fs16.ensureDir(path17.dirname(fullPath));
4675
+ await fs16.writeFile(fullPath, content);
4533
4676
  count++;
4534
4677
  }
4678
+ await wireLayout3(projectRoot, ctx);
4535
4679
  await setConfigFlag10(projectRoot, ctx);
4536
4680
  await ctx.commit();
4537
- log.success(`Generated cookie consent feature with ${count} files`);
4681
+ log.success(`Generated and wired cookie consent feature (${count} files created)`);
4538
4682
  log.blank();
4539
4683
  log.step("src/features/cookie-consent/types.ts \u2014 ConsentStatus, ConsentPreferences");
4540
4684
  log.step("src/features/cookie-consent/hooks/use-consent.ts \u2014 useConsent hook");
4541
4685
  log.step("src/features/cookie-consent/components/CookieConsentBanner.tsx \u2014 banner");
4542
4686
  log.step("src/features/cookie-consent/components/CookiePreferencesDialog.tsx \u2014 preferences modal");
4543
4687
  log.blank();
4544
- log.warn("Complete setup:");
4545
- log.step("Add <CookieConsentBanner /> to your root layout");
4688
+ log.step("Wired automatically:");
4689
+ log.step(" \u2713 CookieConsentBanner mounted in root layout");
4690
+ log.blank();
4546
4691
  log.step("Optionally add <CookiePreferencesDialog /> for granular consent management");
4547
4692
  log.step("Check consent status with useConsent() before loading tracking scripts");
4548
4693
  log.blank();
@@ -4551,13 +4696,27 @@ async function generateCookieConsent(projectRoot) {
4551
4696
  throw error;
4552
4697
  }
4553
4698
  }
4699
+ async function wireLayout3(projectRoot, ctx) {
4700
+ const layoutPath = path17.join(projectRoot, "src", "app", "layout.tsx");
4701
+ if (!await fs16.pathExists(layoutPath)) return;
4702
+ let content = await fs16.readFile(layoutPath, "utf-8");
4703
+ if (content.includes("CookieConsentBanner")) return;
4704
+ await ctx.trackModifiedFile(layoutPath);
4705
+ content = `import { CookieConsentBanner } from '@/features/cookie-consent';
4706
+ ${content}`;
4707
+ content = content.replace(
4708
+ /(\s*)(<Providers>[\s\S]*?<\/Providers>)/,
4709
+ "$1$2\n$1<CookieConsentBanner />"
4710
+ );
4711
+ await fs16.writeFile(layoutPath, content);
4712
+ }
4554
4713
  async function setConfigFlag10(projectRoot, ctx) {
4555
- const configPath = path15.join(projectRoot, "src", "config", "app.config.ts");
4556
- if (!await fs15.pathExists(configPath)) return;
4714
+ const configPath = path17.join(projectRoot, "src", "config", "app.config.ts");
4715
+ if (!await fs16.pathExists(configPath)) return;
4557
4716
  await ctx.trackModifiedFile(configPath);
4558
- const content = await fs15.readFile(configPath, "utf-8");
4717
+ const content = await fs16.readFile(configPath, "utf-8");
4559
4718
  const updated = content.replace(/cookieConsent:\s*false/, "cookieConsent: true");
4560
- await fs15.writeFile(configPath, updated);
4719
+ await fs16.writeFile(configPath, updated);
4561
4720
  }
4562
4721
  function types9() {
4563
4722
  return `${STAMP10}
@@ -4819,7 +4978,7 @@ export { CookieConsentBanner } from './CookieConsentBanner';
4819
4978
  export { CookiePreferencesDialog } from './CookiePreferencesDialog';
4820
4979
  `;
4821
4980
  }
4822
- function featureIndex5() {
4981
+ function featureIndex6() {
4823
4982
  return `${STAMP10}
4824
4983
  export type { ConsentStatus, ConsentPreferences } from './types';
4825
4984
  export { useConsent } from './hooks/use-consent';
@@ -4842,11 +5001,11 @@ var coming_soon_exports = {};
4842
5001
  __export(coming_soon_exports, {
4843
5002
  generateComingSoon: () => generateComingSoon
4844
5003
  });
4845
- import fs16 from "fs-extra";
4846
- import path16 from "path";
5004
+ import fs17 from "fs-extra";
5005
+ import path18 from "path";
4847
5006
  async function generateComingSoon(projectRoot) {
4848
- const featureDir = path16.join(projectRoot, "src", "features", "coming-soon");
4849
- if (await fs16.pathExists(featureDir)) {
5007
+ const featureDir = path18.join(projectRoot, "src", "features", "coming-soon");
5008
+ if (await fs17.pathExists(featureDir)) {
4850
5009
  log.error("Coming soon feature already exists at src/features/coming-soon/");
4851
5010
  return;
4852
5011
  }
@@ -4863,12 +5022,13 @@ async function generateComingSoon(projectRoot) {
4863
5022
  };
4864
5023
  let count = 0;
4865
5024
  for (const [filePath, content] of Object.entries(files)) {
4866
- const fullPath = path16.join(projectRoot, filePath);
5025
+ const fullPath = path18.join(projectRoot, filePath);
4867
5026
  ctx.trackCreatedFile(fullPath);
4868
- await fs16.ensureDir(path16.dirname(fullPath));
4869
- await fs16.writeFile(fullPath, content);
5027
+ await fs17.ensureDir(path18.dirname(fullPath));
5028
+ await fs17.writeFile(fullPath, content);
4870
5029
  count++;
4871
5030
  }
5031
+ await registerRoute(projectRoot, "comingSoon", "/coming-soon", ctx);
4872
5032
  await setConfigFlag11(projectRoot, ctx);
4873
5033
  await ctx.commit();
4874
5034
  log.success(`Generated coming soon feature with ${count} files`);
@@ -4887,12 +5047,12 @@ async function generateComingSoon(projectRoot) {
4887
5047
  }
4888
5048
  }
4889
5049
  async function setConfigFlag11(projectRoot, ctx) {
4890
- const configPath = path16.join(projectRoot, "src", "config", "app.config.ts");
4891
- if (!await fs16.pathExists(configPath)) return;
5050
+ const configPath = path18.join(projectRoot, "src", "config", "app.config.ts");
5051
+ if (!await fs17.pathExists(configPath)) return;
4892
5052
  await ctx.trackModifiedFile(configPath);
4893
- const content = await fs16.readFile(configPath, "utf-8");
5053
+ const content = await fs17.readFile(configPath, "utf-8");
4894
5054
  const updated = content.replace(/comingSoon:\s*false/, "comingSoon: true");
4895
- await fs16.writeFile(configPath, updated);
5055
+ await fs17.writeFile(configPath, updated);
4896
5056
  }
4897
5057
  function types10() {
4898
5058
  return `${STAMP11}
@@ -5069,6 +5229,7 @@ var init_coming_soon = __esm({
5069
5229
  "use strict";
5070
5230
  init_logger();
5071
5231
  init_rollback();
5232
+ init_routes();
5072
5233
  GENERATOR_VERSION11 = "0.1.0";
5073
5234
  STAMP11 = `// @mars-generated coming-soon@${GENERATOR_VERSION11}`;
5074
5235
  }
@@ -5079,11 +5240,11 @@ var sentry_exports = {};
5079
5240
  __export(sentry_exports, {
5080
5241
  generateSentry: () => generateSentry
5081
5242
  });
5082
- import fs17 from "fs-extra";
5083
- import path17 from "path";
5243
+ import fs18 from "fs-extra";
5244
+ import path19 from "path";
5084
5245
  async function generateSentry(projectRoot) {
5085
- const featureDir = path17.join(projectRoot, "src", "features", "sentry");
5086
- if (await fs17.pathExists(featureDir)) {
5246
+ const featureDir = path19.join(projectRoot, "src", "features", "sentry");
5247
+ if (await fs18.pathExists(featureDir)) {
5087
5248
  log.error("Sentry feature already exists at src/features/sentry/");
5088
5249
  return;
5089
5250
  }
@@ -5099,24 +5260,27 @@ async function generateSentry(projectRoot) {
5099
5260
  };
5100
5261
  let count = 0;
5101
5262
  for (const [filePath, content] of Object.entries(files)) {
5102
- const fullPath = path17.join(projectRoot, filePath);
5263
+ const fullPath = path19.join(projectRoot, filePath);
5103
5264
  ctx.trackCreatedFile(fullPath);
5104
- await fs17.ensureDir(path17.dirname(fullPath));
5105
- await fs17.writeFile(fullPath, content);
5265
+ await fs18.ensureDir(path19.dirname(fullPath));
5266
+ await fs18.writeFile(fullPath, content);
5106
5267
  count++;
5107
5268
  }
5108
5269
  await addDependencies(projectRoot, { "@sentry/nextjs": "^8.0.0" });
5270
+ await wireRootLayout(projectRoot, ctx);
5109
5271
  await setConfigFlag12(projectRoot, ctx);
5110
5272
  await ctx.commit();
5111
- log.success(`Generated sentry feature with ${count} files`);
5273
+ log.success(`Generated and wired sentry feature (${count} files created)`);
5112
5274
  log.blank();
5113
5275
  log.step("src/features/sentry/ \u2014 types, client-init, server-init, ErrorBoundary");
5114
5276
  log.blank();
5277
+ log.step("Wired automatically:");
5278
+ log.step(" \u2713 ErrorBoundary wrapping app content in root layout");
5279
+ log.blank();
5115
5280
  log.warn("Optional: run npx @sentry/wizard@latest -i nextjs for full setup");
5116
5281
  log.blank();
5117
5282
  log.warn("Next steps:");
5118
5283
  log.step("Set NEXT_PUBLIC_SENTRY_DSN and SENTRY_DSN environment variables");
5119
- log.step("Wrap pages or layouts with <ErrorBoundary> for graceful error handling");
5120
5284
  log.step("Import client-init and server-init in your instrumentation files");
5121
5285
  log.blank();
5122
5286
  } catch (error) {
@@ -5124,13 +5288,27 @@ async function generateSentry(projectRoot) {
5124
5288
  throw error;
5125
5289
  }
5126
5290
  }
5291
+ async function wireRootLayout(projectRoot, ctx) {
5292
+ const layoutPath = path19.join(projectRoot, "src", "app", "layout.tsx");
5293
+ if (!await fs18.pathExists(layoutPath)) return;
5294
+ let content = await fs18.readFile(layoutPath, "utf-8");
5295
+ if (content.includes("ErrorBoundary")) return;
5296
+ await ctx.trackModifiedFile(layoutPath);
5297
+ content = `import { ErrorBoundary } from '@/features/sentry';
5298
+ ${content}`;
5299
+ content = content.replace(
5300
+ /(<Providers>)\{children\}(<\/Providers>)/,
5301
+ "$1<ErrorBoundary>{children}</ErrorBoundary>$2"
5302
+ );
5303
+ await fs18.writeFile(layoutPath, content);
5304
+ }
5127
5305
  async function setConfigFlag12(projectRoot, ctx) {
5128
- const configPath = path17.join(projectRoot, "src", "config", "app.config.ts");
5129
- if (!await fs17.pathExists(configPath)) return;
5306
+ const configPath = path19.join(projectRoot, "src", "config", "app.config.ts");
5307
+ if (!await fs18.pathExists(configPath)) return;
5130
5308
  await ctx.trackModifiedFile(configPath);
5131
- const content = await fs17.readFile(configPath, "utf-8");
5309
+ const content = await fs18.readFile(configPath, "utf-8");
5132
5310
  const updated = content.replace(/sentry:\s*false/, "sentry: true");
5133
- await fs17.writeFile(configPath, updated);
5311
+ await fs18.writeFile(configPath, updated);
5134
5312
  }
5135
5313
  function types11() {
5136
5314
  return `${STAMP12}
@@ -5275,11 +5453,11 @@ var feature_flags_exports = {};
5275
5453
  __export(feature_flags_exports, {
5276
5454
  generateFeatureFlags: () => generateFeatureFlags
5277
5455
  });
5278
- import fs18 from "fs-extra";
5279
- import path18 from "path";
5456
+ import fs19 from "fs-extra";
5457
+ import path20 from "path";
5280
5458
  async function generateFeatureFlags(projectRoot) {
5281
- const featureDir = path18.join(projectRoot, "src", "features", "feature-flags");
5282
- if (await fs18.pathExists(featureDir)) {
5459
+ const featureDir = path20.join(projectRoot, "src", "features", "feature-flags");
5460
+ if (await fs19.pathExists(featureDir)) {
5283
5461
  log.error("Feature flags feature already exists at src/features/feature-flags/");
5284
5462
  return;
5285
5463
  }
@@ -5292,16 +5470,16 @@ async function generateFeatureFlags(projectRoot) {
5292
5470
  "src/features/feature-flags/hooks/use-feature-flag.ts": useFeatureFlag(),
5293
5471
  "src/features/feature-flags/components/FeatureGate.tsx": featureGate(),
5294
5472
  "src/features/feature-flags/components/index.ts": componentsIndex3(),
5295
- "src/features/feature-flags/index.ts": featureIndex6(),
5473
+ "src/features/feature-flags/index.ts": featureIndex7(),
5296
5474
  "src/config/feature-flags.json": featureFlagsJson(),
5297
5475
  "src/app/api/protected/feature-flags/route.ts": apiRoute()
5298
5476
  };
5299
5477
  let count = 0;
5300
5478
  for (const [filePath, content] of Object.entries(files)) {
5301
- const fullPath = path18.join(projectRoot, filePath);
5479
+ const fullPath = path20.join(projectRoot, filePath);
5302
5480
  ctx.trackCreatedFile(fullPath);
5303
- await fs18.ensureDir(path18.dirname(fullPath));
5304
- await fs18.writeFile(fullPath, content);
5481
+ await fs19.ensureDir(path20.dirname(fullPath));
5482
+ await fs19.writeFile(fullPath, content);
5305
5483
  count++;
5306
5484
  }
5307
5485
  await setConfigFlag13(projectRoot, ctx);
@@ -5321,12 +5499,12 @@ async function generateFeatureFlags(projectRoot) {
5321
5499
  }
5322
5500
  }
5323
5501
  async function setConfigFlag13(projectRoot, ctx) {
5324
- const configPath = path18.join(projectRoot, "src", "config", "app.config.ts");
5325
- if (!await fs18.pathExists(configPath)) return;
5502
+ const configPath = path20.join(projectRoot, "src", "config", "app.config.ts");
5503
+ if (!await fs19.pathExists(configPath)) return;
5326
5504
  await ctx.trackModifiedFile(configPath);
5327
- const content = await fs18.readFile(configPath, "utf-8");
5505
+ const content = await fs19.readFile(configPath, "utf-8");
5328
5506
  const updated = content.replace(/featureFlags:\s*false/, "featureFlags: true");
5329
- await fs18.writeFile(configPath, updated);
5507
+ await fs19.writeFile(configPath, updated);
5330
5508
  }
5331
5509
  function types12() {
5332
5510
  return `${STAMP13}
@@ -5503,7 +5681,7 @@ function componentsIndex3() {
5503
5681
  export { FeatureGate } from './FeatureGate';
5504
5682
  `;
5505
5683
  }
5506
- function featureIndex6() {
5684
+ function featureIndex7() {
5507
5685
  return `${STAMP13}
5508
5686
  export { getFlag } from './server';
5509
5687
  export { useFeatureFlag } from './hooks/use-feature-flag';
@@ -5564,27 +5742,43 @@ var init_feature_flags = __esm({
5564
5742
  // src/index.ts
5565
5743
  import { Command } from "commander";
5566
5744
 
5745
+ // src/utils/version.ts
5746
+ import { createRequire } from "module";
5747
+ import path from "path";
5748
+ import { fileURLToPath } from "url";
5749
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
5750
+ function readVersionFromPackageJson(packageDir) {
5751
+ const require2 = createRequire(import.meta.url);
5752
+ const pkg = require2(path.join(packageDir, "package.json"));
5753
+ const v = pkg.version?.trim();
5754
+ return v ? v : "0.0.0";
5755
+ }
5756
+ function getCliVersion() {
5757
+ const packageRoot = path.resolve(__dirname, "..", "..");
5758
+ return readVersionFromPackageJson(packageRoot);
5759
+ }
5760
+
5567
5761
  // src/commands/create.ts
5568
5762
  init_logger();
5569
- import fs20 from "fs-extra";
5570
- import path20 from "path";
5763
+ import fs21 from "fs-extra";
5764
+ import path22 from "path";
5571
5765
  import os3 from "os";
5572
5766
  import ora from "ora";
5573
5767
  import pc2 from "picocolors";
5574
5768
  import prompts5 from "prompts";
5575
5769
 
5576
5770
  // src/utils/template.ts
5577
- import path from "path";
5578
- import { fileURLToPath } from "url";
5771
+ import path2 from "path";
5772
+ import { fileURLToPath as fileURLToPath2 } from "url";
5579
5773
  import fs from "fs-extra";
5580
- var __dirname = path.dirname(fileURLToPath(import.meta.url));
5774
+ var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
5581
5775
  function getTemplatePath() {
5582
5776
  const candidates = [
5583
5777
  // Built bundle: __dirname = packages/cli/dist/
5584
- path.resolve(__dirname, "..", "template"),
5585
- path.resolve(__dirname, "..", "..", "..", "template"),
5778
+ path2.resolve(__dirname2, "..", "template"),
5779
+ path2.resolve(__dirname2, "..", "..", "..", "template"),
5586
5780
  // Source (vitest): __dirname = packages/cli/src/utils/
5587
- path.resolve(__dirname, "..", "..", "..", "..", "template")
5781
+ path2.resolve(__dirname2, "..", "..", "..", "..", "template")
5588
5782
  ];
5589
5783
  for (const candidate of candidates) {
5590
5784
  if (fs.existsSync(candidate)) {
@@ -5596,7 +5790,7 @@ function getTemplatePath() {
5596
5790
  );
5597
5791
  }
5598
5792
  function resolveProjectPath(projectName) {
5599
- return path.resolve(process.cwd(), projectName);
5793
+ return path2.resolve(process.cwd(), projectName);
5600
5794
  }
5601
5795
 
5602
5796
  // src/prompts/project-info.ts
@@ -5946,7 +6140,6 @@ var DESIGN_DIRECTIONS = [
5946
6140
  function getDefaultTheme() {
5947
6141
  return {
5948
6142
  primaryColor: COLOR_PRESETS[0].value,
5949
- secondaryColor: "amber-400",
5950
6143
  font: FONT_CHOICES[0].value,
5951
6144
  designDirection: DESIGN_DIRECTIONS[0].value
5952
6145
  };
@@ -5987,7 +6180,6 @@ async function promptTheme() {
5987
6180
  if (response.primaryColor === void 0) return null;
5988
6181
  return {
5989
6182
  primaryColor: response.primaryColor,
5990
- secondaryColor: "amber-400",
5991
6183
  font: response.font,
5992
6184
  designDirection: response.designDirection
5993
6185
  };
@@ -5996,7 +6188,7 @@ async function promptTheme() {
5996
6188
  // src/generators/scaffold.ts
5997
6189
  init_logger();
5998
6190
  import fs2 from "fs-extra";
5999
- import path2 from "path";
6191
+ import path3 from "path";
6000
6192
  import { generateBrandCss } from "@mars-stack/ui/utils";
6001
6193
 
6002
6194
  // src/generators/app-config.ts
@@ -6039,15 +6231,7 @@ function generateAppConfig(config) {
6039
6231
  address: '',
6040
6232
  },
6041
6233
  theme: {
6042
- primaryColor: '${config.theme.primaryColor}' as string,
6043
- secondaryColor: '${config.theme.secondaryColor}' as string,
6044
6234
  font: '${config.theme.font}' as string,
6045
- designDirection: '${config.theme.designDirection}' as
6046
- | 'modern-saas'
6047
- | 'minimal'
6048
- | 'enterprise'
6049
- | 'creative'
6050
- | 'dashboard',
6051
6235
  },
6052
6236
  features: {
6053
6237
  ${featureEntries}
@@ -6190,8 +6374,8 @@ async function copyTemplateFiles(templateDir, targetDir) {
6190
6374
  async function walkAndCopy(src, dest) {
6191
6375
  const entries = await fs2.readdir(src, { withFileTypes: true });
6192
6376
  for (const entry of entries) {
6193
- const srcPath = path2.join(src, entry.name);
6194
- const destPath = path2.join(dest, entry.name);
6377
+ const srcPath = path3.join(src, entry.name);
6378
+ const destPath = path3.join(dest, entry.name);
6195
6379
  if (entry.isDirectory()) {
6196
6380
  if (IGNORED_DIRS.includes(entry.name)) continue;
6197
6381
  await fs2.ensureDir(destPath);
@@ -6209,8 +6393,8 @@ async function copyTemplateFiles(templateDir, targetDir) {
6209
6393
  function resolveMarsPackageRange(packageName, templateDir) {
6210
6394
  const shortName = packageName.replace("@mars-stack/", "");
6211
6395
  const candidates = [
6212
- path2.resolve(templateDir, "..", "packages", shortName, "package.json"),
6213
- path2.resolve(templateDir, "..", "..", "packages", shortName, "package.json")
6396
+ path3.resolve(templateDir, "..", "packages", shortName, "package.json"),
6397
+ path3.resolve(templateDir, "..", "..", "packages", shortName, "package.json")
6214
6398
  ];
6215
6399
  for (const candidate of candidates) {
6216
6400
  if (fs2.existsSync(candidate)) {
@@ -6221,7 +6405,7 @@ function resolveMarsPackageRange(packageName, templateDir) {
6221
6405
  }
6222
6406
  }
6223
6407
  }
6224
- const templatePkg = fs2.readJsonSync(path2.join(templateDir, "package.json"));
6408
+ const templatePkg = fs2.readJsonSync(path3.join(templateDir, "package.json"));
6225
6409
  const existing = templatePkg.dependencies?.[packageName];
6226
6410
  if (existing && existing !== "*") {
6227
6411
  if (/^[\^~>=]/.test(existing)) return existing;
@@ -6232,7 +6416,7 @@ function resolveMarsPackageRange(packageName, templateDir) {
6232
6416
  }
6233
6417
  function generatePackageJson(config) {
6234
6418
  const templateDir = getTemplatePath();
6235
- const templatePkg = path2.join(templateDir, "package.json");
6419
+ const templatePkg = path3.join(templateDir, "package.json");
6236
6420
  const pkg = fs2.readJsonSync(templatePkg);
6237
6421
  pkg.name = config.name;
6238
6422
  pkg.version = "0.1.0";
@@ -6432,7 +6616,7 @@ async function pruneDisabledFeatures(features, targetDir) {
6432
6616
  const flagValue = features[feature];
6433
6617
  if (flagValue !== false) continue;
6434
6618
  for (const relativePath of paths) {
6435
- const fullPath = path2.join(targetDir, relativePath);
6619
+ const fullPath = path3.join(targetDir, relativePath);
6436
6620
  if (await fs2.pathExists(fullPath)) {
6437
6621
  await fs2.remove(fullPath);
6438
6622
  log.step(`Pruned disabled feature path: ${relativePath}`);
@@ -6443,7 +6627,7 @@ async function pruneDisabledFeatures(features, targetDir) {
6443
6627
  }
6444
6628
  }
6445
6629
  if (relationsToRemove.length > 0) {
6446
- const authPath = path2.join(targetDir, "prisma", "schema", "auth.prisma");
6630
+ const authPath = path3.join(targetDir, "prisma", "schema", "auth.prisma");
6447
6631
  if (await fs2.pathExists(authPath)) {
6448
6632
  let content = await fs2.readFile(authPath, "utf-8");
6449
6633
  for (const field of relationsToRemove) {
@@ -6461,22 +6645,22 @@ async function scaffoldProject(targetDir, config) {
6461
6645
  await fs2.ensureDir(targetDir);
6462
6646
  const fileCount = await copyTemplateFiles(templateDir, targetDir);
6463
6647
  await pruneDisabledFeatures(config.features, targetDir);
6464
- await fs2.writeFile(path2.join(targetDir, "package.json"), generatePackageJson(config));
6648
+ await fs2.writeFile(path3.join(targetDir, "package.json"), generatePackageJson(config));
6465
6649
  await fs2.writeFile(
6466
- path2.join(targetDir, "src", "config", "app.config.ts"),
6650
+ path3.join(targetDir, "src", "config", "app.config.ts"),
6467
6651
  generateAppConfig(config)
6468
6652
  );
6469
- await fs2.writeFile(path2.join(targetDir, "docker-compose.yml"), generateDockerCompose(config));
6470
- await fs2.writeFile(path2.join(targetDir, "src", "app", "layout.tsx"), generateLayout(config));
6471
- await fs2.writeFile(path2.join(targetDir, ".env"), generateEnvFile(config));
6472
- await fs2.writeFile(path2.join(targetDir, ".env.example"), generateEnvExample(config));
6473
- await fs2.writeFile(path2.join(targetDir, ".gitignore"), generateGitignore());
6653
+ await fs2.writeFile(path3.join(targetDir, "docker-compose.yml"), generateDockerCompose(config));
6654
+ await fs2.writeFile(path3.join(targetDir, "src", "app", "layout.tsx"), generateLayout(config));
6655
+ await fs2.writeFile(path3.join(targetDir, ".env"), generateEnvFile(config));
6656
+ await fs2.writeFile(path3.join(targetDir, ".env.example"), generateEnvExample(config));
6657
+ await fs2.writeFile(path3.join(targetDir, ".gitignore"), generateGitignore());
6474
6658
  await patchGlobalsCssForScaffoldedProject(targetDir, config);
6475
6659
  await generateBrandCssForProject(targetDir, config);
6476
6660
  return { fileCount };
6477
6661
  }
6478
6662
  async function generateBrandCssForProject(targetDir, config) {
6479
- const brandPath = path2.join(targetDir, "src", "styles", "brand.css");
6663
+ const brandPath = path3.join(targetDir, "src", "styles", "brand.css");
6480
6664
  if (!await fs2.pathExists(brandPath)) return;
6481
6665
  const css = generateBrandCss(config.theme.primaryColor);
6482
6666
  await fs2.writeFile(brandPath, css);
@@ -6489,7 +6673,7 @@ var VALID_DESIGN_DIRECTIONS = [
6489
6673
  "dashboard"
6490
6674
  ];
6491
6675
  async function patchGlobalsCssForScaffoldedProject(targetDir, config) {
6492
- const globalsPath = path2.join(targetDir, "src", "styles", "globals.css");
6676
+ const globalsPath = path3.join(targetDir, "src", "styles", "globals.css");
6493
6677
  if (!await fs2.pathExists(globalsPath)) return;
6494
6678
  let content = await fs2.readFile(globalsPath, "utf-8");
6495
6679
  content = content.replace(
@@ -6626,13 +6810,13 @@ async function generateSelectedFeatures(projectRoot, features) {
6626
6810
  }
6627
6811
 
6628
6812
  // src/utils/telemetry.ts
6629
- import fs19 from "fs-extra";
6630
- import path19 from "path";
6813
+ import fs20 from "fs-extra";
6814
+ import path21 from "path";
6631
6815
  import os2 from "os";
6632
- var RC_PATH = path19.join(os2.homedir(), ".marsrc");
6816
+ var RC_PATH = path21.join(os2.homedir(), ".marsrc");
6633
6817
  function isTelemetryEnabled() {
6634
6818
  try {
6635
- const config = fs19.readJsonSync(RC_PATH);
6819
+ const config = fs20.readJsonSync(RC_PATH);
6636
6820
  return config.enabled === true;
6637
6821
  } catch {
6638
6822
  return false;
@@ -6641,16 +6825,16 @@ function isTelemetryEnabled() {
6641
6825
  function enableTelemetry() {
6642
6826
  const config = loadOrCreateConfig();
6643
6827
  config.enabled = true;
6644
- fs19.writeJsonSync(RC_PATH, config, { spaces: 2 });
6828
+ fs20.writeJsonSync(RC_PATH, config, { spaces: 2 });
6645
6829
  }
6646
6830
  function disableTelemetry() {
6647
6831
  const config = loadOrCreateConfig();
6648
6832
  config.enabled = false;
6649
- fs19.writeJsonSync(RC_PATH, config, { spaces: 2 });
6833
+ fs20.writeJsonSync(RC_PATH, config, { spaces: 2 });
6650
6834
  }
6651
6835
  function loadOrCreateConfig() {
6652
6836
  try {
6653
- return fs19.readJsonSync(RC_PATH);
6837
+ return fs20.readJsonSync(RC_PATH);
6654
6838
  } catch {
6655
6839
  return { enabled: false, anonymousId: crypto.randomUUID() };
6656
6840
  }
@@ -6670,13 +6854,13 @@ function trackEvent(event, properties) {
6670
6854
  },
6671
6855
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6672
6856
  };
6673
- const logPath = path19.join(os2.homedir(), ".mars-telemetry.log");
6674
- fs19.appendFile(logPath, JSON.stringify(payload) + "\n").catch(() => {
6857
+ const logPath = path21.join(os2.homedir(), ".mars-telemetry.log");
6858
+ fs20.appendFile(logPath, JSON.stringify(payload) + "\n").catch(() => {
6675
6859
  });
6676
6860
  }
6677
6861
 
6678
6862
  // src/commands/create.ts
6679
- var RC_PATH2 = path20.join(os3.homedir(), ".marsrc");
6863
+ var RC_PATH2 = path22.join(os3.homedir(), ".marsrc");
6680
6864
  var PROJECT_NAME_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
6681
6865
  var MAX_PROJECT_NAME_LENGTH = 214;
6682
6866
  async function createCommand(projectName, options) {
@@ -6703,8 +6887,8 @@ async function createCommand(projectName, options) {
6703
6887
  const projectInfo = useDefaults ? getDefaultProjectInfo(projectName) : await promptProjectInfo(projectName);
6704
6888
  if (!projectInfo) return;
6705
6889
  const targetDir = resolveProjectPath(projectInfo.name);
6706
- if (await fs20.pathExists(targetDir)) {
6707
- const entries = await fs20.readdir(targetDir);
6890
+ if (await fs21.pathExists(targetDir)) {
6891
+ const entries = await fs21.readdir(targetDir);
6708
6892
  if (entries.length > 0) {
6709
6893
  log.error(`Directory "${projectInfo.name}" already exists and is not empty.`);
6710
6894
  return;
@@ -6745,7 +6929,7 @@ async function createCommand(projectName, options) {
6745
6929
  log.info("Scaffolding cancelled.");
6746
6930
  process.exit(0);
6747
6931
  }
6748
- if (!fs20.pathExistsSync(RC_PATH2)) {
6932
+ if (!fs21.pathExistsSync(RC_PATH2)) {
6749
6933
  const { telemetryOptIn } = await prompts5(
6750
6934
  {
6751
6935
  type: "confirm",
@@ -6784,10 +6968,10 @@ async function createCommand(projectName, options) {
6784
6968
  } catch (err) {
6785
6969
  spinner.fail("Failed to scaffold project");
6786
6970
  log.error(err instanceof Error ? err.message : String(err));
6787
- if (await fs20.pathExists(targetDir)) {
6971
+ if (await fs21.pathExists(targetDir)) {
6788
6972
  const cleanupSpinner = ora("Cleaning up...").start();
6789
6973
  try {
6790
- await fs20.remove(targetDir);
6974
+ await fs21.remove(targetDir);
6791
6975
  cleanupSpinner.succeed("Cleaned up partial scaffold");
6792
6976
  } catch {
6793
6977
  cleanupSpinner.warn(`Could not clean up ${targetDir}. You may need to remove it manually.`);
@@ -6802,8 +6986,8 @@ function countEnabled(flags) {
6802
6986
 
6803
6987
  // src/commands/doctor.ts
6804
6988
  init_logger();
6805
- import fs21 from "fs-extra";
6806
- import path21 from "path";
6989
+ import fs22 from "fs-extra";
6990
+ import path23 from "path";
6807
6991
  import { execSync } from "child_process";
6808
6992
  function commandExists(cmd) {
6809
6993
  try {
@@ -6823,12 +7007,12 @@ function getVersion(cmd) {
6823
7007
  async function checkForUpgrades() {
6824
7008
  log.blank();
6825
7009
  log.title("Upgrade Check");
6826
- const packageJsonPath = path21.join(process.cwd(), "package.json");
6827
- if (!fs21.pathExistsSync(packageJsonPath)) {
7010
+ const packageJsonPath = path23.join(process.cwd(), "package.json");
7011
+ if (!fs22.pathExistsSync(packageJsonPath)) {
6828
7012
  log.warn("No package.json found \u2014 skipping upgrade check.");
6829
7013
  return;
6830
7014
  }
6831
- const packageJson = fs21.readJsonSync(packageJsonPath);
7015
+ const packageJson = fs22.readJsonSync(packageJsonPath);
6832
7016
  const deps = packageJson.dependencies ?? {};
6833
7017
  const devDeps = packageJson.devDependencies ?? {};
6834
7018
  const currentRaw = deps["@mars-stack/core"] ?? devDeps["@mars-stack/core"];
@@ -6890,25 +7074,25 @@ async function doctorCommand(options) {
6890
7074
  },
6891
7075
  {
6892
7076
  name: "package.json exists",
6893
- check: () => fs21.pathExistsSync(path21.join(process.cwd(), "package.json"))
7077
+ check: () => fs22.pathExistsSync(path23.join(process.cwd(), "package.json"))
6894
7078
  },
6895
7079
  {
6896
7080
  name: ".env file exists",
6897
- check: () => fs21.pathExistsSync(path21.join(process.cwd(), ".env"))
7081
+ check: () => fs22.pathExistsSync(path23.join(process.cwd(), ".env"))
6898
7082
  },
6899
7083
  {
6900
7084
  name: "Prisma schema exists",
6901
- check: () => fs21.pathExistsSync(path21.join(process.cwd(), "prisma", "schema")) || fs21.pathExistsSync(path21.join(process.cwd(), "prisma", "schema.prisma"))
7085
+ check: () => fs22.pathExistsSync(path23.join(process.cwd(), "prisma", "schema")) || fs22.pathExistsSync(path23.join(process.cwd(), "prisma", "schema.prisma"))
6902
7086
  },
6903
7087
  {
6904
7088
  name: "node_modules installed",
6905
- check: () => fs21.pathExistsSync(path21.join(process.cwd(), "node_modules"))
7089
+ check: () => fs22.pathExistsSync(path23.join(process.cwd(), "node_modules"))
6906
7090
  },
6907
7091
  {
6908
7092
  name: "DATABASE_URL set",
6909
7093
  check: () => {
6910
7094
  try {
6911
- const envContent = fs21.readFileSync(path21.join(process.cwd(), ".env"), "utf-8");
7095
+ const envContent = fs22.readFileSync(path23.join(process.cwd(), ".env"), "utf-8");
6912
7096
  const match = envContent.match(/^DATABASE_URL=(.+)$/m);
6913
7097
  return match ? match[1] !== "" : false;
6914
7098
  } catch {
@@ -6920,7 +7104,7 @@ async function doctorCommand(options) {
6920
7104
  name: "JWT_SECRET set",
6921
7105
  check: () => {
6922
7106
  try {
6923
- const envContent = fs21.readFileSync(path21.join(process.cwd(), ".env"), "utf-8");
7107
+ const envContent = fs22.readFileSync(path23.join(process.cwd(), ".env"), "utf-8");
6924
7108
  const match = envContent.match(/^JWT_SECRET=(.+)$/m);
6925
7109
  if (!match || !match[1]) return false;
6926
7110
  return match[1].length >= 32 ? true : "set but too short (need >=32 chars)";
@@ -6965,16 +7149,16 @@ async function doctorCommand(options) {
6965
7149
 
6966
7150
  // src/commands/add.ts
6967
7151
  init_logger();
6968
- import fs23 from "fs-extra";
6969
- import path23 from "path";
7152
+ import fs24 from "fs-extra";
7153
+ import path25 from "path";
6970
7154
  import pc3 from "picocolors";
6971
7155
 
6972
7156
  // src/utils/doc-updater.ts
6973
- import fs22 from "fs-extra";
6974
- import path22 from "path";
7157
+ import fs23 from "fs-extra";
7158
+ import path24 from "path";
6975
7159
  async function appendToDbSchema(projectDir, modelName, fields) {
6976
- const filePath = path22.join(projectDir, "docs", "generated", "db-schema.md");
6977
- if (!await fs22.pathExists(filePath)) return;
7160
+ const filePath = path24.join(projectDir, "docs", "generated", "db-schema.md");
7161
+ if (!await fs23.pathExists(filePath)) return;
6978
7162
  const entry = [
6979
7163
  "",
6980
7164
  `### ${modelName}`,
@@ -6984,12 +7168,12 @@ async function appendToDbSchema(projectDir, modelName, fields) {
6984
7168
  ...fields.map((f) => `| ${f} | |`),
6985
7169
  ""
6986
7170
  ].join("\n");
6987
- await fs22.appendFile(filePath, entry);
7171
+ await fs23.appendFile(filePath, entry);
6988
7172
  }
6989
7173
  async function updateQualityScore(projectDir, domain, grade) {
6990
- const filePath = path22.join(projectDir, "docs", "QUALITY_SCORE.md");
6991
- if (!await fs22.pathExists(filePath)) return;
6992
- const content = await fs22.readFile(filePath, "utf-8");
7174
+ const filePath = path24.join(projectDir, "docs", "QUALITY_SCORE.md");
7175
+ if (!await fs23.pathExists(filePath)) return;
7176
+ const content = await fs23.readFile(filePath, "utf-8");
6993
7177
  const pattern = new RegExp(`^\\|\\s*${escapeRegex(domain)}\\s*\\|`, "m");
6994
7178
  if (pattern.test(content)) {
6995
7179
  const updated = content.replace(pattern, (match) => {
@@ -6997,11 +7181,11 @@ async function updateQualityScore(projectDir, domain, grade) {
6997
7181
  parts[2] = ` ${grade} `;
6998
7182
  return parts.join("|");
6999
7183
  });
7000
- await fs22.writeFile(filePath, updated);
7184
+ await fs23.writeFile(filePath, updated);
7001
7185
  } else {
7002
7186
  const row = `| ${domain} | ${grade} | |
7003
7187
  `;
7004
- await fs22.appendFile(filePath, row);
7188
+ await fs23.appendFile(filePath, row);
7005
7189
  }
7006
7190
  }
7007
7191
  function escapeRegex(str) {
@@ -7012,8 +7196,8 @@ function escapeRegex(str) {
7012
7196
  init_rollback();
7013
7197
  function ensureInProject() {
7014
7198
  const cwd = process.cwd();
7015
- const configPath = path23.join(cwd, "src", "config", "app.config.ts");
7016
- if (!fs23.pathExistsSync(configPath)) {
7199
+ const configPath = path25.join(cwd, "src", "config", "app.config.ts");
7200
+ if (!fs24.pathExistsSync(configPath)) {
7017
7201
  log.error("Not inside a MARS project. Run this command from the project root.");
7018
7202
  process.exit(1);
7019
7203
  }
@@ -7046,8 +7230,8 @@ async function addFeatureCommand(name) {
7046
7230
  const kebab = toKebab(name);
7047
7231
  const pascal = toPascal(name);
7048
7232
  const camel = toCamel(name);
7049
- const featureDir = path23.join(root, "src", "features", kebab);
7050
- if (await fs23.pathExists(featureDir)) {
7233
+ const featureDir = path25.join(root, "src", "features", kebab);
7234
+ if (await fs24.pathExists(featureDir)) {
7051
7235
  log.error(`Feature "${kebab}" already exists at src/features/${kebab}/`);
7052
7236
  return;
7053
7237
  }
@@ -7112,9 +7296,9 @@ export type Update${pascal}Input = z.infer<typeof ${camel}Schemas.update>;
7112
7296
  ctx.trackCreatedFile(featureDir);
7113
7297
  let count = 0;
7114
7298
  for (const [filePath, content] of Object.entries(files)) {
7115
- const fullPath = path23.join(featureDir, filePath);
7116
- await fs23.ensureDir(path23.dirname(fullPath));
7117
- await fs23.writeFile(fullPath, content);
7299
+ const fullPath = path25.join(featureDir, filePath);
7300
+ await fs24.ensureDir(path25.dirname(fullPath));
7301
+ await fs24.writeFile(fullPath, content);
7118
7302
  count++;
7119
7303
  }
7120
7304
  await ctx.commit();
@@ -7135,8 +7319,8 @@ async function addPageCommand(routePath, options) {
7135
7319
  const root = ensureInProject();
7136
7320
  const cleanPath = routePath.startsWith("/") ? routePath.slice(1) : routePath;
7137
7321
  const group = options.protected ? "(protected)" : "(public)";
7138
- const pageDir = path23.join(root, "src", "app", group, cleanPath);
7139
- if (await fs23.pathExists(path23.join(pageDir, "page.tsx"))) {
7322
+ const pageDir = path25.join(root, "src", "app", group, cleanPath);
7323
+ if (await fs24.pathExists(path25.join(pageDir, "page.tsx"))) {
7140
7324
  log.error(`Page already exists at src/app/${group}/${cleanPath}/page.tsx`);
7141
7325
  return;
7142
7326
  }
@@ -7184,9 +7368,9 @@ export default function Loading() {
7184
7368
  const ctx = createRollbackContext();
7185
7369
  try {
7186
7370
  ctx.trackCreatedFile(pageDir);
7187
- await fs23.ensureDir(pageDir);
7188
- await fs23.writeFile(path23.join(pageDir, "page.tsx"), pageContent);
7189
- await fs23.writeFile(path23.join(pageDir, "loading.tsx"), loadingContent);
7371
+ await fs24.ensureDir(pageDir);
7372
+ await fs24.writeFile(path25.join(pageDir, "page.tsx"), pageContent);
7373
+ await fs24.writeFile(path25.join(pageDir, "loading.tsx"), loadingContent);
7190
7374
  await ctx.commit();
7191
7375
  log.success(`Created page at ${pc3.bold(`src/app/${group}/${cleanPath}/`)}`);
7192
7376
  trackEvent("add", { type: "page" });
@@ -7203,9 +7387,9 @@ async function addModelCommand(name) {
7203
7387
  const pascal = toPascal(name);
7204
7388
  const camel = toCamel(name);
7205
7389
  const kebab = toKebab(name);
7206
- const schemaDir = path23.join(root, "prisma", "schema");
7207
- const schemaFile = path23.join(schemaDir, `${kebab}.prisma`);
7208
- if (await fs23.pathExists(schemaFile)) {
7390
+ const schemaDir = path25.join(root, "prisma", "schema");
7391
+ const schemaFile = path25.join(schemaDir, `${kebab}.prisma`);
7392
+ if (await fs24.pathExists(schemaFile)) {
7209
7393
  log.error(`Schema file already exists: prisma/schema/${kebab}.prisma`);
7210
7394
  return;
7211
7395
  }
@@ -7223,8 +7407,8 @@ async function addModelCommand(name) {
7223
7407
  const ctx = createRollbackContext();
7224
7408
  try {
7225
7409
  ctx.trackCreatedFile(schemaFile);
7226
- await fs23.ensureDir(schemaDir);
7227
- await fs23.writeFile(schemaFile, content);
7410
+ await fs24.ensureDir(schemaDir);
7411
+ await fs24.writeFile(schemaFile, content);
7228
7412
  await ctx.commit();
7229
7413
  log.success(`Created model ${pc3.bold(pascal)} at prisma/schema/${kebab}.prisma`);
7230
7414
  trackEvent("add", { type: "model" });
@@ -7246,9 +7430,9 @@ async function addEmailCommand(name) {
7246
7430
  const kebab = toKebab(name);
7247
7431
  const pascal = toPascal(name);
7248
7432
  const camel = toCamel(name);
7249
- const templatesDir = path23.join(root, "src", "lib", "core", "email", "templates");
7250
- const filePath = path23.join(templatesDir, `${kebab}-email.ts`);
7251
- if (await fs23.pathExists(filePath)) {
7433
+ const templatesDir = path25.join(root, "src", "lib", "core", "email", "templates");
7434
+ const filePath = path25.join(templatesDir, `${kebab}-email.ts`);
7435
+ if (await fs24.pathExists(filePath)) {
7252
7436
  log.error(`Email template already exists: src/lib/core/email/templates/${kebab}-email.ts`);
7253
7437
  return;
7254
7438
  }
@@ -7292,25 +7476,25 @@ export function ${functionName}({ appName, actionUrl, userName }: ${pascal}Email
7292
7476
  }
7293
7477
  `;
7294
7478
  const ctx = createRollbackContext();
7295
- const indexPath = path23.join(templatesDir, "index.ts");
7479
+ const indexPath = path25.join(templatesDir, "index.ts");
7296
7480
  try {
7297
7481
  ctx.trackCreatedFile(filePath);
7298
- if (await fs23.pathExists(indexPath)) {
7482
+ if (await fs24.pathExists(indexPath)) {
7299
7483
  await ctx.trackModifiedFile(indexPath);
7300
7484
  } else {
7301
7485
  ctx.trackCreatedFile(indexPath);
7302
7486
  }
7303
- await fs23.ensureDir(templatesDir);
7304
- await fs23.writeFile(filePath, content);
7487
+ await fs24.ensureDir(templatesDir);
7488
+ await fs24.writeFile(filePath, content);
7305
7489
  const exportLine = `export { ${functionName} } from './${kebab}-email';
7306
7490
  `;
7307
- if (await fs23.pathExists(indexPath)) {
7308
- const existing = await fs23.readFile(indexPath, "utf-8");
7491
+ if (await fs24.pathExists(indexPath)) {
7492
+ const existing = await fs24.readFile(indexPath, "utf-8");
7309
7493
  if (!existing.includes(functionName)) {
7310
- await fs23.appendFile(indexPath, exportLine);
7494
+ await fs24.appendFile(indexPath, exportLine);
7311
7495
  }
7312
7496
  } else {
7313
- await fs23.writeFile(indexPath, exportLine);
7497
+ await fs24.writeFile(indexPath, exportLine);
7314
7498
  }
7315
7499
  await ctx.commit();
7316
7500
  log.success(`Created email template ${pc3.bold(kebab)} at src/lib/core/email/templates/${kebab}-email.ts`);
@@ -7333,10 +7517,10 @@ async function addComponentCommand(name, options) {
7333
7517
  log.error(`Invalid type "${type}". Use: ${validTypes.join(", ")}`);
7334
7518
  return;
7335
7519
  }
7336
- const dir = type === "primitive" ? path23.join(root, "src", "components", "primitives") : path23.join(root, "src", "components", "patterns");
7337
- await fs23.ensureDir(dir);
7338
- const filePath = path23.join(dir, `${pascal}.tsx`);
7339
- if (await fs23.pathExists(filePath)) {
7520
+ const dir = type === "primitive" ? path25.join(root, "src", "components", "primitives") : path25.join(root, "src", "components", "patterns");
7521
+ await fs24.ensureDir(dir);
7522
+ const filePath = path25.join(dir, `${pascal}.tsx`);
7523
+ if (await fs24.pathExists(filePath)) {
7340
7524
  log.error(`Component already exists: ${pascal}.tsx`);
7341
7525
  return;
7342
7526
  }
@@ -7385,9 +7569,9 @@ export function ${pascal}({ children, className }: ${pascal}Props) {
7385
7569
  const ctx = createRollbackContext();
7386
7570
  try {
7387
7571
  ctx.trackCreatedFile(filePath);
7388
- await fs23.writeFile(filePath, content);
7572
+ await fs24.writeFile(filePath, content);
7389
7573
  await ctx.commit();
7390
- log.success(`Created ${type} component ${pc3.bold(pascal)} at ${path23.relative(root, filePath)}`);
7574
+ log.success(`Created ${type} component ${pc3.bold(pascal)} at ${path25.relative(root, filePath)}`);
7391
7575
  trackEvent("add", { type: "component", componentType: type });
7392
7576
  log.blank();
7393
7577
  log.warn(`Remember to add the export to the barrel file:`);
@@ -7402,14 +7586,14 @@ export function ${pascal}({ children, className }: ${pascal}Props) {
7402
7586
  // src/commands/configure.ts
7403
7587
  init_logger();
7404
7588
  import { execSync as execSync2 } from "child_process";
7405
- import fs24 from "fs-extra";
7406
- import path24 from "path";
7589
+ import fs25 from "fs-extra";
7590
+ import path26 from "path";
7407
7591
  import pc4 from "picocolors";
7408
7592
  import prompts6 from "prompts";
7409
7593
  function ensureInProject2() {
7410
7594
  const cwd = process.cwd();
7411
- const configPath = path24.join(cwd, "src", "config", "app.config.ts");
7412
- if (!fs24.pathExistsSync(configPath)) {
7595
+ const configPath = path26.join(cwd, "src", "config", "app.config.ts");
7596
+ if (!fs25.pathExistsSync(configPath)) {
7413
7597
  log.error("Not inside a MARS project. Run this command from the project root.");
7414
7598
  process.exit(1);
7415
7599
  }
@@ -7423,8 +7607,8 @@ var PROVIDER_DEPENDENCIES = {
7423
7607
  "storage:s3": ["@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner"]
7424
7608
  };
7425
7609
  function updateAppConfig(projectDir, serviceKey, provider, featureKey) {
7426
- const configPath = path24.join(projectDir, "src", "config", "app.config.ts");
7427
- let content = fs24.readFileSync(configPath, "utf-8");
7610
+ const configPath = path26.join(projectDir, "src", "config", "app.config.ts");
7611
+ let content = fs25.readFileSync(configPath, "utf-8");
7428
7612
  if (serviceKey === "auth") {
7429
7613
  const boolValue = provider === "google" ? "true" : "false";
7430
7614
  const featureRegex = new RegExp(`(googleOAuth\\s*:\\s*)(?:true|false)`);
@@ -7439,10 +7623,10 @@ function updateAppConfig(projectDir, serviceKey, provider, featureKey) {
7439
7623
  content = content.replace(featureRegex, `$1true`);
7440
7624
  }
7441
7625
  }
7442
- fs24.writeFileSync(configPath, content);
7626
+ fs25.writeFileSync(configPath, content);
7443
7627
  }
7444
7628
  function detectPackageManager(projectDir) {
7445
- if (fs24.existsSync(path24.join(projectDir, "yarn.lock"))) return "yarn";
7629
+ if (fs25.existsSync(path26.join(projectDir, "yarn.lock"))) return "yarn";
7446
7630
  return "npm";
7447
7631
  }
7448
7632
  function installDependencies(projectDir, deps) {
@@ -7552,15 +7736,15 @@ async function configureCommand(service) {
7552
7736
  log.step(`Manually set ${pc4.bold(`services.${serviceConfig.configKey}.provider`)} to ${pc4.bold(`'${provider}'`)} in ${pc4.dim("src/config/app.config.ts")}`);
7553
7737
  }
7554
7738
  if (selectedProvider.envVars.length > 0) {
7555
- const envPath = path24.join(root, ".env");
7556
- if (await fs24.pathExists(envPath)) {
7557
- const envContent = await fs24.readFile(envPath, "utf-8");
7739
+ const envPath = path26.join(root, ".env");
7740
+ if (await fs25.pathExists(envPath)) {
7741
+ const envContent = await fs25.readFile(envPath, "utf-8");
7558
7742
  const missingVars = selectedProvider.envVars.filter(
7559
7743
  (v) => !envContent.includes(v)
7560
7744
  );
7561
7745
  if (missingVars.length > 0) {
7562
7746
  const additions = missingVars.map((v) => `${v}=""`).join("\n");
7563
- await fs24.appendFile(envPath, `
7747
+ await fs25.appendFile(envPath, `
7564
7748
  # ${selectedService} (${provider})
7565
7749
  ${additions}
7566
7750
  `);
@@ -7589,15 +7773,15 @@ ${additions}
7589
7773
 
7590
7774
  // src/commands/deploy.ts
7591
7775
  init_logger();
7592
- import fs25 from "fs-extra";
7593
- import path25 from "path";
7776
+ import fs26 from "fs-extra";
7777
+ import path27 from "path";
7594
7778
  import { execSync as execSync3 } from "child_process";
7595
7779
  import pc5 from "picocolors";
7596
7780
  import prompts7 from "prompts";
7597
7781
  function ensureInProject3() {
7598
7782
  const cwd = process.cwd();
7599
- const configPath = path25.join(cwd, "src", "config", "app.config.ts");
7600
- if (!fs25.pathExistsSync(configPath)) {
7783
+ const configPath = path27.join(cwd, "src", "config", "app.config.ts");
7784
+ if (!fs26.pathExistsSync(configPath)) {
7601
7785
  log.error("Not inside a MARS project. Run this command from the project root.");
7602
7786
  process.exit(1);
7603
7787
  }
@@ -7636,8 +7820,8 @@ async function deployCommand() {
7636
7820
  return;
7637
7821
  }
7638
7822
  }
7639
- const vercelDir = path25.join(root, ".vercel");
7640
- if (!fs25.existsSync(vercelDir)) {
7823
+ const vercelDir = path27.join(root, ".vercel");
7824
+ if (!fs26.existsSync(vercelDir)) {
7641
7825
  log.step("Linking project to Vercel...");
7642
7826
  try {
7643
7827
  execSync3("vercel link", { cwd: root, stdio: "inherit" });
@@ -7654,8 +7838,8 @@ async function deployCommand() {
7654
7838
  log.blank();
7655
7839
  const requiredVars = ["JWT_SECRET", "DATABASE_URL"];
7656
7840
  log.step(`Core: ${pc5.dim(requiredVars.join(", "))}`);
7657
- const configPath = path25.join(root, "src", "config", "app.config.ts");
7658
- const configContent = await fs25.readFile(configPath, "utf-8");
7841
+ const configPath = path27.join(root, "src", "config", "app.config.ts");
7842
+ const configContent = await fs26.readFile(configPath, "utf-8");
7659
7843
  if (configContent.includes("email: { provider: 'sendgrid'")) {
7660
7844
  log.step(`Email (SendGrid): ${pc5.dim("SENDGRID_API_KEY, SENDGRID_FROM_EMAIL")}`);
7661
7845
  } else if (configContent.includes("email: { provider: 'resend'")) {
@@ -7716,8 +7900,8 @@ async function deployCommand() {
7716
7900
 
7717
7901
  // src/commands/upgrade.ts
7718
7902
  init_logger();
7719
- import fs26 from "fs-extra";
7720
- import path26 from "path";
7903
+ import fs27 from "fs-extra";
7904
+ import path28 from "path";
7721
7905
  import { execSync as execSync4 } from "child_process";
7722
7906
  var MARS_PACKAGES = ["@mars-stack/core", "@mars-stack/ui"];
7723
7907
  var CHANGELOG_URL = "https://github.com/greaveselliott/mars/blob/main/CHANGELOG.md";
@@ -7740,7 +7924,7 @@ function readCurrentVersion(packageJson, packageName) {
7740
7924
  return version ? version.replace(/^[\^~]/, "") : null;
7741
7925
  }
7742
7926
  function detectPackageManager2(projectDir) {
7743
- if (fs26.pathExistsSync(path26.join(projectDir, "yarn.lock"))) {
7927
+ if (fs27.pathExistsSync(path28.join(projectDir, "yarn.lock"))) {
7744
7928
  return "yarn";
7745
7929
  }
7746
7930
  return "npm";
@@ -7804,7 +7988,7 @@ function updatePackageJsonVersions(packageJsonPath, packageJson, versions) {
7804
7988
  if (updated.length > 0) {
7805
7989
  packageJson.dependencies = deps;
7806
7990
  packageJson.devDependencies = devDeps;
7807
- fs26.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
7991
+ fs27.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
7808
7992
  }
7809
7993
  return updated;
7810
7994
  }
@@ -7814,15 +7998,15 @@ function upgradeCommand(program2) {
7814
7998
  ).option("--dry-run", "Show what would be updated without making changes").action(async (options) => {
7815
7999
  log.title("MARS Upgrade");
7816
8000
  const projectDir = process.cwd();
7817
- const packageJsonPath = path26.join(projectDir, "package.json");
7818
- if (!fs26.pathExistsSync(packageJsonPath)) {
8001
+ const packageJsonPath = path28.join(projectDir, "package.json");
8002
+ if (!fs27.pathExistsSync(packageJsonPath)) {
7819
8003
  log.error(
7820
8004
  "No package.json found. Are you in a Mars project directory?"
7821
8005
  );
7822
8006
  process.exitCode = 1;
7823
8007
  return;
7824
8008
  }
7825
- const packageJson = fs26.readJsonSync(packageJsonPath);
8009
+ const packageJson = fs27.readJsonSync(packageJsonPath);
7826
8010
  const hasMarsPackage = MARS_PACKAGES.some(
7827
8011
  (name) => readCurrentVersion(packageJson, name) !== null
7828
8012
  );
@@ -7918,7 +8102,7 @@ init_sentry();
7918
8102
  init_feature_flags();
7919
8103
  init_logger();
7920
8104
  var program = new Command();
7921
- program.name("mars").description("MARS CLI: scaffold, configure, and maintain SaaS apps").version("0.1.0").option("-v, --verbose", "Enable verbose output for debugging");
8105
+ program.name("mars").description("MARS CLI: scaffold, configure, and maintain SaaS apps").version(getCliVersion()).option("-v, --verbose", "Enable verbose output for debugging");
7922
8106
  function isVerbose() {
7923
8107
  return program.opts().verbose === true;
7924
8108
  }