@mars-stack/cli 0.2.0 → 1.0.2

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 (175) hide show
  1. package/dist/index.js +137 -12
  2. package/dist/index.js.map +1 -1
  3. package/package.json +4 -3
  4. package/template/.cursor/rules/composition-patterns.mdc +186 -0
  5. package/template/.cursor/rules/data-access.mdc +29 -0
  6. package/template/.cursor/rules/project-structure.mdc +34 -0
  7. package/template/.cursor/rules/security.mdc +25 -0
  8. package/template/.cursor/rules/testing.mdc +24 -0
  9. package/template/.cursor/rules/ui-conventions.mdc +29 -0
  10. package/template/.cursor/skills/add-api-route/SKILL.md +122 -0
  11. package/template/.cursor/skills/add-audit-log/SKILL.md +375 -0
  12. package/template/.cursor/skills/add-blog/SKILL.md +447 -0
  13. package/template/.cursor/skills/add-command-palette/SKILL.md +438 -0
  14. package/template/.cursor/skills/add-component/SKILL.md +158 -0
  15. package/template/.cursor/skills/add-crud-routes/SKILL.md +221 -0
  16. package/template/.cursor/skills/add-e2e-test/SKILL.md +227 -0
  17. package/template/.cursor/skills/add-error-boundary/SKILL.md +472 -0
  18. package/template/.cursor/skills/add-feature/SKILL.md +174 -0
  19. package/template/.cursor/skills/add-middleware/SKILL.md +135 -0
  20. package/template/.cursor/skills/add-page/SKILL.md +151 -0
  21. package/template/.cursor/skills/add-prisma-model/SKILL.md +148 -0
  22. package/template/.cursor/skills/add-protected-resource/SKILL.md +192 -0
  23. package/template/.cursor/skills/add-role/SKILL.md +156 -0
  24. package/template/.cursor/skills/add-server-action/SKILL.md +167 -0
  25. package/template/.cursor/skills/add-webhook/SKILL.md +192 -0
  26. package/template/.cursor/skills/build-complete-feature/SKILL.md +227 -0
  27. package/template/.cursor/skills/build-dashboard/SKILL.md +211 -0
  28. package/template/.cursor/skills/build-data-table/SKILL.md +283 -0
  29. package/template/.cursor/skills/build-form/SKILL.md +231 -0
  30. package/template/.cursor/skills/build-landing-page/SKILL.md +248 -0
  31. package/template/.cursor/skills/configure-ai/SKILL.md +617 -0
  32. package/template/.cursor/skills/configure-analytics/SKILL.md +413 -0
  33. package/template/.cursor/skills/configure-dark-mode/SKILL.md +309 -0
  34. package/template/.cursor/skills/configure-email/SKILL.md +170 -0
  35. package/template/.cursor/skills/configure-email-verification/SKILL.md +333 -0
  36. package/template/.cursor/skills/configure-feature-flags/SKILL.md +361 -0
  37. package/template/.cursor/skills/configure-i18n/SKILL.md +518 -0
  38. package/template/.cursor/skills/configure-jobs/SKILL.md +500 -0
  39. package/template/.cursor/skills/configure-magic-links/SKILL.md +385 -0
  40. package/template/.cursor/skills/configure-multi-tenancy/SKILL.md +611 -0
  41. package/template/.cursor/skills/configure-notifications/SKILL.md +569 -0
  42. package/template/.cursor/skills/configure-oauth/SKILL.md +217 -0
  43. package/template/.cursor/skills/configure-onboarding/SKILL.md +483 -0
  44. package/template/.cursor/skills/configure-payments/SKILL.md +243 -0
  45. package/template/.cursor/skills/configure-realtime/SKILL.md +733 -0
  46. package/template/.cursor/skills/configure-search/SKILL.md +581 -0
  47. package/template/.cursor/skills/configure-storage/SKILL.md +273 -0
  48. package/template/.cursor/skills/configure-two-factor/SKILL.md +518 -0
  49. package/template/.cursor/skills/create-execution-plan/SKILL.md +204 -0
  50. package/template/.cursor/skills/create-seed/SKILL.md +191 -0
  51. package/template/.cursor/skills/deploy-to-vercel/SKILL.md +300 -0
  52. package/template/.cursor/skills/design-tokens/SKILL.md +138 -0
  53. package/template/.cursor/skills/mars-capture-conversation-context/SKILL.md +119 -0
  54. package/template/.cursor/skills/setup-billing/SKILL.md +322 -0
  55. package/template/.cursor/skills/setup-project/SKILL.md +104 -0
  56. package/template/.cursor/skills/setup-teams/SKILL.md +682 -0
  57. package/template/.cursor/skills/test-api-route/SKILL.md +219 -0
  58. package/template/.cursor/skills/update-architecture-docs/SKILL.md +99 -0
  59. package/template/AGENTS.md +104 -0
  60. package/template/ARCHITECTURE.md +102 -0
  61. package/template/docs/QUALITY_SCORE.md +20 -0
  62. package/template/docs/design-docs/conversation-as-system-record.md +70 -0
  63. package/template/docs/design-docs/core-beliefs.md +43 -0
  64. package/template/docs/design-docs/index.md +8 -0
  65. package/template/docs/exec-plans/active/.gitkeep +0 -0
  66. package/template/docs/exec-plans/completed/.gitkeep +0 -0
  67. package/template/docs/exec-plans/tech-debt.md +7 -0
  68. package/template/docs/generated/.gitkeep +0 -0
  69. package/template/docs/product-specs/index.md +7 -0
  70. package/template/docs/references/index.md +18 -0
  71. package/template/e2e/api.spec.ts +20 -0
  72. package/template/e2e/auth.spec.ts +24 -0
  73. package/template/e2e/public.spec.ts +25 -0
  74. package/template/eslint.config.mjs +24 -0
  75. package/template/next-env.d.ts +6 -0
  76. package/template/next.config.ts +45 -0
  77. package/template/package.json +80 -0
  78. package/template/playwright.config.ts +31 -0
  79. package/template/postcss.config.mjs +8 -0
  80. package/template/prisma/generated/prisma/browser.ts +49 -0
  81. package/template/prisma/generated/prisma/client.ts +73 -0
  82. package/template/prisma/generated/prisma/commonInputTypes.ts +406 -0
  83. package/template/prisma/generated/prisma/enums.ts +15 -0
  84. package/template/prisma/generated/prisma/internal/class.ts +254 -0
  85. package/template/prisma/generated/prisma/internal/prismaNamespace.ts +1240 -0
  86. package/template/prisma/generated/prisma/internal/prismaNamespaceBrowser.ts +190 -0
  87. package/template/prisma/generated/prisma/models/Account.ts +1543 -0
  88. package/template/prisma/generated/prisma/models/File.ts +1529 -0
  89. package/template/prisma/generated/prisma/models/Session.ts +1415 -0
  90. package/template/prisma/generated/prisma/models/Subscription.ts +1455 -0
  91. package/template/prisma/generated/prisma/models/User.ts +2235 -0
  92. package/template/prisma/generated/prisma/models/VerificationToken.ts +1099 -0
  93. package/template/prisma/generated/prisma/models.ts +17 -0
  94. package/template/prisma/schema/auth.prisma +69 -0
  95. package/template/prisma/schema/base.prisma +8 -0
  96. package/template/prisma/schema/file.prisma +15 -0
  97. package/template/prisma/schema/subscription.prisma +17 -0
  98. package/template/prisma.config.ts +13 -0
  99. package/template/scripts/check-architecture.ts +221 -0
  100. package/template/scripts/check-doc-freshness.ts +242 -0
  101. package/template/scripts/ensure-db.mjs +291 -0
  102. package/template/scripts/generate-docs.ts +143 -0
  103. package/template/scripts/generate-env-example.ts +89 -0
  104. package/template/scripts/seed.ts +56 -0
  105. package/template/scripts/update-quality-score.ts +263 -0
  106. package/template/src/__tests__/architecture.test.ts +114 -0
  107. package/template/src/app/(auth)/forgotten-password/page.tsx +92 -0
  108. package/template/src/app/(auth)/layout.tsx +11 -0
  109. package/template/src/app/(auth)/register/page.tsx +162 -0
  110. package/template/src/app/(auth)/reset-password/page.tsx +109 -0
  111. package/template/src/app/(auth)/sign-in/page.tsx +122 -0
  112. package/template/src/app/(auth)/verify/[token]/page.tsx +87 -0
  113. package/template/src/app/(auth)/verify/page.tsx +56 -0
  114. package/template/src/app/(protected)/admin/page.tsx +108 -0
  115. package/template/src/app/(protected)/dashboard/loading.tsx +20 -0
  116. package/template/src/app/(protected)/dashboard/page.tsx +22 -0
  117. package/template/src/app/(protected)/layout.tsx +262 -0
  118. package/template/src/app/(protected)/settings/page.tsx +370 -0
  119. package/template/src/app/api/auth/forgot/route.ts +63 -0
  120. package/template/src/app/api/auth/login/route.ts +121 -0
  121. package/template/src/app/api/auth/logout/route.ts +19 -0
  122. package/template/src/app/api/auth/me/route.ts +30 -0
  123. package/template/src/app/api/auth/reset/route.ts +45 -0
  124. package/template/src/app/api/auth/signup/route.ts +85 -0
  125. package/template/src/app/api/auth/verify/route.ts +46 -0
  126. package/template/src/app/api/csrf/route.ts +12 -0
  127. package/template/src/app/api/health/route.ts +10 -0
  128. package/template/src/app/api/protected/admin/users/route.ts +24 -0
  129. package/template/src/app/api/protected/billing/checkout/route.ts +83 -0
  130. package/template/src/app/api/protected/billing/portal/route.ts +39 -0
  131. package/template/src/app/api/protected/files/[fileId]/route.ts +86 -0
  132. package/template/src/app/api/protected/files/upload/route.ts +64 -0
  133. package/template/src/app/api/protected/user/password/route.ts +63 -0
  134. package/template/src/app/api/protected/user/profile/route.ts +35 -0
  135. package/template/src/app/api/protected/user/sessions/[sessionId]/route.ts +33 -0
  136. package/template/src/app/api/protected/user/sessions/route.ts +22 -0
  137. package/template/src/app/api/readiness/route.ts +15 -0
  138. package/template/src/app/api/webhooks/stripe/route.ts +166 -0
  139. package/template/src/app/error.tsx +33 -0
  140. package/template/src/app/layout.tsx +29 -0
  141. package/template/src/app/not-found.tsx +20 -0
  142. package/template/src/app/page.tsx +136 -0
  143. package/template/src/app/privacy/page.tsx +178 -0
  144. package/template/src/app/providers.tsx +8 -0
  145. package/template/src/app/terms/page.tsx +139 -0
  146. package/template/src/config/app.config.ts +70 -0
  147. package/template/src/config/routes.ts +17 -0
  148. package/template/src/features/admin/index.ts +11 -0
  149. package/template/src/features/admin/permissions.ts +64 -0
  150. package/template/src/features/auth/context/AuthContext.tsx +96 -0
  151. package/template/src/features/auth/context/index.ts +2 -0
  152. package/template/src/features/auth/index.ts +3 -0
  153. package/template/src/features/auth/server/consent.ts +66 -0
  154. package/template/src/features/auth/server/session-revocation.ts +20 -0
  155. package/template/src/features/auth/server/sessions.ts +66 -0
  156. package/template/src/features/auth/server/user.ts +166 -0
  157. package/template/src/features/auth/types.ts +19 -0
  158. package/template/src/features/auth/validators.ts +29 -0
  159. package/template/src/features/billing/server/index.ts +66 -0
  160. package/template/src/features/billing/types.ts +43 -0
  161. package/template/src/features/uploads/server/index.ts +49 -0
  162. package/template/src/features/uploads/types.ts +26 -0
  163. package/template/src/lib/core/email/templates/base-layout.ts +122 -0
  164. package/template/src/lib/core/email/templates/index.ts +4 -0
  165. package/template/src/lib/core/email/templates/password-reset-email.ts +42 -0
  166. package/template/src/lib/core/email/templates/verification-email.ts +41 -0
  167. package/template/src/lib/core/email/templates/welcome-email.ts +40 -0
  168. package/template/src/lib/mars.ts +56 -0
  169. package/template/src/lib/prisma.ts +19 -0
  170. package/template/src/proxy.ts +92 -0
  171. package/template/src/styles/brand.css +15 -0
  172. package/template/src/styles/globals.css +7 -0
  173. package/template/tsconfig.json +59 -0
  174. package/template/vitest.config.ts +41 -0
  175. package/template/vitest.setup.ts +24 -0
package/dist/index.js CHANGED
@@ -686,6 +686,21 @@ var init_blog = __esm({
686
686
  }
687
687
  });
688
688
 
689
+ // src/utils/client-file-patch.ts
690
+ function insertImportAfterDirectives(content, lineToInsert) {
691
+ if (CLIENT_DIRECTIVE.test(content)) {
692
+ return content.replace(/^(['"]use client['"];?\s*\n)/, `$1${lineToInsert}`);
693
+ }
694
+ return lineToInsert + content;
695
+ }
696
+ var CLIENT_DIRECTIVE;
697
+ var init_client_file_patch = __esm({
698
+ "src/utils/client-file-patch.ts"() {
699
+ "use strict";
700
+ CLIENT_DIRECTIVE = /^['"]use client['"];?\s*\n/;
701
+ }
702
+ });
703
+
689
704
  // src/generators/features/dark-mode.ts
690
705
  var dark_mode_exports = {};
691
706
  __export(dark_mode_exports, {
@@ -716,9 +731,13 @@ async function generateDarkMode(projectRoot) {
716
731
  await fs6.writeFile(fullPath, content);
717
732
  count++;
718
733
  }
734
+ await wireLayout(projectRoot, ctx);
735
+ await wireProviders(projectRoot, ctx);
736
+ await wireProtectedNav(projectRoot, ctx);
737
+ await wireTailwindDarkMode(projectRoot, ctx);
719
738
  await setConfigFlag2(projectRoot, ctx);
720
739
  await ctx.commit();
721
- log.success(`Generated dark mode feature with ${count} files`);
740
+ log.success(`Generated and wired dark mode feature (${count} files created)`);
722
741
  log.blank();
723
742
  log.step("src/lib/shared/components/providers/ThemeProvider.tsx \u2014 context, hook, persistence");
724
743
  log.step("src/lib/shared/components/patterns/ThemeToggle.tsx \u2014 three-option toggle");
@@ -726,14 +745,12 @@ async function generateDarkMode(projectRoot) {
726
745
  log.step("src/features/dark-mode/theme-script.ts \u2014 FOUC prevention script");
727
746
  log.step("src/features/dark-mode/index.ts \u2014 barrel exports");
728
747
  log.blank();
729
- log.warn("Complete setup in your root layout (src/app/layout.tsx):");
730
- log.step("1. Add suppressHydrationWarning to <html>");
731
- log.step("2. Add the theme script to <head>:");
732
- log.step(' import { getThemeScript } from "@/features/dark-mode/theme-script"');
733
- log.step(" <script dangerouslySetInnerHTML={{ __html: getThemeScript() }} />");
734
- log.step("3. Wrap the app with <ThemeProvider>");
735
- log.step("4. Add <ThemeToggle /> or <ThemeToggleSimple /> to your navbar");
736
- log.step('5. Ensure darkMode: "class" is set in your Tailwind config');
748
+ log.step("Wired automatically:");
749
+ log.step(" \u2713 FOUC-prevention script added to <head> in layout.tsx");
750
+ log.step(" \u2713 suppressHydrationWarning on <html>");
751
+ log.step(" \u2713 ThemeProvider wrapping app in providers.tsx");
752
+ log.step(" \u2713 ThemeToggleSimple added to navigation bar");
753
+ log.step(" \u2713 Tailwind dark mode class variant configured in globals.css");
737
754
  log.blank();
738
755
  } catch (error) {
739
756
  await ctx.rollback();
@@ -748,6 +765,84 @@ async function setConfigFlag2(projectRoot, ctx) {
748
765
  const updated = content.replace(/darkMode:\s*false/, "darkMode: true");
749
766
  await fs6.writeFile(configPath, updated);
750
767
  }
768
+ async function wireLayout(projectRoot, ctx) {
769
+ const layoutPath = path6.join(projectRoot, "src", "app", "layout.tsx");
770
+ if (!await fs6.pathExists(layoutPath)) return;
771
+ await ctx.trackModifiedFile(layoutPath);
772
+ let content = await fs6.readFile(layoutPath, "utf-8");
773
+ if (!content.includes("getThemeScript")) {
774
+ content = `import { getThemeScript } from '@/features/dark-mode';
775
+ ${content}`;
776
+ }
777
+ if (!content.includes("suppressHydrationWarning")) {
778
+ content = content.replace(/<html\s+lang="en"/, '<html lang="en" suppressHydrationWarning');
779
+ }
780
+ if (!content.includes("getThemeScript()")) {
781
+ content = content.replace(
782
+ /(<html[^>]*>)\s*\n(\s*<body)/,
783
+ "$1\n <head>\n <script dangerouslySetInnerHTML={{ __html: getThemeScript() }} />\n </head>\n$2"
784
+ );
785
+ }
786
+ await fs6.writeFile(layoutPath, content);
787
+ }
788
+ async function wireProviders(projectRoot, ctx) {
789
+ const providersPath = path6.join(projectRoot, "src", "app", "providers.tsx");
790
+ if (!await fs6.pathExists(providersPath)) return;
791
+ await ctx.trackModifiedFile(providersPath);
792
+ let content = await fs6.readFile(providersPath, "utf-8");
793
+ if (content.includes("ThemeProvider")) return;
794
+ content = insertImportAfterDirectives(
795
+ content,
796
+ `import { ThemeProvider } from '@/features/dark-mode';
797
+ `
798
+ );
799
+ content = content.replace(
800
+ /return\s+(?:\(\s*)?((?:<\w+[^>]*>)[\s\S]*?\{children\}[\s\S]*?(?:<\/\w+>))\s*(?:\)\s*)?;/,
801
+ (_match, jsx) => {
802
+ const wrapped = jsx.replace(
803
+ /(\{children\})/,
804
+ "<ThemeProvider>$1</ThemeProvider>"
805
+ );
806
+ return `return (
807
+ ${wrapped}
808
+ );`;
809
+ }
810
+ );
811
+ await fs6.writeFile(providersPath, content);
812
+ }
813
+ async function wireProtectedNav(projectRoot, ctx) {
814
+ const navPath = path6.join(projectRoot, "src", "app", "(protected)", "layout.tsx");
815
+ if (!await fs6.pathExists(navPath)) return;
816
+ await ctx.trackModifiedFile(navPath);
817
+ let content = await fs6.readFile(navPath, "utf-8");
818
+ if (content.includes("ThemeToggleSimple")) return;
819
+ content = insertImportAfterDirectives(
820
+ content,
821
+ `import { ThemeToggleSimple } from '@/features/dark-mode';
822
+ `
823
+ );
824
+ content = content.replace(
825
+ /(<div className="hidden md:block">)\s*\n(\s*)(<UserMenu \/>)/,
826
+ "$1\n$2<ThemeToggleSimple />\n$2$3"
827
+ );
828
+ content = content.replace(
829
+ /(<div className="border-t border-border-default px-4 py-3">\s*\n\s*<UserMenu \/>)/,
830
+ '$1\n <div className="mt-2">\n <ThemeToggleSimple />\n </div>'
831
+ );
832
+ await fs6.writeFile(navPath, content);
833
+ }
834
+ async function wireTailwindDarkMode(projectRoot, ctx) {
835
+ const globalsPath = path6.join(projectRoot, "src", "styles", "globals.css");
836
+ if (!await fs6.pathExists(globalsPath)) return;
837
+ await ctx.trackModifiedFile(globalsPath);
838
+ let content = await fs6.readFile(globalsPath, "utf-8");
839
+ if (content.includes("@custom-variant dark")) return;
840
+ content = content.replace(
841
+ /@import 'tailwindcss';/,
842
+ "@import 'tailwindcss';\n@custom-variant dark (&:where(.dark, .dark *));"
843
+ );
844
+ await fs6.writeFile(globalsPath, content);
845
+ }
751
846
  function themeProvider() {
752
847
  return `${STAMP2}
753
848
  'use client';
@@ -1017,9 +1112,10 @@ var GENERATOR_VERSION2, STAMP2;
1017
1112
  var init_dark_mode = __esm({
1018
1113
  "src/generators/features/dark-mode.ts"() {
1019
1114
  "use strict";
1115
+ init_client_file_patch();
1020
1116
  init_logger();
1021
1117
  init_rollback();
1022
- GENERATOR_VERSION2 = "0.1.0";
1118
+ GENERATOR_VERSION2 = "0.2.0";
1023
1119
  STAMP2 = `// @mars-generated dark-mode@${GENERATOR_VERSION2}`;
1024
1120
  }
1025
1121
  });
@@ -5901,6 +5997,7 @@ async function promptTheme() {
5901
5997
  init_logger();
5902
5998
  import fs2 from "fs-extra";
5903
5999
  import path2 from "path";
6000
+ import { generateBrandCss } from "@mars-stack/ui/utils";
5904
6001
 
5905
6002
  // src/generators/app-config.ts
5906
6003
  function escapeQuotes(value) {
@@ -6374,10 +6471,24 @@ async function scaffoldProject(targetDir, config) {
6374
6471
  await fs2.writeFile(path2.join(targetDir, ".env"), generateEnvFile(config));
6375
6472
  await fs2.writeFile(path2.join(targetDir, ".env.example"), generateEnvExample(config));
6376
6473
  await fs2.writeFile(path2.join(targetDir, ".gitignore"), generateGitignore());
6377
- await patchGlobalsCssForScaffoldedProject(targetDir);
6474
+ await patchGlobalsCssForScaffoldedProject(targetDir, config);
6475
+ await generateBrandCssForProject(targetDir, config);
6378
6476
  return { fileCount };
6379
6477
  }
6380
- async function patchGlobalsCssForScaffoldedProject(targetDir) {
6478
+ async function generateBrandCssForProject(targetDir, config) {
6479
+ const brandPath = path2.join(targetDir, "src", "styles", "brand.css");
6480
+ if (!await fs2.pathExists(brandPath)) return;
6481
+ const css = generateBrandCss(config.theme.primaryColor);
6482
+ await fs2.writeFile(brandPath, css);
6483
+ }
6484
+ var VALID_DESIGN_DIRECTIONS = [
6485
+ "modern-saas",
6486
+ "minimal",
6487
+ "enterprise",
6488
+ "creative",
6489
+ "dashboard"
6490
+ ];
6491
+ async function patchGlobalsCssForScaffoldedProject(targetDir, config) {
6381
6492
  const globalsPath = path2.join(targetDir, "src", "styles", "globals.css");
6382
6493
  if (!await fs2.pathExists(globalsPath)) return;
6383
6494
  let content = await fs2.readFile(globalsPath, "utf-8");
@@ -6385,6 +6496,20 @@ async function patchGlobalsCssForScaffoldedProject(targetDir) {
6385
6496
  '@source "../../../node_modules/@mars-stack/ui/dist/**/*.js";',
6386
6497
  '@source "../../node_modules/@mars-stack/ui/dist/**/*.js";'
6387
6498
  );
6499
+ const direction = VALID_DESIGN_DIRECTIONS.includes(
6500
+ config.theme.designDirection
6501
+ ) ? config.theme.designDirection : "modern-saas";
6502
+ const currentMetaImport = /@import\s+['"]@mars-stack\/ui\/styles\/meta-[^'"]+['"]\s*;/;
6503
+ const newImport = `@import '@mars-stack/ui/styles/meta-${direction}.css';`;
6504
+ if (currentMetaImport.test(content)) {
6505
+ content = content.replace(currentMetaImport, newImport);
6506
+ } else {
6507
+ content = content.replace(
6508
+ "@import '@mars-stack/ui/styles/index.css';",
6509
+ `@import '@mars-stack/ui/styles/index.css';
6510
+ ${newImport}`
6511
+ );
6512
+ }
6388
6513
  await fs2.writeFile(globalsPath, content);
6389
6514
  }
6390
6515