@mars-stack/cli 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/index.js +475 -366
  2. package/dist/index.js.map +1 -1
  3. package/package.json +3 -3
  4. package/template/.cursor/skills/mars-address-pr-comments/SKILL.md +129 -0
  5. package/template/AGENTS.md +3 -2
  6. package/template/package.json +4 -4
  7. package/template/src/app/(auth)/forgotten-password/page.tsx +18 -0
  8. package/template/src/app/(auth)/register/page.tsx +3 -0
  9. package/template/src/app/(auth)/verify/page.tsx +19 -0
  10. package/template/src/app/(protected)/layout.tsx +18 -0
  11. package/template/src/app/api/auth/forgot/route.ts +7 -1
  12. package/template/src/app/api/auth/signup/route.ts +9 -0
  13. package/template/src/config/app.config.ts +0 -8
  14. package/template/src/config/routes.ts +2 -0
  15. /package/template/.cursor/rules/{composition-patterns.mdc → mars-composition-patterns.mdc} +0 -0
  16. /package/template/.cursor/rules/{data-access.mdc → mars-data-access.mdc} +0 -0
  17. /package/template/.cursor/rules/{project-structure.mdc → mars-project-structure.mdc} +0 -0
  18. /package/template/.cursor/rules/{security.mdc → mars-security.mdc} +0 -0
  19. /package/template/.cursor/rules/{testing.mdc → mars-testing.mdc} +0 -0
  20. /package/template/.cursor/rules/{ui-conventions.mdc → mars-ui-conventions.mdc} +0 -0
  21. /package/template/.cursor/skills/{add-api-route → mars-add-api-route}/SKILL.md +0 -0
  22. /package/template/.cursor/skills/{add-audit-log → mars-add-audit-log}/SKILL.md +0 -0
  23. /package/template/.cursor/skills/{add-blog → mars-add-blog}/SKILL.md +0 -0
  24. /package/template/.cursor/skills/{add-command-palette → mars-add-command-palette}/SKILL.md +0 -0
  25. /package/template/.cursor/skills/{add-component → mars-add-component}/SKILL.md +0 -0
  26. /package/template/.cursor/skills/{add-crud-routes → mars-add-crud-routes}/SKILL.md +0 -0
  27. /package/template/.cursor/skills/{add-e2e-test → mars-add-e2e-test}/SKILL.md +0 -0
  28. /package/template/.cursor/skills/{add-error-boundary → mars-add-error-boundary}/SKILL.md +0 -0
  29. /package/template/.cursor/skills/{add-feature → mars-add-feature}/SKILL.md +0 -0
  30. /package/template/.cursor/skills/{add-middleware → mars-add-middleware}/SKILL.md +0 -0
  31. /package/template/.cursor/skills/{add-page → mars-add-page}/SKILL.md +0 -0
  32. /package/template/.cursor/skills/{add-prisma-model → mars-add-prisma-model}/SKILL.md +0 -0
  33. /package/template/.cursor/skills/{add-protected-resource → mars-add-protected-resource}/SKILL.md +0 -0
  34. /package/template/.cursor/skills/{add-role → mars-add-role}/SKILL.md +0 -0
  35. /package/template/.cursor/skills/{add-server-action → mars-add-server-action}/SKILL.md +0 -0
  36. /package/template/.cursor/skills/{add-webhook → mars-add-webhook}/SKILL.md +0 -0
  37. /package/template/.cursor/skills/{build-complete-feature → mars-build-complete-feature}/SKILL.md +0 -0
  38. /package/template/.cursor/skills/{build-dashboard → mars-build-dashboard}/SKILL.md +0 -0
  39. /package/template/.cursor/skills/{build-data-table → mars-build-data-table}/SKILL.md +0 -0
  40. /package/template/.cursor/skills/{build-form → mars-build-form}/SKILL.md +0 -0
  41. /package/template/.cursor/skills/{build-landing-page → mars-build-landing-page}/SKILL.md +0 -0
  42. /package/template/.cursor/skills/{configure-ai → mars-configure-ai}/SKILL.md +0 -0
  43. /package/template/.cursor/skills/{configure-analytics → mars-configure-analytics}/SKILL.md +0 -0
  44. /package/template/.cursor/skills/{configure-dark-mode → mars-configure-dark-mode}/SKILL.md +0 -0
  45. /package/template/.cursor/skills/{configure-email → mars-configure-email}/SKILL.md +0 -0
  46. /package/template/.cursor/skills/{configure-email-verification → mars-configure-email-verification}/SKILL.md +0 -0
  47. /package/template/.cursor/skills/{configure-feature-flags → mars-configure-feature-flags}/SKILL.md +0 -0
  48. /package/template/.cursor/skills/{configure-i18n → mars-configure-i18n}/SKILL.md +0 -0
  49. /package/template/.cursor/skills/{configure-jobs → mars-configure-jobs}/SKILL.md +0 -0
  50. /package/template/.cursor/skills/{configure-magic-links → mars-configure-magic-links}/SKILL.md +0 -0
  51. /package/template/.cursor/skills/{configure-multi-tenancy → mars-configure-multi-tenancy}/SKILL.md +0 -0
  52. /package/template/.cursor/skills/{configure-notifications → mars-configure-notifications}/SKILL.md +0 -0
  53. /package/template/.cursor/skills/{configure-oauth → mars-configure-oauth}/SKILL.md +0 -0
  54. /package/template/.cursor/skills/{configure-onboarding → mars-configure-onboarding}/SKILL.md +0 -0
  55. /package/template/.cursor/skills/{configure-payments → mars-configure-payments}/SKILL.md +0 -0
  56. /package/template/.cursor/skills/{configure-realtime → mars-configure-realtime}/SKILL.md +0 -0
  57. /package/template/.cursor/skills/{configure-search → mars-configure-search}/SKILL.md +0 -0
  58. /package/template/.cursor/skills/{configure-storage → mars-configure-storage}/SKILL.md +0 -0
  59. /package/template/.cursor/skills/{configure-two-factor → mars-configure-two-factor}/SKILL.md +0 -0
  60. /package/template/.cursor/skills/{create-execution-plan → mars-create-execution-plan}/SKILL.md +0 -0
  61. /package/template/.cursor/skills/{create-seed → mars-create-seed}/SKILL.md +0 -0
  62. /package/template/.cursor/skills/{deploy-to-vercel → mars-deploy-to-vercel}/SKILL.md +0 -0
  63. /package/template/.cursor/skills/{design-tokens → mars-design-tokens}/SKILL.md +0 -0
  64. /package/template/.cursor/skills/{setup-billing → mars-setup-billing}/SKILL.md +0 -0
  65. /package/template/.cursor/skills/{setup-project → mars-setup-project}/SKILL.md +0 -0
  66. /package/template/.cursor/skills/{setup-teams → mars-setup-teams}/SKILL.md +0 -0
  67. /package/template/.cursor/skills/{test-api-route → mars-test-api-route}/SKILL.md +0 -0
  68. /package/template/.cursor/skills/{update-architecture-docs → mars-update-architecture-docs}/SKILL.md +0 -0
package/dist/index.js CHANGED
@@ -44,8 +44,8 @@ var init_logger = __esm({
44
44
  });
45
45
 
46
46
  // src/utils/rollback.ts
47
- import fs3 from "fs-extra";
48
- import path3 from "path";
47
+ import fs4 from "fs-extra";
48
+ import path4 from "path";
49
49
  import os from "os";
50
50
  function createRollbackContext() {
51
51
  const manifest = {
@@ -53,15 +53,15 @@ function createRollbackContext() {
53
53
  filesModified: [],
54
54
  depsInstalled: []
55
55
  };
56
- const backupDir = 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
- if (!await fs3.pathExists(filePath)) return;
62
- await fs3.ensureDir(backupDir);
63
- const backupPath = path3.join(backupDir, `${manifest.filesModified.length}-${path3.basename(filePath)}`);
64
- await fs3.copy(filePath, backupPath);
61
+ if (!await fs4.pathExists(filePath)) return;
62
+ await fs4.ensureDir(backupDir);
63
+ const backupPath = path4.join(backupDir, `${manifest.filesModified.length}-${path4.basename(filePath)}`);
64
+ await fs4.copy(filePath, backupPath);
65
65
  manifest.filesModified.push({ path: filePath, backup: backupPath });
66
66
  }
67
67
  function trackInstalledDep(depName) {
@@ -70,13 +70,13 @@ function createRollbackContext() {
70
70
  async function rollback() {
71
71
  for (const filePath of manifest.filesCreated.reverse()) {
72
72
  try {
73
- await fs3.remove(filePath);
73
+ await fs4.remove(filePath);
74
74
  } catch {
75
75
  }
76
76
  }
77
77
  for (const { path: originalPath, backup } of manifest.filesModified) {
78
78
  try {
79
- await fs3.copy(backup, originalPath, { overwrite: true });
79
+ await fs4.copy(backup, originalPath, { overwrite: true });
80
80
  } catch {
81
81
  }
82
82
  }
@@ -86,9 +86,9 @@ function createRollbackContext() {
86
86
  await cleanupBackups();
87
87
  }
88
88
  async function cleanupBackups() {
89
- if (await fs3.pathExists(backupDir)) {
89
+ if (await fs4.pathExists(backupDir)) {
90
90
  try {
91
- await fs3.remove(backupDir);
91
+ await fs4.remove(backupDir);
92
92
  } catch {
93
93
  }
94
94
  }
@@ -108,15 +108,15 @@ var init_rollback = __esm({
108
108
  });
109
109
 
110
110
  // src/utils/dependencies.ts
111
- import fs4 from "fs-extra";
112
- import path4 from "path";
111
+ import fs5 from "fs-extra";
112
+ import path5 from "path";
113
113
  async function addDependencies(projectRoot, deps, dev = false) {
114
- const pkgPath = path4.join(projectRoot, "package.json");
115
- if (!await fs4.pathExists(pkgPath)) return;
116
- const pkg = await fs4.readJson(pkgPath);
114
+ const pkgPath = path5.join(projectRoot, "package.json");
115
+ if (!await fs5.pathExists(pkgPath)) return;
116
+ const pkg = await fs5.readJson(pkgPath);
117
117
  const key = dev ? "devDependencies" : "dependencies";
118
118
  pkg[key] = { ...pkg[key], ...deps };
119
- await fs4.writeJson(pkgPath, pkg, { spaces: 2 });
119
+ await fs5.writeJson(pkgPath, pkg, { spaces: 2 });
120
120
  }
121
121
  var init_dependencies = __esm({
122
122
  "src/utils/dependencies.ts"() {
@@ -124,16 +124,40 @@ var init_dependencies = __esm({
124
124
  }
125
125
  });
126
126
 
127
+ // src/utils/routes.ts
128
+ import fs6 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 fs6.pathExists(routesFile)) return;
133
+ let content = await fs6.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 fs6.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 fs7 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 fs7.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 fs7.ensureDir(path7.dirname(fullPath));
184
+ await fs7.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 fs7.pathExists(configPath)) return;
188
213
  await ctx.trackModifiedFile(configPath);
189
- const content = await fs5.readFile(configPath, "utf-8");
214
+ const content = await fs7.readFile(configPath, "utf-8");
190
215
  const updated = content.replace(/blog:\s*false/, "blog: true");
191
- await fs5.writeFile(configPath, updated);
216
+ await fs7.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 fs8 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 fs8.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 fs8.ensureDir(path8.dirname(fullPath));
757
+ await fs8.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 fs8.pathExists(configPath)) return;
763
789
  await ctx.trackModifiedFile(configPath);
764
- const content = await fs6.readFile(configPath, "utf-8");
790
+ const content = await fs8.readFile(configPath, "utf-8");
765
791
  const updated = content.replace(/darkMode:\s*false/, "darkMode: true");
766
- await fs6.writeFile(configPath, updated);
792
+ await fs8.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 fs8.pathExists(layoutPath)) return;
771
797
  await ctx.trackModifiedFile(layoutPath);
772
- let content = await fs6.readFile(layoutPath, "utf-8");
798
+ let content = await fs8.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 fs8.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 fs8.pathExists(providersPath)) return;
791
817
  await ctx.trackModifiedFile(providersPath);
792
- let content = await fs6.readFile(providersPath, "utf-8");
818
+ let content = await fs8.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 fs8.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 fs8.pathExists(navPath)) return;
816
842
  await ctx.trackModifiedFile(navPath);
817
- let content = await fs6.readFile(navPath, "utf-8");
843
+ let content = await fs8.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 fs8.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 fs8.pathExists(globalsPath)) return;
837
863
  await ctx.trackModifiedFile(globalsPath);
838
- let content = await fs6.readFile(globalsPath, "utf-8");
864
+ let content = await fs8.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 fs8.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 fs9 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 fs9.pathExists(authPath)) return;
1129
1155
  await ctx.trackModifiedFile(authPath);
1130
- let content = await fs7.readFile(authPath, "utf-8");
1156
+ let content = await fs9.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 fs9.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 fs10 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 fs10.pathExists(featureDir)) {
1154
1180
  log.error("Notifications feature already exists at src/features/notifications/");
1155
1181
  return;
1156
1182
  }
@@ -1173,10 +1199,10 @@ async function generateNotifications(projectRoot) {
1173
1199
  };
1174
1200
  let count = 0;
1175
1201
  for (const [filePath, content] of Object.entries(files)) {
1176
- const fullPath = path8.join(projectRoot, filePath);
1202
+ const fullPath = path10.join(projectRoot, filePath);
1177
1203
  ctx.trackCreatedFile(fullPath);
1178
- await fs8.ensureDir(path8.dirname(fullPath));
1179
- await fs8.writeFile(fullPath, content);
1204
+ await fs10.ensureDir(path10.dirname(fullPath));
1205
+ await fs10.writeFile(fullPath, content);
1180
1206
  count++;
1181
1207
  }
1182
1208
  await addUserRelation(projectRoot, "notifications Notification[]", ctx);
@@ -1203,10 +1229,10 @@ async function generateNotifications(projectRoot) {
1203
1229
  }
1204
1230
  }
1205
1231
  async function wireProtectedNav2(projectRoot, ctx) {
1206
- const navPath = path8.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
1207
- if (!await fs8.pathExists(navPath)) return;
1232
+ const navPath = path10.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
1233
+ if (!await fs10.pathExists(navPath)) return;
1208
1234
  await ctx.trackModifiedFile(navPath);
1209
- let content = await fs8.readFile(navPath, "utf-8");
1235
+ let content = await fs10.readFile(navPath, "utf-8");
1210
1236
  if (content.includes("NotificationBell")) return;
1211
1237
  content = insertImportAfterDirectives(
1212
1238
  content,
@@ -1217,15 +1243,15 @@ async function wireProtectedNav2(projectRoot, ctx) {
1217
1243
  /(<div className="flex items-center gap-3">)\s*\n(\s*)(<div className="hidden md:block">)/,
1218
1244
  "$1\n$2<NotificationBell />\n$2$3"
1219
1245
  );
1220
- await fs8.writeFile(navPath, content);
1246
+ await fs10.writeFile(navPath, content);
1221
1247
  }
1222
1248
  async function setConfigFlag3(projectRoot, ctx) {
1223
- const configPath = path8.join(projectRoot, "src", "config", "app.config.ts");
1224
- if (!await fs8.pathExists(configPath)) return;
1249
+ const configPath = path10.join(projectRoot, "src", "config", "app.config.ts");
1250
+ if (!await fs10.pathExists(configPath)) return;
1225
1251
  await ctx.trackModifiedFile(configPath);
1226
- const content = await fs8.readFile(configPath, "utf-8");
1252
+ const content = await fs10.readFile(configPath, "utf-8");
1227
1253
  const updated = content.replace(/notifications:\s*false/, "notifications: true");
1228
- await fs8.writeFile(configPath, updated);
1254
+ await fs10.writeFile(configPath, updated);
1229
1255
  }
1230
1256
  function prismaSchema() {
1231
1257
  return `// Generated by mars generate notifications (notifications@${GENERATOR_VERSION3})
@@ -1821,11 +1847,11 @@ var analytics_exports = {};
1821
1847
  __export(analytics_exports, {
1822
1848
  generateAnalytics: () => generateAnalytics
1823
1849
  });
1824
- import fs9 from "fs-extra";
1825
- import path9 from "path";
1850
+ import fs11 from "fs-extra";
1851
+ import path11 from "path";
1826
1852
  async function generateAnalytics(projectRoot) {
1827
- const featureDir = path9.join(projectRoot, "src", "features", "analytics");
1828
- if (await fs9.pathExists(featureDir)) {
1853
+ const featureDir = path11.join(projectRoot, "src", "features", "analytics");
1854
+ if (await fs11.pathExists(featureDir)) {
1829
1855
  log.error("Analytics feature already exists at src/features/analytics/");
1830
1856
  return;
1831
1857
  }
@@ -1840,10 +1866,10 @@ async function generateAnalytics(projectRoot) {
1840
1866
  };
1841
1867
  let count = 0;
1842
1868
  for (const [filePath, content] of Object.entries(files)) {
1843
- const fullPath = path9.join(projectRoot, filePath);
1869
+ const fullPath = path11.join(projectRoot, filePath);
1844
1870
  ctx.trackCreatedFile(fullPath);
1845
- await fs9.ensureDir(path9.dirname(fullPath));
1846
- await fs9.writeFile(fullPath, content);
1871
+ await fs11.ensureDir(path11.dirname(fullPath));
1872
+ await fs11.writeFile(fullPath, content);
1847
1873
  count++;
1848
1874
  }
1849
1875
  await wireProviders2(projectRoot, ctx);
@@ -1874,10 +1900,10 @@ async function generateAnalytics(projectRoot) {
1874
1900
  }
1875
1901
  }
1876
1902
  async function wireProviders2(projectRoot, ctx) {
1877
- const providersPath = path9.join(projectRoot, "src", "app", "providers.tsx");
1878
- if (!await fs9.pathExists(providersPath)) return;
1903
+ const providersPath = path11.join(projectRoot, "src", "app", "providers.tsx");
1904
+ if (!await fs11.pathExists(providersPath)) return;
1879
1905
  await ctx.trackModifiedFile(providersPath);
1880
- let content = await fs9.readFile(providersPath, "utf-8");
1906
+ let content = await fs11.readFile(providersPath, "utf-8");
1881
1907
  if (content.includes("AnalyticsProvider")) return;
1882
1908
  content = insertImportAfterDirectives(
1883
1909
  content,
@@ -1892,8 +1918,8 @@ async function wireProviders2(projectRoot, ctx) {
1892
1918
  );`;
1893
1919
  }
1894
1920
  );
1895
- await fs9.writeFile(providersPath, content);
1896
- const written = await fs9.readFile(providersPath, "utf-8");
1921
+ await fs11.writeFile(providersPath, content);
1922
+ const written = await fs11.readFile(providersPath, "utf-8");
1897
1923
  if (!written.includes("AnalyticsProvider")) {
1898
1924
  throw new Error(
1899
1925
  "wireProviders: AnalyticsProvider was not inserted into providers.tsx \u2014 the return statement pattern did not match. Review the template file structure."
@@ -1901,12 +1927,12 @@ async function wireProviders2(projectRoot, ctx) {
1901
1927
  }
1902
1928
  }
1903
1929
  async function setConfigFlag4(projectRoot, ctx) {
1904
- const configPath = path9.join(projectRoot, "src", "config", "app.config.ts");
1905
- if (!await fs9.pathExists(configPath)) return;
1930
+ const configPath = path11.join(projectRoot, "src", "config", "app.config.ts");
1931
+ if (!await fs11.pathExists(configPath)) return;
1906
1932
  await ctx.trackModifiedFile(configPath);
1907
- const content = await fs9.readFile(configPath, "utf-8");
1933
+ const content = await fs11.readFile(configPath, "utf-8");
1908
1934
  const updated = content.replace(/analytics:\s*false/, "analytics: true");
1909
- await fs9.writeFile(configPath, updated);
1935
+ await fs11.writeFile(configPath, updated);
1910
1936
  }
1911
1937
  function types3() {
1912
1938
  return `${STAMP4}
@@ -2265,11 +2291,11 @@ var command_palette_exports = {};
2265
2291
  __export(command_palette_exports, {
2266
2292
  generateCommandPalette: () => generateCommandPalette
2267
2293
  });
2268
- import fs10 from "fs-extra";
2269
- import path10 from "path";
2294
+ import fs12 from "fs-extra";
2295
+ import path12 from "path";
2270
2296
  async function generateCommandPalette(projectRoot) {
2271
- const featureDir = path10.join(projectRoot, "src", "features", "command-palette");
2272
- if (await fs10.pathExists(featureDir)) {
2297
+ const featureDir = path12.join(projectRoot, "src", "features", "command-palette");
2298
+ if (await fs12.pathExists(featureDir)) {
2273
2299
  log.error("Command palette feature already exists at src/features/command-palette/");
2274
2300
  return;
2275
2301
  }
@@ -2287,10 +2313,10 @@ async function generateCommandPalette(projectRoot) {
2287
2313
  };
2288
2314
  let count = 0;
2289
2315
  for (const [filePath, content] of Object.entries(files)) {
2290
- const fullPath = path10.join(projectRoot, filePath);
2316
+ const fullPath = path12.join(projectRoot, filePath);
2291
2317
  ctx.trackCreatedFile(fullPath);
2292
- await fs10.ensureDir(path10.dirname(fullPath));
2293
- await fs10.writeFile(fullPath, content);
2318
+ await fs12.ensureDir(path12.dirname(fullPath));
2319
+ await fs12.writeFile(fullPath, content);
2294
2320
  count++;
2295
2321
  }
2296
2322
  await addDependencies(projectRoot, { cmdk: "^1.0.0" });
@@ -2313,26 +2339,26 @@ async function generateCommandPalette(projectRoot) {
2313
2339
  }
2314
2340
  }
2315
2341
  async function wireLayout2(projectRoot, ctx) {
2316
- const layoutPath = path10.join(projectRoot, "src", "app", "layout.tsx");
2317
- if (!await fs10.pathExists(layoutPath)) return;
2318
- await ctx.trackModifiedFile(layoutPath);
2319
- let content = await fs10.readFile(layoutPath, "utf-8");
2342
+ const layoutPath = path12.join(projectRoot, "src", "app", "layout.tsx");
2343
+ if (!await fs12.pathExists(layoutPath)) return;
2344
+ let content = await fs12.readFile(layoutPath, "utf-8");
2320
2345
  if (content.includes("CommandPalette")) return;
2346
+ await ctx.trackModifiedFile(layoutPath);
2321
2347
  content = `import { CommandPalette } from '@/features/command-palette';
2322
2348
  ${content}`;
2323
2349
  content = content.replace(
2324
- /(\s*)(<Providers>\{children\}<\/Providers>)/,
2350
+ /(\s*)(<Providers>[\s\S]*?<\/Providers>)/,
2325
2351
  "$1$2\n$1<CommandPalette />"
2326
2352
  );
2327
- await fs10.writeFile(layoutPath, content);
2353
+ await fs12.writeFile(layoutPath, content);
2328
2354
  }
2329
2355
  async function setConfigFlag5(projectRoot, ctx) {
2330
- const configPath = path10.join(projectRoot, "src", "config", "app.config.ts");
2331
- if (!await fs10.pathExists(configPath)) return;
2356
+ const configPath = path12.join(projectRoot, "src", "config", "app.config.ts");
2357
+ if (!await fs12.pathExists(configPath)) return;
2332
2358
  await ctx.trackModifiedFile(configPath);
2333
- const content = await fs10.readFile(configPath, "utf-8");
2359
+ const content = await fs12.readFile(configPath, "utf-8");
2334
2360
  const updated = content.replace(/commandPalette:\s*false/, "commandPalette: true");
2335
- await fs10.writeFile(configPath, updated);
2361
+ await fs12.writeFile(configPath, updated);
2336
2362
  }
2337
2363
  function types4() {
2338
2364
  return `${STAMP5}
@@ -2748,11 +2774,11 @@ var onboarding_exports = {};
2748
2774
  __export(onboarding_exports, {
2749
2775
  generateOnboarding: () => generateOnboarding
2750
2776
  });
2751
- import fs11 from "fs-extra";
2752
- import path11 from "path";
2777
+ import fs13 from "fs-extra";
2778
+ import path13 from "path";
2753
2779
  async function generateOnboarding(projectRoot) {
2754
- const featureDir = path11.join(projectRoot, "src", "features", "onboarding");
2755
- if (await fs11.pathExists(featureDir)) {
2780
+ const featureDir = path13.join(projectRoot, "src", "features", "onboarding");
2781
+ if (await fs13.pathExists(featureDir)) {
2756
2782
  log.error("Onboarding feature already exists at src/features/onboarding/");
2757
2783
  return;
2758
2784
  }
@@ -2774,13 +2800,14 @@ async function generateOnboarding(projectRoot) {
2774
2800
  };
2775
2801
  let count = 0;
2776
2802
  for (const [filePath, content] of Object.entries(files)) {
2777
- const fullPath = path11.join(projectRoot, filePath);
2803
+ const fullPath = path13.join(projectRoot, filePath);
2778
2804
  ctx.trackCreatedFile(fullPath);
2779
- await fs11.ensureDir(path11.dirname(fullPath));
2780
- await fs11.writeFile(fullPath, content);
2805
+ await fs13.ensureDir(path13.dirname(fullPath));
2806
+ await fs13.writeFile(fullPath, content);
2781
2807
  count++;
2782
2808
  }
2783
2809
  await addUserRelation(projectRoot, "onboardingProgress OnboardingProgress?", ctx);
2810
+ await registerRoute(projectRoot, "onboarding", "/onboarding", ctx);
2784
2811
  await setConfigFlag6(projectRoot, ctx);
2785
2812
  await ctx.commit();
2786
2813
  log.success(`Generated onboarding feature with ${count} files`);
@@ -2801,12 +2828,12 @@ async function generateOnboarding(projectRoot) {
2801
2828
  }
2802
2829
  }
2803
2830
  async function setConfigFlag6(projectRoot, ctx) {
2804
- const configPath = path11.join(projectRoot, "src", "config", "app.config.ts");
2805
- if (!await fs11.pathExists(configPath)) return;
2831
+ const configPath = path13.join(projectRoot, "src", "config", "app.config.ts");
2832
+ if (!await fs13.pathExists(configPath)) return;
2806
2833
  await ctx.trackModifiedFile(configPath);
2807
- const content = await fs11.readFile(configPath, "utf-8");
2834
+ const content = await fs13.readFile(configPath, "utf-8");
2808
2835
  const updated = content.replace(/onboarding:\s*false/, "onboarding: true");
2809
- await fs11.writeFile(configPath, updated);
2836
+ await fs13.writeFile(configPath, updated);
2810
2837
  }
2811
2838
  function prismaSchema2() {
2812
2839
  return `// Generated by mars generate onboarding (onboarding@${GENERATOR_VERSION6})
@@ -3322,6 +3349,7 @@ var init_onboarding = __esm({
3322
3349
  init_logger();
3323
3350
  init_rollback();
3324
3351
  init_prisma();
3352
+ init_routes();
3325
3353
  GENERATOR_VERSION6 = "0.1.0";
3326
3354
  STAMP6 = `// @mars-generated onboarding@${GENERATOR_VERSION6}`;
3327
3355
  }
@@ -3332,11 +3360,11 @@ var search_exports = {};
3332
3360
  __export(search_exports, {
3333
3361
  generateSearch: () => generateSearch
3334
3362
  });
3335
- import fs12 from "fs-extra";
3336
- import path12 from "path";
3363
+ import fs14 from "fs-extra";
3364
+ import path14 from "path";
3337
3365
  async function generateSearch(projectRoot) {
3338
- const featureDir = path12.join(projectRoot, "src", "features", "search");
3339
- if (await fs12.pathExists(featureDir)) {
3366
+ const featureDir = path14.join(projectRoot, "src", "features", "search");
3367
+ if (await fs14.pathExists(featureDir)) {
3340
3368
  log.error("Search feature already exists at src/features/search/");
3341
3369
  return;
3342
3370
  }
@@ -3349,25 +3377,30 @@ async function generateSearch(projectRoot) {
3349
3377
  "src/features/search/server/index.ts": serverIndex(),
3350
3378
  "src/features/search/hooks/use-search.ts": useSearchHook(),
3351
3379
  "src/features/search/components/SearchInput.tsx": searchInput(),
3380
+ "src/features/search/components/NavSearch.tsx": navSearch(),
3352
3381
  "src/features/search/components/index.ts": componentIndex5(),
3353
3382
  "src/features/search/index.ts": featureIndex4(),
3354
3383
  "src/app/api/protected/search/route.ts": searchRoute()
3355
3384
  };
3356
3385
  let count = 0;
3357
3386
  for (const [filePath, content] of Object.entries(files)) {
3358
- const fullPath = path12.join(projectRoot, filePath);
3387
+ const fullPath = path14.join(projectRoot, filePath);
3359
3388
  ctx.trackCreatedFile(fullPath);
3360
- await fs12.ensureDir(path12.dirname(fullPath));
3361
- await fs12.writeFile(fullPath, content);
3389
+ await fs14.ensureDir(path14.dirname(fullPath));
3390
+ await fs14.writeFile(fullPath, content);
3362
3391
  count++;
3363
3392
  }
3393
+ await wireProtectedNav3(projectRoot, ctx);
3364
3394
  await setConfigFlag7(projectRoot, ctx);
3365
3395
  await ctx.commit();
3366
- log.success(`Generated search feature with ${count} files`);
3396
+ log.success(`Generated and wired search feature (${count} files created)`);
3367
3397
  log.blank();
3368
3398
  log.step("src/features/search/ \u2014 types, validation, server logic, hooks, components");
3369
3399
  log.step("src/app/api/protected/search/ \u2014 authenticated search endpoint");
3370
3400
  log.blank();
3401
+ log.step("Wired automatically:");
3402
+ log.step(" \u2713 NavSearch added to navigation bar");
3403
+ log.blank();
3371
3404
  log.warn("Search uses Postgres full-text search by default (no extra setup).");
3372
3405
  log.blank();
3373
3406
  log.step("For Algolia: yarn add algoliasearch + set ALGOLIA_APP_ID, ALGOLIA_API_KEY env vars");
@@ -3381,13 +3414,30 @@ async function generateSearch(projectRoot) {
3381
3414
  throw error;
3382
3415
  }
3383
3416
  }
3417
+ async function wireProtectedNav3(projectRoot, ctx) {
3418
+ const navPath = path14.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
3419
+ if (!await fs14.pathExists(navPath)) return;
3420
+ let content = await fs14.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 fs14.writeFile(navPath, content);
3433
+ }
3384
3434
  async function setConfigFlag7(projectRoot, ctx) {
3385
- const configPath = path12.join(projectRoot, "src", "config", "app.config.ts");
3386
- if (!await fs12.pathExists(configPath)) return;
3435
+ const configPath = path14.join(projectRoot, "src", "config", "app.config.ts");
3436
+ if (!await fs14.pathExists(configPath)) return;
3387
3437
  await ctx.trackModifiedFile(configPath);
3388
- const content = await fs12.readFile(configPath, "utf-8");
3438
+ const content = await fs14.readFile(configPath, "utf-8");
3389
3439
  const updated = content.replace(/search:\s*false/, "search: true");
3390
- await fs12.writeFile(configPath, updated);
3440
+ await fs14.writeFile(configPath, updated);
3391
3441
  }
3392
3442
  function types6() {
3393
3443
  return `${STAMP7}
@@ -3657,9 +3707,31 @@ export function SearchInput({
3657
3707
  }
3658
3708
  `;
3659
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
+ }
3660
3731
  function componentIndex5() {
3661
3732
  return `${STAMP7}
3662
3733
  export { SearchInput } from './SearchInput';
3734
+ export { NavSearch } from './NavSearch';
3663
3735
  `;
3664
3736
  }
3665
3737
  function featureIndex4() {
@@ -3667,7 +3739,7 @@ function featureIndex4() {
3667
3739
  export type { SearchResult, SearchOptions, SearchProvider } from './types';
3668
3740
  export { searchParamsSchema } from './validation/schemas';
3669
3741
  export type { SearchParams } from './validation/schemas';
3670
- export { SearchInput } from './components';
3742
+ export { SearchInput, NavSearch } from './components';
3671
3743
  export { useSearch } from './hooks/use-search';
3672
3744
  `;
3673
3745
  }
@@ -3706,6 +3778,7 @@ var GENERATOR_VERSION7, STAMP7;
3706
3778
  var init_search = __esm({
3707
3779
  "src/generators/features/search.ts"() {
3708
3780
  "use strict";
3781
+ init_client_file_patch();
3709
3782
  init_logger();
3710
3783
  init_rollback();
3711
3784
  GENERATOR_VERSION7 = "0.1.0";
@@ -3718,11 +3791,11 @@ var realtime_exports = {};
3718
3791
  __export(realtime_exports, {
3719
3792
  generateRealtime: () => generateRealtime
3720
3793
  });
3721
- import fs13 from "fs-extra";
3722
- import path13 from "path";
3794
+ import fs15 from "fs-extra";
3795
+ import path15 from "path";
3723
3796
  async function generateRealtime(projectRoot) {
3724
- const featureDir = path13.join(projectRoot, "src", "features", "realtime");
3725
- if (await fs13.pathExists(featureDir)) {
3797
+ const featureDir = path15.join(projectRoot, "src", "features", "realtime");
3798
+ if (await fs15.pathExists(featureDir)) {
3726
3799
  log.error("Realtime feature already exists at src/features/realtime/");
3727
3800
  return;
3728
3801
  }
@@ -3739,10 +3812,10 @@ async function generateRealtime(projectRoot) {
3739
3812
  };
3740
3813
  let count = 0;
3741
3814
  for (const [filePath, content] of Object.entries(files)) {
3742
- const fullPath = path13.join(projectRoot, filePath);
3815
+ const fullPath = path15.join(projectRoot, filePath);
3743
3816
  ctx.trackCreatedFile(fullPath);
3744
- await fs13.ensureDir(path13.dirname(fullPath));
3745
- await fs13.writeFile(fullPath, content);
3817
+ await fs15.ensureDir(path15.dirname(fullPath));
3818
+ await fs15.writeFile(fullPath, content);
3746
3819
  count++;
3747
3820
  }
3748
3821
  await setConfigFlag8(projectRoot, ctx);
@@ -3767,12 +3840,12 @@ async function generateRealtime(projectRoot) {
3767
3840
  }
3768
3841
  }
3769
3842
  async function setConfigFlag8(projectRoot, ctx) {
3770
- const configPath = path13.join(projectRoot, "src", "config", "app.config.ts");
3771
- if (!await fs13.pathExists(configPath)) return;
3843
+ const configPath = path15.join(projectRoot, "src", "config", "app.config.ts");
3844
+ if (!await fs15.pathExists(configPath)) return;
3772
3845
  await ctx.trackModifiedFile(configPath);
3773
- const content = await fs13.readFile(configPath, "utf-8");
3846
+ const content = await fs15.readFile(configPath, "utf-8");
3774
3847
  const updated = content.replace(/realtime:\s*false/, "realtime: true");
3775
- await fs13.writeFile(configPath, updated);
3848
+ await fs15.writeFile(configPath, updated);
3776
3849
  }
3777
3850
  function types7() {
3778
3851
  return `${STAMP8}
@@ -4081,11 +4154,11 @@ var ai_exports = {};
4081
4154
  __export(ai_exports, {
4082
4155
  generateAI: () => generateAI
4083
4156
  });
4084
- import fs14 from "fs-extra";
4085
- import path14 from "path";
4157
+ import fs16 from "fs-extra";
4158
+ import path16 from "path";
4086
4159
  async function generateAI(projectRoot) {
4087
- const featureDir = path14.join(projectRoot, "src", "features", "ai");
4088
- if (await fs14.pathExists(featureDir)) {
4160
+ const featureDir = path16.join(projectRoot, "src", "features", "ai");
4161
+ if (await fs16.pathExists(featureDir)) {
4089
4162
  log.error("AI feature already exists at src/features/ai/");
4090
4163
  return;
4091
4164
  }
@@ -4103,10 +4176,10 @@ async function generateAI(projectRoot) {
4103
4176
  };
4104
4177
  let count = 0;
4105
4178
  for (const [filePath, content] of Object.entries(files)) {
4106
- const fullPath = path14.join(projectRoot, filePath);
4179
+ const fullPath = path16.join(projectRoot, filePath);
4107
4180
  ctx.trackCreatedFile(fullPath);
4108
- await fs14.ensureDir(path14.dirname(fullPath));
4109
- await fs14.writeFile(fullPath, content);
4181
+ await fs16.ensureDir(path16.dirname(fullPath));
4182
+ await fs16.writeFile(fullPath, content);
4110
4183
  count++;
4111
4184
  }
4112
4185
  await addDependencies(projectRoot, {
@@ -4132,12 +4205,12 @@ async function generateAI(projectRoot) {
4132
4205
  }
4133
4206
  }
4134
4207
  async function setConfigFlag9(projectRoot, ctx) {
4135
- const configPath = path14.join(projectRoot, "src", "config", "app.config.ts");
4136
- if (!await fs14.pathExists(configPath)) return;
4208
+ const configPath = path16.join(projectRoot, "src", "config", "app.config.ts");
4209
+ if (!await fs16.pathExists(configPath)) return;
4137
4210
  await ctx.trackModifiedFile(configPath);
4138
- const content = await fs14.readFile(configPath, "utf-8");
4211
+ const content = await fs16.readFile(configPath, "utf-8");
4139
4212
  const updated = content.replace(/ai:\s*false/, "ai: true");
4140
- await fs14.writeFile(configPath, updated);
4213
+ await fs16.writeFile(configPath, updated);
4141
4214
  }
4142
4215
  function types8() {
4143
4216
  return `${STAMP9}
@@ -4576,11 +4649,11 @@ var cookie_consent_exports = {};
4576
4649
  __export(cookie_consent_exports, {
4577
4650
  generateCookieConsent: () => generateCookieConsent
4578
4651
  });
4579
- import fs15 from "fs-extra";
4580
- import path15 from "path";
4652
+ import fs17 from "fs-extra";
4653
+ import path17 from "path";
4581
4654
  async function generateCookieConsent(projectRoot) {
4582
- const featureDir = path15.join(projectRoot, "src", "features", "cookie-consent");
4583
- if (await fs15.pathExists(featureDir)) {
4655
+ const featureDir = path17.join(projectRoot, "src", "features", "cookie-consent");
4656
+ if (await fs17.pathExists(featureDir)) {
4584
4657
  log.error("Cookie consent feature already exists at src/features/cookie-consent/");
4585
4658
  return;
4586
4659
  }
@@ -4596,10 +4669,10 @@ async function generateCookieConsent(projectRoot) {
4596
4669
  };
4597
4670
  let count = 0;
4598
4671
  for (const [filePath, content] of Object.entries(files)) {
4599
- const fullPath = path15.join(projectRoot, filePath);
4672
+ const fullPath = path17.join(projectRoot, filePath);
4600
4673
  ctx.trackCreatedFile(fullPath);
4601
- await fs15.ensureDir(path15.dirname(fullPath));
4602
- await fs15.writeFile(fullPath, content);
4674
+ await fs17.ensureDir(path17.dirname(fullPath));
4675
+ await fs17.writeFile(fullPath, content);
4603
4676
  count++;
4604
4677
  }
4605
4678
  await wireLayout3(projectRoot, ctx);
@@ -4624,26 +4697,26 @@ async function generateCookieConsent(projectRoot) {
4624
4697
  }
4625
4698
  }
4626
4699
  async function wireLayout3(projectRoot, ctx) {
4627
- const layoutPath = path15.join(projectRoot, "src", "app", "layout.tsx");
4628
- if (!await fs15.pathExists(layoutPath)) return;
4629
- await ctx.trackModifiedFile(layoutPath);
4630
- let content = await fs15.readFile(layoutPath, "utf-8");
4700
+ const layoutPath = path17.join(projectRoot, "src", "app", "layout.tsx");
4701
+ if (!await fs17.pathExists(layoutPath)) return;
4702
+ let content = await fs17.readFile(layoutPath, "utf-8");
4631
4703
  if (content.includes("CookieConsentBanner")) return;
4704
+ await ctx.trackModifiedFile(layoutPath);
4632
4705
  content = `import { CookieConsentBanner } from '@/features/cookie-consent';
4633
4706
  ${content}`;
4634
4707
  content = content.replace(
4635
- /(\s*)(<Providers>\{children\}<\/Providers>)/,
4708
+ /(\s*)(<Providers>[\s\S]*?<\/Providers>)/,
4636
4709
  "$1$2\n$1<CookieConsentBanner />"
4637
4710
  );
4638
- await fs15.writeFile(layoutPath, content);
4711
+ await fs17.writeFile(layoutPath, content);
4639
4712
  }
4640
4713
  async function setConfigFlag10(projectRoot, ctx) {
4641
- const configPath = path15.join(projectRoot, "src", "config", "app.config.ts");
4642
- if (!await fs15.pathExists(configPath)) return;
4714
+ const configPath = path17.join(projectRoot, "src", "config", "app.config.ts");
4715
+ if (!await fs17.pathExists(configPath)) return;
4643
4716
  await ctx.trackModifiedFile(configPath);
4644
- const content = await fs15.readFile(configPath, "utf-8");
4717
+ const content = await fs17.readFile(configPath, "utf-8");
4645
4718
  const updated = content.replace(/cookieConsent:\s*false/, "cookieConsent: true");
4646
- await fs15.writeFile(configPath, updated);
4719
+ await fs17.writeFile(configPath, updated);
4647
4720
  }
4648
4721
  function types9() {
4649
4722
  return `${STAMP10}
@@ -4928,11 +5001,11 @@ var coming_soon_exports = {};
4928
5001
  __export(coming_soon_exports, {
4929
5002
  generateComingSoon: () => generateComingSoon
4930
5003
  });
4931
- import fs16 from "fs-extra";
4932
- import path16 from "path";
5004
+ import fs18 from "fs-extra";
5005
+ import path18 from "path";
4933
5006
  async function generateComingSoon(projectRoot) {
4934
- const featureDir = path16.join(projectRoot, "src", "features", "coming-soon");
4935
- if (await fs16.pathExists(featureDir)) {
5007
+ const featureDir = path18.join(projectRoot, "src", "features", "coming-soon");
5008
+ if (await fs18.pathExists(featureDir)) {
4936
5009
  log.error("Coming soon feature already exists at src/features/coming-soon/");
4937
5010
  return;
4938
5011
  }
@@ -4949,12 +5022,13 @@ async function generateComingSoon(projectRoot) {
4949
5022
  };
4950
5023
  let count = 0;
4951
5024
  for (const [filePath, content] of Object.entries(files)) {
4952
- const fullPath = path16.join(projectRoot, filePath);
5025
+ const fullPath = path18.join(projectRoot, filePath);
4953
5026
  ctx.trackCreatedFile(fullPath);
4954
- await fs16.ensureDir(path16.dirname(fullPath));
4955
- await fs16.writeFile(fullPath, content);
5027
+ await fs18.ensureDir(path18.dirname(fullPath));
5028
+ await fs18.writeFile(fullPath, content);
4956
5029
  count++;
4957
5030
  }
5031
+ await registerRoute(projectRoot, "comingSoon", "/coming-soon", ctx);
4958
5032
  await setConfigFlag11(projectRoot, ctx);
4959
5033
  await ctx.commit();
4960
5034
  log.success(`Generated coming soon feature with ${count} files`);
@@ -4973,12 +5047,12 @@ async function generateComingSoon(projectRoot) {
4973
5047
  }
4974
5048
  }
4975
5049
  async function setConfigFlag11(projectRoot, ctx) {
4976
- const configPath = path16.join(projectRoot, "src", "config", "app.config.ts");
4977
- if (!await fs16.pathExists(configPath)) return;
5050
+ const configPath = path18.join(projectRoot, "src", "config", "app.config.ts");
5051
+ if (!await fs18.pathExists(configPath)) return;
4978
5052
  await ctx.trackModifiedFile(configPath);
4979
- const content = await fs16.readFile(configPath, "utf-8");
5053
+ const content = await fs18.readFile(configPath, "utf-8");
4980
5054
  const updated = content.replace(/comingSoon:\s*false/, "comingSoon: true");
4981
- await fs16.writeFile(configPath, updated);
5055
+ await fs18.writeFile(configPath, updated);
4982
5056
  }
4983
5057
  function types10() {
4984
5058
  return `${STAMP11}
@@ -5155,6 +5229,7 @@ var init_coming_soon = __esm({
5155
5229
  "use strict";
5156
5230
  init_logger();
5157
5231
  init_rollback();
5232
+ init_routes();
5158
5233
  GENERATOR_VERSION11 = "0.1.0";
5159
5234
  STAMP11 = `// @mars-generated coming-soon@${GENERATOR_VERSION11}`;
5160
5235
  }
@@ -5165,11 +5240,11 @@ var sentry_exports = {};
5165
5240
  __export(sentry_exports, {
5166
5241
  generateSentry: () => generateSentry
5167
5242
  });
5168
- import fs17 from "fs-extra";
5169
- import path17 from "path";
5243
+ import fs19 from "fs-extra";
5244
+ import path19 from "path";
5170
5245
  async function generateSentry(projectRoot) {
5171
- const featureDir = path17.join(projectRoot, "src", "features", "sentry");
5172
- if (await fs17.pathExists(featureDir)) {
5246
+ const featureDir = path19.join(projectRoot, "src", "features", "sentry");
5247
+ if (await fs19.pathExists(featureDir)) {
5173
5248
  log.error("Sentry feature already exists at src/features/sentry/");
5174
5249
  return;
5175
5250
  }
@@ -5185,24 +5260,27 @@ async function generateSentry(projectRoot) {
5185
5260
  };
5186
5261
  let count = 0;
5187
5262
  for (const [filePath, content] of Object.entries(files)) {
5188
- const fullPath = path17.join(projectRoot, filePath);
5263
+ const fullPath = path19.join(projectRoot, filePath);
5189
5264
  ctx.trackCreatedFile(fullPath);
5190
- await fs17.ensureDir(path17.dirname(fullPath));
5191
- await fs17.writeFile(fullPath, content);
5265
+ await fs19.ensureDir(path19.dirname(fullPath));
5266
+ await fs19.writeFile(fullPath, content);
5192
5267
  count++;
5193
5268
  }
5194
5269
  await addDependencies(projectRoot, { "@sentry/nextjs": "^8.0.0" });
5270
+ await wireRootLayout(projectRoot, ctx);
5195
5271
  await setConfigFlag12(projectRoot, ctx);
5196
5272
  await ctx.commit();
5197
- log.success(`Generated sentry feature with ${count} files`);
5273
+ log.success(`Generated and wired sentry feature (${count} files created)`);
5198
5274
  log.blank();
5199
5275
  log.step("src/features/sentry/ \u2014 types, client-init, server-init, ErrorBoundary");
5200
5276
  log.blank();
5277
+ log.step("Wired automatically:");
5278
+ log.step(" \u2713 ErrorBoundary wrapping app content in root layout");
5279
+ log.blank();
5201
5280
  log.warn("Optional: run npx @sentry/wizard@latest -i nextjs for full setup");
5202
5281
  log.blank();
5203
5282
  log.warn("Next steps:");
5204
5283
  log.step("Set NEXT_PUBLIC_SENTRY_DSN and SENTRY_DSN environment variables");
5205
- log.step("Wrap pages or layouts with <ErrorBoundary> for graceful error handling");
5206
5284
  log.step("Import client-init and server-init in your instrumentation files");
5207
5285
  log.blank();
5208
5286
  } catch (error) {
@@ -5210,13 +5288,27 @@ async function generateSentry(projectRoot) {
5210
5288
  throw error;
5211
5289
  }
5212
5290
  }
5291
+ async function wireRootLayout(projectRoot, ctx) {
5292
+ const layoutPath = path19.join(projectRoot, "src", "app", "layout.tsx");
5293
+ if (!await fs19.pathExists(layoutPath)) return;
5294
+ let content = await fs19.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 fs19.writeFile(layoutPath, content);
5304
+ }
5213
5305
  async function setConfigFlag12(projectRoot, ctx) {
5214
- const configPath = path17.join(projectRoot, "src", "config", "app.config.ts");
5215
- if (!await fs17.pathExists(configPath)) return;
5306
+ const configPath = path19.join(projectRoot, "src", "config", "app.config.ts");
5307
+ if (!await fs19.pathExists(configPath)) return;
5216
5308
  await ctx.trackModifiedFile(configPath);
5217
- const content = await fs17.readFile(configPath, "utf-8");
5309
+ const content = await fs19.readFile(configPath, "utf-8");
5218
5310
  const updated = content.replace(/sentry:\s*false/, "sentry: true");
5219
- await fs17.writeFile(configPath, updated);
5311
+ await fs19.writeFile(configPath, updated);
5220
5312
  }
5221
5313
  function types11() {
5222
5314
  return `${STAMP12}
@@ -5361,11 +5453,11 @@ var feature_flags_exports = {};
5361
5453
  __export(feature_flags_exports, {
5362
5454
  generateFeatureFlags: () => generateFeatureFlags
5363
5455
  });
5364
- import fs18 from "fs-extra";
5365
- import path18 from "path";
5456
+ import fs20 from "fs-extra";
5457
+ import path20 from "path";
5366
5458
  async function generateFeatureFlags(projectRoot) {
5367
- const featureDir = path18.join(projectRoot, "src", "features", "feature-flags");
5368
- if (await fs18.pathExists(featureDir)) {
5459
+ const featureDir = path20.join(projectRoot, "src", "features", "feature-flags");
5460
+ if (await fs20.pathExists(featureDir)) {
5369
5461
  log.error("Feature flags feature already exists at src/features/feature-flags/");
5370
5462
  return;
5371
5463
  }
@@ -5384,10 +5476,10 @@ async function generateFeatureFlags(projectRoot) {
5384
5476
  };
5385
5477
  let count = 0;
5386
5478
  for (const [filePath, content] of Object.entries(files)) {
5387
- const fullPath = path18.join(projectRoot, filePath);
5479
+ const fullPath = path20.join(projectRoot, filePath);
5388
5480
  ctx.trackCreatedFile(fullPath);
5389
- await fs18.ensureDir(path18.dirname(fullPath));
5390
- await fs18.writeFile(fullPath, content);
5481
+ await fs20.ensureDir(path20.dirname(fullPath));
5482
+ await fs20.writeFile(fullPath, content);
5391
5483
  count++;
5392
5484
  }
5393
5485
  await setConfigFlag13(projectRoot, ctx);
@@ -5407,12 +5499,12 @@ async function generateFeatureFlags(projectRoot) {
5407
5499
  }
5408
5500
  }
5409
5501
  async function setConfigFlag13(projectRoot, ctx) {
5410
- const configPath = path18.join(projectRoot, "src", "config", "app.config.ts");
5411
- if (!await fs18.pathExists(configPath)) return;
5502
+ const configPath = path20.join(projectRoot, "src", "config", "app.config.ts");
5503
+ if (!await fs20.pathExists(configPath)) return;
5412
5504
  await ctx.trackModifiedFile(configPath);
5413
- const content = await fs18.readFile(configPath, "utf-8");
5505
+ const content = await fs20.readFile(configPath, "utf-8");
5414
5506
  const updated = content.replace(/featureFlags:\s*false/, "featureFlags: true");
5415
- await fs18.writeFile(configPath, updated);
5507
+ await fs20.writeFile(configPath, updated);
5416
5508
  }
5417
5509
  function types12() {
5418
5510
  return `${STAMP13}
@@ -5650,30 +5742,57 @@ var init_feature_flags = __esm({
5650
5742
  // src/index.ts
5651
5743
  import { Command } from "commander";
5652
5744
 
5745
+ // src/utils/version.ts
5746
+ import { createRequire } from "module";
5747
+ import path from "path";
5748
+ import { fileURLToPath } from "url";
5749
+ import fs from "fs-extra";
5750
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
5751
+ function readVersionFromPackageJson(packageDir) {
5752
+ const require2 = createRequire(import.meta.url);
5753
+ const pkg = require2(path.join(packageDir, "package.json"));
5754
+ const v = pkg.version?.trim();
5755
+ return v ? v : "0.0.0";
5756
+ }
5757
+ function getCliVersion() {
5758
+ const candidates = [
5759
+ // Built bundle: __dirname = packages/cli/dist/
5760
+ path.resolve(__dirname, ".."),
5761
+ // Source (vitest): __dirname = packages/cli/src/utils/
5762
+ path.resolve(__dirname, "..", "..")
5763
+ ];
5764
+ for (const candidate of candidates) {
5765
+ if (fs.existsSync(path.join(candidate, "package.json"))) {
5766
+ return readVersionFromPackageJson(candidate);
5767
+ }
5768
+ }
5769
+ return "0.0.0";
5770
+ }
5771
+
5653
5772
  // src/commands/create.ts
5654
5773
  init_logger();
5655
- import fs20 from "fs-extra";
5656
- import path20 from "path";
5774
+ import fs22 from "fs-extra";
5775
+ import path22 from "path";
5657
5776
  import os3 from "os";
5658
5777
  import ora from "ora";
5659
5778
  import pc2 from "picocolors";
5660
5779
  import prompts5 from "prompts";
5661
5780
 
5662
5781
  // src/utils/template.ts
5663
- import path from "path";
5664
- import { fileURLToPath } from "url";
5665
- import fs from "fs-extra";
5666
- var __dirname = path.dirname(fileURLToPath(import.meta.url));
5782
+ import path2 from "path";
5783
+ import { fileURLToPath as fileURLToPath2 } from "url";
5784
+ import fs2 from "fs-extra";
5785
+ var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
5667
5786
  function getTemplatePath() {
5668
5787
  const candidates = [
5669
5788
  // Built bundle: __dirname = packages/cli/dist/
5670
- path.resolve(__dirname, "..", "template"),
5671
- path.resolve(__dirname, "..", "..", "..", "template"),
5789
+ path2.resolve(__dirname2, "..", "template"),
5790
+ path2.resolve(__dirname2, "..", "..", "..", "template"),
5672
5791
  // Source (vitest): __dirname = packages/cli/src/utils/
5673
- path.resolve(__dirname, "..", "..", "..", "..", "template")
5792
+ path2.resolve(__dirname2, "..", "..", "..", "..", "template")
5674
5793
  ];
5675
5794
  for (const candidate of candidates) {
5676
- if (fs.existsSync(candidate)) {
5795
+ if (fs2.existsSync(candidate)) {
5677
5796
  return candidate;
5678
5797
  }
5679
5798
  }
@@ -5682,7 +5801,7 @@ function getTemplatePath() {
5682
5801
  );
5683
5802
  }
5684
5803
  function resolveProjectPath(projectName) {
5685
- return path.resolve(process.cwd(), projectName);
5804
+ return path2.resolve(process.cwd(), projectName);
5686
5805
  }
5687
5806
 
5688
5807
  // src/prompts/project-info.ts
@@ -6032,7 +6151,6 @@ var DESIGN_DIRECTIONS = [
6032
6151
  function getDefaultTheme() {
6033
6152
  return {
6034
6153
  primaryColor: COLOR_PRESETS[0].value,
6035
- secondaryColor: "amber-400",
6036
6154
  font: FONT_CHOICES[0].value,
6037
6155
  designDirection: DESIGN_DIRECTIONS[0].value
6038
6156
  };
@@ -6073,7 +6191,6 @@ async function promptTheme() {
6073
6191
  if (response.primaryColor === void 0) return null;
6074
6192
  return {
6075
6193
  primaryColor: response.primaryColor,
6076
- secondaryColor: "amber-400",
6077
6194
  font: response.font,
6078
6195
  designDirection: response.designDirection
6079
6196
  };
@@ -6081,8 +6198,8 @@ async function promptTheme() {
6081
6198
 
6082
6199
  // src/generators/scaffold.ts
6083
6200
  init_logger();
6084
- import fs2 from "fs-extra";
6085
- import path2 from "path";
6201
+ import fs3 from "fs-extra";
6202
+ import path3 from "path";
6086
6203
  import { generateBrandCss } from "@mars-stack/ui/utils";
6087
6204
 
6088
6205
  // src/generators/app-config.ts
@@ -6125,15 +6242,7 @@ function generateAppConfig(config) {
6125
6242
  address: '',
6126
6243
  },
6127
6244
  theme: {
6128
- primaryColor: '${config.theme.primaryColor}' as string,
6129
- secondaryColor: '${config.theme.secondaryColor}' as string,
6130
6245
  font: '${config.theme.font}' as string,
6131
- designDirection: '${config.theme.designDirection}' as
6132
- | 'modern-saas'
6133
- | 'minimal'
6134
- | 'enterprise'
6135
- | 'creative'
6136
- | 'dashboard',
6137
6246
  },
6138
6247
  features: {
6139
6248
  ${featureEntries}
@@ -6274,17 +6383,17 @@ var IGNORED_FILES = [
6274
6383
  async function copyTemplateFiles(templateDir, targetDir) {
6275
6384
  let fileCount = 0;
6276
6385
  async function walkAndCopy(src, dest) {
6277
- const entries = await fs2.readdir(src, { withFileTypes: true });
6386
+ const entries = await fs3.readdir(src, { withFileTypes: true });
6278
6387
  for (const entry of entries) {
6279
- const srcPath = path2.join(src, entry.name);
6280
- const destPath = path2.join(dest, entry.name);
6388
+ const srcPath = path3.join(src, entry.name);
6389
+ const destPath = path3.join(dest, entry.name);
6281
6390
  if (entry.isDirectory()) {
6282
6391
  if (IGNORED_DIRS.includes(entry.name)) continue;
6283
- await fs2.ensureDir(destPath);
6392
+ await fs3.ensureDir(destPath);
6284
6393
  await walkAndCopy(srcPath, destPath);
6285
6394
  } else {
6286
6395
  if (IGNORED_FILES.includes(entry.name)) continue;
6287
- await fs2.copy(srcPath, destPath);
6396
+ await fs3.copy(srcPath, destPath);
6288
6397
  fileCount++;
6289
6398
  }
6290
6399
  }
@@ -6295,19 +6404,19 @@ async function copyTemplateFiles(templateDir, targetDir) {
6295
6404
  function resolveMarsPackageRange(packageName, templateDir) {
6296
6405
  const shortName = packageName.replace("@mars-stack/", "");
6297
6406
  const candidates = [
6298
- path2.resolve(templateDir, "..", "packages", shortName, "package.json"),
6299
- path2.resolve(templateDir, "..", "..", "packages", shortName, "package.json")
6407
+ path3.resolve(templateDir, "..", "packages", shortName, "package.json"),
6408
+ path3.resolve(templateDir, "..", "..", "packages", shortName, "package.json")
6300
6409
  ];
6301
6410
  for (const candidate of candidates) {
6302
- if (fs2.existsSync(candidate)) {
6303
- const pkg = fs2.readJsonSync(candidate);
6411
+ if (fs3.existsSync(candidate)) {
6412
+ const pkg = fs3.readJsonSync(candidate);
6304
6413
  if (typeof pkg.version === "string") {
6305
6414
  const [major, minor] = pkg.version.split(".");
6306
6415
  return `^${major}.${minor}.0`;
6307
6416
  }
6308
6417
  }
6309
6418
  }
6310
- const templatePkg = fs2.readJsonSync(path2.join(templateDir, "package.json"));
6419
+ const templatePkg = fs3.readJsonSync(path3.join(templateDir, "package.json"));
6311
6420
  const existing = templatePkg.dependencies?.[packageName];
6312
6421
  if (existing && existing !== "*") {
6313
6422
  if (/^[\^~>=]/.test(existing)) return existing;
@@ -6318,8 +6427,8 @@ function resolveMarsPackageRange(packageName, templateDir) {
6318
6427
  }
6319
6428
  function generatePackageJson(config) {
6320
6429
  const templateDir = getTemplatePath();
6321
- const templatePkg = path2.join(templateDir, "package.json");
6322
- const pkg = fs2.readJsonSync(templatePkg);
6430
+ const templatePkg = path3.join(templateDir, "package.json");
6431
+ const pkg = fs3.readJsonSync(templatePkg);
6323
6432
  pkg.name = config.name;
6324
6433
  pkg.version = "0.1.0";
6325
6434
  pkg.private = true;
@@ -6518,9 +6627,9 @@ async function pruneDisabledFeatures(features, targetDir) {
6518
6627
  const flagValue = features[feature];
6519
6628
  if (flagValue !== false) continue;
6520
6629
  for (const relativePath of paths) {
6521
- const fullPath = path2.join(targetDir, relativePath);
6522
- if (await fs2.pathExists(fullPath)) {
6523
- await fs2.remove(fullPath);
6630
+ const fullPath = path3.join(targetDir, relativePath);
6631
+ if (await fs3.pathExists(fullPath)) {
6632
+ await fs3.remove(fullPath);
6524
6633
  log.step(`Pruned disabled feature path: ${relativePath}`);
6525
6634
  }
6526
6635
  }
@@ -6529,43 +6638,43 @@ async function pruneDisabledFeatures(features, targetDir) {
6529
6638
  }
6530
6639
  }
6531
6640
  if (relationsToRemove.length > 0) {
6532
- const authPath = path2.join(targetDir, "prisma", "schema", "auth.prisma");
6533
- if (await fs2.pathExists(authPath)) {
6534
- let content = await fs2.readFile(authPath, "utf-8");
6641
+ const authPath = path3.join(targetDir, "prisma", "schema", "auth.prisma");
6642
+ if (await fs3.pathExists(authPath)) {
6643
+ let content = await fs3.readFile(authPath, "utf-8");
6535
6644
  for (const field of relationsToRemove) {
6536
6645
  content = content.replace(new RegExp(`\\s*${field}\\s+\\S+.*\\n`, "g"), "\n");
6537
6646
  }
6538
- await fs2.writeFile(authPath, content);
6647
+ await fs3.writeFile(authPath, content);
6539
6648
  }
6540
6649
  }
6541
6650
  }
6542
6651
  async function scaffoldProject(targetDir, config) {
6543
6652
  const templateDir = getTemplatePath();
6544
- if (!await fs2.pathExists(templateDir)) {
6653
+ if (!await fs3.pathExists(templateDir)) {
6545
6654
  throw new Error(`Template directory not found: ${templateDir}`);
6546
6655
  }
6547
- await fs2.ensureDir(targetDir);
6656
+ await fs3.ensureDir(targetDir);
6548
6657
  const fileCount = await copyTemplateFiles(templateDir, targetDir);
6549
6658
  await pruneDisabledFeatures(config.features, targetDir);
6550
- await fs2.writeFile(path2.join(targetDir, "package.json"), generatePackageJson(config));
6551
- await fs2.writeFile(
6552
- path2.join(targetDir, "src", "config", "app.config.ts"),
6659
+ await fs3.writeFile(path3.join(targetDir, "package.json"), generatePackageJson(config));
6660
+ await fs3.writeFile(
6661
+ path3.join(targetDir, "src", "config", "app.config.ts"),
6553
6662
  generateAppConfig(config)
6554
6663
  );
6555
- await fs2.writeFile(path2.join(targetDir, "docker-compose.yml"), generateDockerCompose(config));
6556
- await fs2.writeFile(path2.join(targetDir, "src", "app", "layout.tsx"), generateLayout(config));
6557
- await fs2.writeFile(path2.join(targetDir, ".env"), generateEnvFile(config));
6558
- await fs2.writeFile(path2.join(targetDir, ".env.example"), generateEnvExample(config));
6559
- await fs2.writeFile(path2.join(targetDir, ".gitignore"), generateGitignore());
6664
+ await fs3.writeFile(path3.join(targetDir, "docker-compose.yml"), generateDockerCompose(config));
6665
+ await fs3.writeFile(path3.join(targetDir, "src", "app", "layout.tsx"), generateLayout(config));
6666
+ await fs3.writeFile(path3.join(targetDir, ".env"), generateEnvFile(config));
6667
+ await fs3.writeFile(path3.join(targetDir, ".env.example"), generateEnvExample(config));
6668
+ await fs3.writeFile(path3.join(targetDir, ".gitignore"), generateGitignore());
6560
6669
  await patchGlobalsCssForScaffoldedProject(targetDir, config);
6561
6670
  await generateBrandCssForProject(targetDir, config);
6562
6671
  return { fileCount };
6563
6672
  }
6564
6673
  async function generateBrandCssForProject(targetDir, config) {
6565
- const brandPath = path2.join(targetDir, "src", "styles", "brand.css");
6566
- if (!await fs2.pathExists(brandPath)) return;
6674
+ const brandPath = path3.join(targetDir, "src", "styles", "brand.css");
6675
+ if (!await fs3.pathExists(brandPath)) return;
6567
6676
  const css = generateBrandCss(config.theme.primaryColor);
6568
- await fs2.writeFile(brandPath, css);
6677
+ await fs3.writeFile(brandPath, css);
6569
6678
  }
6570
6679
  var VALID_DESIGN_DIRECTIONS = [
6571
6680
  "modern-saas",
@@ -6575,9 +6684,9 @@ var VALID_DESIGN_DIRECTIONS = [
6575
6684
  "dashboard"
6576
6685
  ];
6577
6686
  async function patchGlobalsCssForScaffoldedProject(targetDir, config) {
6578
- const globalsPath = path2.join(targetDir, "src", "styles", "globals.css");
6579
- if (!await fs2.pathExists(globalsPath)) return;
6580
- let content = await fs2.readFile(globalsPath, "utf-8");
6687
+ const globalsPath = path3.join(targetDir, "src", "styles", "globals.css");
6688
+ if (!await fs3.pathExists(globalsPath)) return;
6689
+ let content = await fs3.readFile(globalsPath, "utf-8");
6581
6690
  content = content.replace(
6582
6691
  '@source "../../../node_modules/@mars-stack/ui/dist/**/*.js";',
6583
6692
  '@source "../../node_modules/@mars-stack/ui/dist/**/*.js";'
@@ -6596,7 +6705,7 @@ async function patchGlobalsCssForScaffoldedProject(targetDir, config) {
6596
6705
  ${newImport}`
6597
6706
  );
6598
6707
  }
6599
- await fs2.writeFile(globalsPath, content);
6708
+ await fs3.writeFile(globalsPath, content);
6600
6709
  }
6601
6710
 
6602
6711
  // src/generators/generate-selected.ts
@@ -6712,13 +6821,13 @@ async function generateSelectedFeatures(projectRoot, features) {
6712
6821
  }
6713
6822
 
6714
6823
  // src/utils/telemetry.ts
6715
- import fs19 from "fs-extra";
6716
- import path19 from "path";
6824
+ import fs21 from "fs-extra";
6825
+ import path21 from "path";
6717
6826
  import os2 from "os";
6718
- var RC_PATH = path19.join(os2.homedir(), ".marsrc");
6827
+ var RC_PATH = path21.join(os2.homedir(), ".marsrc");
6719
6828
  function isTelemetryEnabled() {
6720
6829
  try {
6721
- const config = fs19.readJsonSync(RC_PATH);
6830
+ const config = fs21.readJsonSync(RC_PATH);
6722
6831
  return config.enabled === true;
6723
6832
  } catch {
6724
6833
  return false;
@@ -6727,16 +6836,16 @@ function isTelemetryEnabled() {
6727
6836
  function enableTelemetry() {
6728
6837
  const config = loadOrCreateConfig();
6729
6838
  config.enabled = true;
6730
- fs19.writeJsonSync(RC_PATH, config, { spaces: 2 });
6839
+ fs21.writeJsonSync(RC_PATH, config, { spaces: 2 });
6731
6840
  }
6732
6841
  function disableTelemetry() {
6733
6842
  const config = loadOrCreateConfig();
6734
6843
  config.enabled = false;
6735
- fs19.writeJsonSync(RC_PATH, config, { spaces: 2 });
6844
+ fs21.writeJsonSync(RC_PATH, config, { spaces: 2 });
6736
6845
  }
6737
6846
  function loadOrCreateConfig() {
6738
6847
  try {
6739
- return fs19.readJsonSync(RC_PATH);
6848
+ return fs21.readJsonSync(RC_PATH);
6740
6849
  } catch {
6741
6850
  return { enabled: false, anonymousId: crypto.randomUUID() };
6742
6851
  }
@@ -6756,13 +6865,13 @@ function trackEvent(event, properties) {
6756
6865
  },
6757
6866
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6758
6867
  };
6759
- const logPath = path19.join(os2.homedir(), ".mars-telemetry.log");
6760
- fs19.appendFile(logPath, JSON.stringify(payload) + "\n").catch(() => {
6868
+ const logPath = path21.join(os2.homedir(), ".mars-telemetry.log");
6869
+ fs21.appendFile(logPath, JSON.stringify(payload) + "\n").catch(() => {
6761
6870
  });
6762
6871
  }
6763
6872
 
6764
6873
  // src/commands/create.ts
6765
- var RC_PATH2 = path20.join(os3.homedir(), ".marsrc");
6874
+ var RC_PATH2 = path22.join(os3.homedir(), ".marsrc");
6766
6875
  var PROJECT_NAME_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
6767
6876
  var MAX_PROJECT_NAME_LENGTH = 214;
6768
6877
  async function createCommand(projectName, options) {
@@ -6789,8 +6898,8 @@ async function createCommand(projectName, options) {
6789
6898
  const projectInfo = useDefaults ? getDefaultProjectInfo(projectName) : await promptProjectInfo(projectName);
6790
6899
  if (!projectInfo) return;
6791
6900
  const targetDir = resolveProjectPath(projectInfo.name);
6792
- if (await fs20.pathExists(targetDir)) {
6793
- const entries = await fs20.readdir(targetDir);
6901
+ if (await fs22.pathExists(targetDir)) {
6902
+ const entries = await fs22.readdir(targetDir);
6794
6903
  if (entries.length > 0) {
6795
6904
  log.error(`Directory "${projectInfo.name}" already exists and is not empty.`);
6796
6905
  return;
@@ -6831,7 +6940,7 @@ async function createCommand(projectName, options) {
6831
6940
  log.info("Scaffolding cancelled.");
6832
6941
  process.exit(0);
6833
6942
  }
6834
- if (!fs20.pathExistsSync(RC_PATH2)) {
6943
+ if (!fs22.pathExistsSync(RC_PATH2)) {
6835
6944
  const { telemetryOptIn } = await prompts5(
6836
6945
  {
6837
6946
  type: "confirm",
@@ -6870,10 +6979,10 @@ async function createCommand(projectName, options) {
6870
6979
  } catch (err) {
6871
6980
  spinner.fail("Failed to scaffold project");
6872
6981
  log.error(err instanceof Error ? err.message : String(err));
6873
- if (await fs20.pathExists(targetDir)) {
6982
+ if (await fs22.pathExists(targetDir)) {
6874
6983
  const cleanupSpinner = ora("Cleaning up...").start();
6875
6984
  try {
6876
- await fs20.remove(targetDir);
6985
+ await fs22.remove(targetDir);
6877
6986
  cleanupSpinner.succeed("Cleaned up partial scaffold");
6878
6987
  } catch {
6879
6988
  cleanupSpinner.warn(`Could not clean up ${targetDir}. You may need to remove it manually.`);
@@ -6888,8 +6997,8 @@ function countEnabled(flags) {
6888
6997
 
6889
6998
  // src/commands/doctor.ts
6890
6999
  init_logger();
6891
- import fs21 from "fs-extra";
6892
- import path21 from "path";
7000
+ import fs23 from "fs-extra";
7001
+ import path23 from "path";
6893
7002
  import { execSync } from "child_process";
6894
7003
  function commandExists(cmd) {
6895
7004
  try {
@@ -6909,12 +7018,12 @@ function getVersion(cmd) {
6909
7018
  async function checkForUpgrades() {
6910
7019
  log.blank();
6911
7020
  log.title("Upgrade Check");
6912
- const packageJsonPath = path21.join(process.cwd(), "package.json");
6913
- if (!fs21.pathExistsSync(packageJsonPath)) {
7021
+ const packageJsonPath = path23.join(process.cwd(), "package.json");
7022
+ if (!fs23.pathExistsSync(packageJsonPath)) {
6914
7023
  log.warn("No package.json found \u2014 skipping upgrade check.");
6915
7024
  return;
6916
7025
  }
6917
- const packageJson = fs21.readJsonSync(packageJsonPath);
7026
+ const packageJson = fs23.readJsonSync(packageJsonPath);
6918
7027
  const deps = packageJson.dependencies ?? {};
6919
7028
  const devDeps = packageJson.devDependencies ?? {};
6920
7029
  const currentRaw = deps["@mars-stack/core"] ?? devDeps["@mars-stack/core"];
@@ -6976,25 +7085,25 @@ async function doctorCommand(options) {
6976
7085
  },
6977
7086
  {
6978
7087
  name: "package.json exists",
6979
- check: () => fs21.pathExistsSync(path21.join(process.cwd(), "package.json"))
7088
+ check: () => fs23.pathExistsSync(path23.join(process.cwd(), "package.json"))
6980
7089
  },
6981
7090
  {
6982
7091
  name: ".env file exists",
6983
- check: () => fs21.pathExistsSync(path21.join(process.cwd(), ".env"))
7092
+ check: () => fs23.pathExistsSync(path23.join(process.cwd(), ".env"))
6984
7093
  },
6985
7094
  {
6986
7095
  name: "Prisma schema exists",
6987
- check: () => fs21.pathExistsSync(path21.join(process.cwd(), "prisma", "schema")) || fs21.pathExistsSync(path21.join(process.cwd(), "prisma", "schema.prisma"))
7096
+ check: () => fs23.pathExistsSync(path23.join(process.cwd(), "prisma", "schema")) || fs23.pathExistsSync(path23.join(process.cwd(), "prisma", "schema.prisma"))
6988
7097
  },
6989
7098
  {
6990
7099
  name: "node_modules installed",
6991
- check: () => fs21.pathExistsSync(path21.join(process.cwd(), "node_modules"))
7100
+ check: () => fs23.pathExistsSync(path23.join(process.cwd(), "node_modules"))
6992
7101
  },
6993
7102
  {
6994
7103
  name: "DATABASE_URL set",
6995
7104
  check: () => {
6996
7105
  try {
6997
- const envContent = fs21.readFileSync(path21.join(process.cwd(), ".env"), "utf-8");
7106
+ const envContent = fs23.readFileSync(path23.join(process.cwd(), ".env"), "utf-8");
6998
7107
  const match = envContent.match(/^DATABASE_URL=(.+)$/m);
6999
7108
  return match ? match[1] !== "" : false;
7000
7109
  } catch {
@@ -7006,7 +7115,7 @@ async function doctorCommand(options) {
7006
7115
  name: "JWT_SECRET set",
7007
7116
  check: () => {
7008
7117
  try {
7009
- const envContent = fs21.readFileSync(path21.join(process.cwd(), ".env"), "utf-8");
7118
+ const envContent = fs23.readFileSync(path23.join(process.cwd(), ".env"), "utf-8");
7010
7119
  const match = envContent.match(/^JWT_SECRET=(.+)$/m);
7011
7120
  if (!match || !match[1]) return false;
7012
7121
  return match[1].length >= 32 ? true : "set but too short (need >=32 chars)";
@@ -7051,16 +7160,16 @@ async function doctorCommand(options) {
7051
7160
 
7052
7161
  // src/commands/add.ts
7053
7162
  init_logger();
7054
- import fs23 from "fs-extra";
7055
- import path23 from "path";
7163
+ import fs25 from "fs-extra";
7164
+ import path25 from "path";
7056
7165
  import pc3 from "picocolors";
7057
7166
 
7058
7167
  // src/utils/doc-updater.ts
7059
- import fs22 from "fs-extra";
7060
- import path22 from "path";
7168
+ import fs24 from "fs-extra";
7169
+ import path24 from "path";
7061
7170
  async function appendToDbSchema(projectDir, modelName, fields) {
7062
- const filePath = path22.join(projectDir, "docs", "generated", "db-schema.md");
7063
- if (!await fs22.pathExists(filePath)) return;
7171
+ const filePath = path24.join(projectDir, "docs", "generated", "db-schema.md");
7172
+ if (!await fs24.pathExists(filePath)) return;
7064
7173
  const entry = [
7065
7174
  "",
7066
7175
  `### ${modelName}`,
@@ -7070,12 +7179,12 @@ async function appendToDbSchema(projectDir, modelName, fields) {
7070
7179
  ...fields.map((f) => `| ${f} | |`),
7071
7180
  ""
7072
7181
  ].join("\n");
7073
- await fs22.appendFile(filePath, entry);
7182
+ await fs24.appendFile(filePath, entry);
7074
7183
  }
7075
7184
  async function updateQualityScore(projectDir, domain, grade) {
7076
- const filePath = path22.join(projectDir, "docs", "QUALITY_SCORE.md");
7077
- if (!await fs22.pathExists(filePath)) return;
7078
- const content = await fs22.readFile(filePath, "utf-8");
7185
+ const filePath = path24.join(projectDir, "docs", "QUALITY_SCORE.md");
7186
+ if (!await fs24.pathExists(filePath)) return;
7187
+ const content = await fs24.readFile(filePath, "utf-8");
7079
7188
  const pattern = new RegExp(`^\\|\\s*${escapeRegex(domain)}\\s*\\|`, "m");
7080
7189
  if (pattern.test(content)) {
7081
7190
  const updated = content.replace(pattern, (match) => {
@@ -7083,11 +7192,11 @@ async function updateQualityScore(projectDir, domain, grade) {
7083
7192
  parts[2] = ` ${grade} `;
7084
7193
  return parts.join("|");
7085
7194
  });
7086
- await fs22.writeFile(filePath, updated);
7195
+ await fs24.writeFile(filePath, updated);
7087
7196
  } else {
7088
7197
  const row = `| ${domain} | ${grade} | |
7089
7198
  `;
7090
- await fs22.appendFile(filePath, row);
7199
+ await fs24.appendFile(filePath, row);
7091
7200
  }
7092
7201
  }
7093
7202
  function escapeRegex(str) {
@@ -7098,8 +7207,8 @@ function escapeRegex(str) {
7098
7207
  init_rollback();
7099
7208
  function ensureInProject() {
7100
7209
  const cwd = process.cwd();
7101
- const configPath = path23.join(cwd, "src", "config", "app.config.ts");
7102
- if (!fs23.pathExistsSync(configPath)) {
7210
+ const configPath = path25.join(cwd, "src", "config", "app.config.ts");
7211
+ if (!fs25.pathExistsSync(configPath)) {
7103
7212
  log.error("Not inside a MARS project. Run this command from the project root.");
7104
7213
  process.exit(1);
7105
7214
  }
@@ -7132,8 +7241,8 @@ async function addFeatureCommand(name) {
7132
7241
  const kebab = toKebab(name);
7133
7242
  const pascal = toPascal(name);
7134
7243
  const camel = toCamel(name);
7135
- const featureDir = path23.join(root, "src", "features", kebab);
7136
- if (await fs23.pathExists(featureDir)) {
7244
+ const featureDir = path25.join(root, "src", "features", kebab);
7245
+ if (await fs25.pathExists(featureDir)) {
7137
7246
  log.error(`Feature "${kebab}" already exists at src/features/${kebab}/`);
7138
7247
  return;
7139
7248
  }
@@ -7198,9 +7307,9 @@ export type Update${pascal}Input = z.infer<typeof ${camel}Schemas.update>;
7198
7307
  ctx.trackCreatedFile(featureDir);
7199
7308
  let count = 0;
7200
7309
  for (const [filePath, content] of Object.entries(files)) {
7201
- const fullPath = path23.join(featureDir, filePath);
7202
- await fs23.ensureDir(path23.dirname(fullPath));
7203
- await fs23.writeFile(fullPath, content);
7310
+ const fullPath = path25.join(featureDir, filePath);
7311
+ await fs25.ensureDir(path25.dirname(fullPath));
7312
+ await fs25.writeFile(fullPath, content);
7204
7313
  count++;
7205
7314
  }
7206
7315
  await ctx.commit();
@@ -7221,8 +7330,8 @@ async function addPageCommand(routePath, options) {
7221
7330
  const root = ensureInProject();
7222
7331
  const cleanPath = routePath.startsWith("/") ? routePath.slice(1) : routePath;
7223
7332
  const group = options.protected ? "(protected)" : "(public)";
7224
- const pageDir = path23.join(root, "src", "app", group, cleanPath);
7225
- if (await fs23.pathExists(path23.join(pageDir, "page.tsx"))) {
7333
+ const pageDir = path25.join(root, "src", "app", group, cleanPath);
7334
+ if (await fs25.pathExists(path25.join(pageDir, "page.tsx"))) {
7226
7335
  log.error(`Page already exists at src/app/${group}/${cleanPath}/page.tsx`);
7227
7336
  return;
7228
7337
  }
@@ -7270,9 +7379,9 @@ export default function Loading() {
7270
7379
  const ctx = createRollbackContext();
7271
7380
  try {
7272
7381
  ctx.trackCreatedFile(pageDir);
7273
- await fs23.ensureDir(pageDir);
7274
- await fs23.writeFile(path23.join(pageDir, "page.tsx"), pageContent);
7275
- await fs23.writeFile(path23.join(pageDir, "loading.tsx"), loadingContent);
7382
+ await fs25.ensureDir(pageDir);
7383
+ await fs25.writeFile(path25.join(pageDir, "page.tsx"), pageContent);
7384
+ await fs25.writeFile(path25.join(pageDir, "loading.tsx"), loadingContent);
7276
7385
  await ctx.commit();
7277
7386
  log.success(`Created page at ${pc3.bold(`src/app/${group}/${cleanPath}/`)}`);
7278
7387
  trackEvent("add", { type: "page" });
@@ -7289,9 +7398,9 @@ async function addModelCommand(name) {
7289
7398
  const pascal = toPascal(name);
7290
7399
  const camel = toCamel(name);
7291
7400
  const kebab = toKebab(name);
7292
- const schemaDir = path23.join(root, "prisma", "schema");
7293
- const schemaFile = path23.join(schemaDir, `${kebab}.prisma`);
7294
- if (await fs23.pathExists(schemaFile)) {
7401
+ const schemaDir = path25.join(root, "prisma", "schema");
7402
+ const schemaFile = path25.join(schemaDir, `${kebab}.prisma`);
7403
+ if (await fs25.pathExists(schemaFile)) {
7295
7404
  log.error(`Schema file already exists: prisma/schema/${kebab}.prisma`);
7296
7405
  return;
7297
7406
  }
@@ -7309,8 +7418,8 @@ async function addModelCommand(name) {
7309
7418
  const ctx = createRollbackContext();
7310
7419
  try {
7311
7420
  ctx.trackCreatedFile(schemaFile);
7312
- await fs23.ensureDir(schemaDir);
7313
- await fs23.writeFile(schemaFile, content);
7421
+ await fs25.ensureDir(schemaDir);
7422
+ await fs25.writeFile(schemaFile, content);
7314
7423
  await ctx.commit();
7315
7424
  log.success(`Created model ${pc3.bold(pascal)} at prisma/schema/${kebab}.prisma`);
7316
7425
  trackEvent("add", { type: "model" });
@@ -7332,9 +7441,9 @@ async function addEmailCommand(name) {
7332
7441
  const kebab = toKebab(name);
7333
7442
  const pascal = toPascal(name);
7334
7443
  const camel = toCamel(name);
7335
- const templatesDir = path23.join(root, "src", "lib", "core", "email", "templates");
7336
- const filePath = path23.join(templatesDir, `${kebab}-email.ts`);
7337
- if (await fs23.pathExists(filePath)) {
7444
+ const templatesDir = path25.join(root, "src", "lib", "core", "email", "templates");
7445
+ const filePath = path25.join(templatesDir, `${kebab}-email.ts`);
7446
+ if (await fs25.pathExists(filePath)) {
7338
7447
  log.error(`Email template already exists: src/lib/core/email/templates/${kebab}-email.ts`);
7339
7448
  return;
7340
7449
  }
@@ -7378,25 +7487,25 @@ export function ${functionName}({ appName, actionUrl, userName }: ${pascal}Email
7378
7487
  }
7379
7488
  `;
7380
7489
  const ctx = createRollbackContext();
7381
- const indexPath = path23.join(templatesDir, "index.ts");
7490
+ const indexPath = path25.join(templatesDir, "index.ts");
7382
7491
  try {
7383
7492
  ctx.trackCreatedFile(filePath);
7384
- if (await fs23.pathExists(indexPath)) {
7493
+ if (await fs25.pathExists(indexPath)) {
7385
7494
  await ctx.trackModifiedFile(indexPath);
7386
7495
  } else {
7387
7496
  ctx.trackCreatedFile(indexPath);
7388
7497
  }
7389
- await fs23.ensureDir(templatesDir);
7390
- await fs23.writeFile(filePath, content);
7498
+ await fs25.ensureDir(templatesDir);
7499
+ await fs25.writeFile(filePath, content);
7391
7500
  const exportLine = `export { ${functionName} } from './${kebab}-email';
7392
7501
  `;
7393
- if (await fs23.pathExists(indexPath)) {
7394
- const existing = await fs23.readFile(indexPath, "utf-8");
7502
+ if (await fs25.pathExists(indexPath)) {
7503
+ const existing = await fs25.readFile(indexPath, "utf-8");
7395
7504
  if (!existing.includes(functionName)) {
7396
- await fs23.appendFile(indexPath, exportLine);
7505
+ await fs25.appendFile(indexPath, exportLine);
7397
7506
  }
7398
7507
  } else {
7399
- await fs23.writeFile(indexPath, exportLine);
7508
+ await fs25.writeFile(indexPath, exportLine);
7400
7509
  }
7401
7510
  await ctx.commit();
7402
7511
  log.success(`Created email template ${pc3.bold(kebab)} at src/lib/core/email/templates/${kebab}-email.ts`);
@@ -7419,10 +7528,10 @@ async function addComponentCommand(name, options) {
7419
7528
  log.error(`Invalid type "${type}". Use: ${validTypes.join(", ")}`);
7420
7529
  return;
7421
7530
  }
7422
- const dir = type === "primitive" ? path23.join(root, "src", "components", "primitives") : path23.join(root, "src", "components", "patterns");
7423
- await fs23.ensureDir(dir);
7424
- const filePath = path23.join(dir, `${pascal}.tsx`);
7425
- if (await fs23.pathExists(filePath)) {
7531
+ const dir = type === "primitive" ? path25.join(root, "src", "components", "primitives") : path25.join(root, "src", "components", "patterns");
7532
+ await fs25.ensureDir(dir);
7533
+ const filePath = path25.join(dir, `${pascal}.tsx`);
7534
+ if (await fs25.pathExists(filePath)) {
7426
7535
  log.error(`Component already exists: ${pascal}.tsx`);
7427
7536
  return;
7428
7537
  }
@@ -7471,9 +7580,9 @@ export function ${pascal}({ children, className }: ${pascal}Props) {
7471
7580
  const ctx = createRollbackContext();
7472
7581
  try {
7473
7582
  ctx.trackCreatedFile(filePath);
7474
- await fs23.writeFile(filePath, content);
7583
+ await fs25.writeFile(filePath, content);
7475
7584
  await ctx.commit();
7476
- log.success(`Created ${type} component ${pc3.bold(pascal)} at ${path23.relative(root, filePath)}`);
7585
+ log.success(`Created ${type} component ${pc3.bold(pascal)} at ${path25.relative(root, filePath)}`);
7477
7586
  trackEvent("add", { type: "component", componentType: type });
7478
7587
  log.blank();
7479
7588
  log.warn(`Remember to add the export to the barrel file:`);
@@ -7488,14 +7597,14 @@ export function ${pascal}({ children, className }: ${pascal}Props) {
7488
7597
  // src/commands/configure.ts
7489
7598
  init_logger();
7490
7599
  import { execSync as execSync2 } from "child_process";
7491
- import fs24 from "fs-extra";
7492
- import path24 from "path";
7600
+ import fs26 from "fs-extra";
7601
+ import path26 from "path";
7493
7602
  import pc4 from "picocolors";
7494
7603
  import prompts6 from "prompts";
7495
7604
  function ensureInProject2() {
7496
7605
  const cwd = process.cwd();
7497
- const configPath = path24.join(cwd, "src", "config", "app.config.ts");
7498
- if (!fs24.pathExistsSync(configPath)) {
7606
+ const configPath = path26.join(cwd, "src", "config", "app.config.ts");
7607
+ if (!fs26.pathExistsSync(configPath)) {
7499
7608
  log.error("Not inside a MARS project. Run this command from the project root.");
7500
7609
  process.exit(1);
7501
7610
  }
@@ -7509,8 +7618,8 @@ var PROVIDER_DEPENDENCIES = {
7509
7618
  "storage:s3": ["@aws-sdk/client-s3", "@aws-sdk/s3-request-presigner"]
7510
7619
  };
7511
7620
  function updateAppConfig(projectDir, serviceKey, provider, featureKey) {
7512
- const configPath = path24.join(projectDir, "src", "config", "app.config.ts");
7513
- let content = fs24.readFileSync(configPath, "utf-8");
7621
+ const configPath = path26.join(projectDir, "src", "config", "app.config.ts");
7622
+ let content = fs26.readFileSync(configPath, "utf-8");
7514
7623
  if (serviceKey === "auth") {
7515
7624
  const boolValue = provider === "google" ? "true" : "false";
7516
7625
  const featureRegex = new RegExp(`(googleOAuth\\s*:\\s*)(?:true|false)`);
@@ -7525,10 +7634,10 @@ function updateAppConfig(projectDir, serviceKey, provider, featureKey) {
7525
7634
  content = content.replace(featureRegex, `$1true`);
7526
7635
  }
7527
7636
  }
7528
- fs24.writeFileSync(configPath, content);
7637
+ fs26.writeFileSync(configPath, content);
7529
7638
  }
7530
7639
  function detectPackageManager(projectDir) {
7531
- if (fs24.existsSync(path24.join(projectDir, "yarn.lock"))) return "yarn";
7640
+ if (fs26.existsSync(path26.join(projectDir, "yarn.lock"))) return "yarn";
7532
7641
  return "npm";
7533
7642
  }
7534
7643
  function installDependencies(projectDir, deps) {
@@ -7638,15 +7747,15 @@ async function configureCommand(service) {
7638
7747
  log.step(`Manually set ${pc4.bold(`services.${serviceConfig.configKey}.provider`)} to ${pc4.bold(`'${provider}'`)} in ${pc4.dim("src/config/app.config.ts")}`);
7639
7748
  }
7640
7749
  if (selectedProvider.envVars.length > 0) {
7641
- const envPath = path24.join(root, ".env");
7642
- if (await fs24.pathExists(envPath)) {
7643
- const envContent = await fs24.readFile(envPath, "utf-8");
7750
+ const envPath = path26.join(root, ".env");
7751
+ if (await fs26.pathExists(envPath)) {
7752
+ const envContent = await fs26.readFile(envPath, "utf-8");
7644
7753
  const missingVars = selectedProvider.envVars.filter(
7645
7754
  (v) => !envContent.includes(v)
7646
7755
  );
7647
7756
  if (missingVars.length > 0) {
7648
7757
  const additions = missingVars.map((v) => `${v}=""`).join("\n");
7649
- await fs24.appendFile(envPath, `
7758
+ await fs26.appendFile(envPath, `
7650
7759
  # ${selectedService} (${provider})
7651
7760
  ${additions}
7652
7761
  `);
@@ -7675,15 +7784,15 @@ ${additions}
7675
7784
 
7676
7785
  // src/commands/deploy.ts
7677
7786
  init_logger();
7678
- import fs25 from "fs-extra";
7679
- import path25 from "path";
7787
+ import fs27 from "fs-extra";
7788
+ import path27 from "path";
7680
7789
  import { execSync as execSync3 } from "child_process";
7681
7790
  import pc5 from "picocolors";
7682
7791
  import prompts7 from "prompts";
7683
7792
  function ensureInProject3() {
7684
7793
  const cwd = process.cwd();
7685
- const configPath = path25.join(cwd, "src", "config", "app.config.ts");
7686
- if (!fs25.pathExistsSync(configPath)) {
7794
+ const configPath = path27.join(cwd, "src", "config", "app.config.ts");
7795
+ if (!fs27.pathExistsSync(configPath)) {
7687
7796
  log.error("Not inside a MARS project. Run this command from the project root.");
7688
7797
  process.exit(1);
7689
7798
  }
@@ -7722,8 +7831,8 @@ async function deployCommand() {
7722
7831
  return;
7723
7832
  }
7724
7833
  }
7725
- const vercelDir = path25.join(root, ".vercel");
7726
- if (!fs25.existsSync(vercelDir)) {
7834
+ const vercelDir = path27.join(root, ".vercel");
7835
+ if (!fs27.existsSync(vercelDir)) {
7727
7836
  log.step("Linking project to Vercel...");
7728
7837
  try {
7729
7838
  execSync3("vercel link", { cwd: root, stdio: "inherit" });
@@ -7740,8 +7849,8 @@ async function deployCommand() {
7740
7849
  log.blank();
7741
7850
  const requiredVars = ["JWT_SECRET", "DATABASE_URL"];
7742
7851
  log.step(`Core: ${pc5.dim(requiredVars.join(", "))}`);
7743
- const configPath = path25.join(root, "src", "config", "app.config.ts");
7744
- const configContent = await fs25.readFile(configPath, "utf-8");
7852
+ const configPath = path27.join(root, "src", "config", "app.config.ts");
7853
+ const configContent = await fs27.readFile(configPath, "utf-8");
7745
7854
  if (configContent.includes("email: { provider: 'sendgrid'")) {
7746
7855
  log.step(`Email (SendGrid): ${pc5.dim("SENDGRID_API_KEY, SENDGRID_FROM_EMAIL")}`);
7747
7856
  } else if (configContent.includes("email: { provider: 'resend'")) {
@@ -7802,8 +7911,8 @@ async function deployCommand() {
7802
7911
 
7803
7912
  // src/commands/upgrade.ts
7804
7913
  init_logger();
7805
- import fs26 from "fs-extra";
7806
- import path26 from "path";
7914
+ import fs28 from "fs-extra";
7915
+ import path28 from "path";
7807
7916
  import { execSync as execSync4 } from "child_process";
7808
7917
  var MARS_PACKAGES = ["@mars-stack/core", "@mars-stack/ui"];
7809
7918
  var CHANGELOG_URL = "https://github.com/greaveselliott/mars/blob/main/CHANGELOG.md";
@@ -7826,7 +7935,7 @@ function readCurrentVersion(packageJson, packageName) {
7826
7935
  return version ? version.replace(/^[\^~]/, "") : null;
7827
7936
  }
7828
7937
  function detectPackageManager2(projectDir) {
7829
- if (fs26.pathExistsSync(path26.join(projectDir, "yarn.lock"))) {
7938
+ if (fs28.pathExistsSync(path28.join(projectDir, "yarn.lock"))) {
7830
7939
  return "yarn";
7831
7940
  }
7832
7941
  return "npm";
@@ -7890,7 +7999,7 @@ function updatePackageJsonVersions(packageJsonPath, packageJson, versions) {
7890
7999
  if (updated.length > 0) {
7891
8000
  packageJson.dependencies = deps;
7892
8001
  packageJson.devDependencies = devDeps;
7893
- fs26.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
8002
+ fs28.writeJsonSync(packageJsonPath, packageJson, { spaces: 2 });
7894
8003
  }
7895
8004
  return updated;
7896
8005
  }
@@ -7900,15 +8009,15 @@ function upgradeCommand(program2) {
7900
8009
  ).option("--dry-run", "Show what would be updated without making changes").action(async (options) => {
7901
8010
  log.title("MARS Upgrade");
7902
8011
  const projectDir = process.cwd();
7903
- const packageJsonPath = path26.join(projectDir, "package.json");
7904
- if (!fs26.pathExistsSync(packageJsonPath)) {
8012
+ const packageJsonPath = path28.join(projectDir, "package.json");
8013
+ if (!fs28.pathExistsSync(packageJsonPath)) {
7905
8014
  log.error(
7906
8015
  "No package.json found. Are you in a Mars project directory?"
7907
8016
  );
7908
8017
  process.exitCode = 1;
7909
8018
  return;
7910
8019
  }
7911
- const packageJson = fs26.readJsonSync(packageJsonPath);
8020
+ const packageJson = fs28.readJsonSync(packageJsonPath);
7912
8021
  const hasMarsPackage = MARS_PACKAGES.some(
7913
8022
  (name) => readCurrentVersion(packageJson, name) !== null
7914
8023
  );
@@ -8004,7 +8113,7 @@ init_sentry();
8004
8113
  init_feature_flags();
8005
8114
  init_logger();
8006
8115
  var program = new Command();
8007
- program.name("mars").description("MARS CLI: scaffold, configure, and maintain SaaS apps").version("0.1.0").option("-v, --verbose", "Enable verbose output for debugging");
8116
+ program.name("mars").description("MARS CLI: scaffold, configure, and maintain SaaS apps").version(getCliVersion()).option("-v, --verbose", "Enable verbose output for debugging");
8008
8117
  function isVerbose() {
8009
8118
  return program.opts().verbose === true;
8010
8119
  }