better-ts-stack 2.1.1 → 2.2.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 (174) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +16 -8
  3. package/dist/args.js +7 -0
  4. package/dist/builder/buildPlanner.js +7 -1
  5. package/dist/builder/configGenerator.js +28 -3
  6. package/dist/builder/index.js +2 -1
  7. package/dist/builder/moduleSelector.js +6 -1
  8. package/dist/builder/readmeGenerator.js +8 -2
  9. package/dist/builder/templateContext.js +1 -0
  10. package/dist/output/nextSteps.js +2 -4
  11. package/dist/prompts/stack.js +6 -1
  12. package/dist/types/index.js +2 -0
  13. package/dist/ui/messages.js +2 -0
  14. package/package.json +71 -71
  15. package/templates/backend/express/.prettierignore +52 -52
  16. package/templates/backend/express/.prettierrc +16 -16
  17. package/templates/backend/express/gitignore +3 -1
  18. package/templates/backend/express/src/env.ts.hbs +13 -2
  19. package/templates/backend/express/src/index.ts.hbs +31 -2
  20. package/templates/backend/express/tsconfig.eslint.json +9 -9
  21. package/templates/backend/nestjs/.prettierignore +52 -52
  22. package/templates/backend/nestjs/gitignore +3 -1
  23. package/templates/backend/nestjs/src/app.module.ts.hbs +4 -1
  24. package/templates/backend/nestjs/src/env.ts.hbs +13 -2
  25. package/templates/backend/nestjs/src/health/health.controller.ts.hbs +7 -0
  26. package/templates/backend/nestjs/src/{main.ts → main.ts.hbs} +11 -0
  27. package/templates/backend/nestjs/tsconfig.eslint.json +9 -9
  28. package/templates/frontend/nextjs/app/globals.css +129 -89
  29. package/templates/frontend/nextjs/app/layout.tsx +36 -34
  30. package/templates/frontend/nextjs/app/page.tsx.hbs +63 -43
  31. package/templates/frontend/nextjs/components/ui/alert.tsx +75 -0
  32. package/templates/frontend/nextjs/components/ui/badge.tsx +51 -0
  33. package/templates/frontend/nextjs/components/ui/button.tsx +57 -51
  34. package/templates/frontend/nextjs/components/ui/card.tsx +102 -60
  35. package/templates/frontend/nextjs/components/ui/field.tsx +238 -67
  36. package/templates/frontend/nextjs/components/ui/input.tsx +19 -18
  37. package/templates/frontend/nextjs/components/ui/label.tsx +19 -0
  38. package/templates/frontend/nextjs/components/ui/separator.tsx +24 -0
  39. package/templates/frontend/nextjs/components/ui/skeleton.tsx +13 -0
  40. package/templates/frontend/nextjs/components/ui/spinner.tsx +10 -0
  41. package/templates/frontend/nextjs/components.json +11 -5
  42. package/templates/frontend/nextjs/config.json +36 -35
  43. package/templates/frontend/nextjs/eslint.config.mjs +11 -11
  44. package/templates/frontend/nextjs/lib/utils.ts +1 -6
  45. package/templates/frontend/nextjs/next.config.ts.hbs +12 -12
  46. package/templates/frontend/nextjs/postcss.config.mjs +7 -7
  47. package/templates/frontend/nextjs/public/file.svg +1 -1
  48. package/templates/frontend/nextjs/public/globe.svg +1 -1
  49. package/templates/frontend/nextjs/public/window.svg +1 -1
  50. package/templates/frontend/tanstack/components.json +25 -18
  51. package/templates/frontend/tanstack/config.json +5 -2
  52. package/templates/frontend/tanstack/eslint.config.mjs +23 -23
  53. package/templates/frontend/tanstack/gitignore +39 -39
  54. package/templates/frontend/tanstack/public/favicon.svg +7 -7
  55. package/templates/frontend/tanstack/src/components/ui/alert.tsx +75 -0
  56. package/templates/frontend/tanstack/src/components/ui/badge.tsx +51 -0
  57. package/templates/frontend/tanstack/src/components/ui/button.tsx +57 -51
  58. package/templates/frontend/tanstack/src/components/ui/card.tsx +102 -60
  59. package/templates/frontend/tanstack/src/components/ui/field.tsx +236 -67
  60. package/templates/frontend/tanstack/src/components/ui/input.tsx +19 -18
  61. package/templates/frontend/tanstack/src/components/ui/label.tsx +17 -0
  62. package/templates/frontend/tanstack/src/components/ui/separator.tsx +24 -0
  63. package/templates/frontend/tanstack/src/components/ui/skeleton.tsx +13 -0
  64. package/templates/frontend/tanstack/src/components/ui/spinner.tsx +10 -0
  65. package/templates/frontend/tanstack/src/lib/utils.ts +1 -6
  66. package/templates/frontend/tanstack/src/router.tsx +31 -31
  67. package/templates/frontend/tanstack/src/routes/__root.tsx +69 -69
  68. package/templates/frontend/tanstack/src/routes/index.tsx.hbs +64 -45
  69. package/templates/frontend/tanstack/src/styles/app.css +100 -55
  70. package/templates/frontend/tanstack/tsconfig.json +22 -22
  71. package/templates/modules/agents/AGENTS.md.hbs +245 -0
  72. package/templates/modules/agents/CLAUDE.md +1 -0
  73. package/templates/modules/agents/config.json +10 -0
  74. package/templates/modules/auth/express/config.json +12 -11
  75. package/templates/modules/auth/express/src/lib/auth.ts.hbs +143 -0
  76. package/templates/modules/auth/express/src/lib/email.ts +37 -0
  77. package/templates/modules/auth/express/src/middleware/requireAuth.ts +29 -0
  78. package/templates/modules/auth/express/src/routes/me.ts +13 -0
  79. package/templates/modules/auth/nestjs/config.json +13 -11
  80. package/templates/modules/auth/nestjs/src/auth/auth.module.ts.hbs +64 -14
  81. package/templates/modules/auth/nestjs/src/auth/auth.ts.hbs +152 -0
  82. package/templates/modules/auth/nestjs/src/auth/email.ts +37 -0
  83. package/templates/modules/auth/nestjs/src/me/me.controller.ts +15 -0
  84. package/templates/modules/auth/nextjs/app/api/auth/[...all]/route.ts +4 -4
  85. package/templates/modules/auth/nextjs/app/dashboard/page.tsx +101 -45
  86. package/templates/modules/auth/nextjs/app/forgot-password/page.tsx +11 -0
  87. package/templates/modules/auth/nextjs/app/reset-password/page.tsx +17 -0
  88. package/templates/modules/auth/nextjs/app/sign-in/page.tsx +23 -17
  89. package/templates/modules/auth/nextjs/app/sign-up/page.tsx +25 -19
  90. package/templates/modules/auth/nextjs/components/auth/change-password-form.tsx +125 -0
  91. package/templates/modules/auth/nextjs/components/auth/email-verification-card.tsx +91 -0
  92. package/templates/modules/auth/nextjs/components/auth/forgot-password-form.tsx +133 -0
  93. package/templates/modules/auth/nextjs/components/auth/reset-password-form.tsx +155 -0
  94. package/templates/modules/auth/nextjs/components/auth/sign-in-form.tsx +46 -32
  95. package/templates/modules/auth/nextjs/components/auth/sign-out-button.tsx +6 -5
  96. package/templates/modules/auth/nextjs/components/auth/sign-up-form.tsx +36 -30
  97. package/templates/modules/auth/nextjs/components/auth/social-buttons.tsx +73 -0
  98. package/templates/modules/auth/nextjs/config.json +24 -18
  99. package/templates/modules/auth/nextjs/lib/auth-client.ts +3 -3
  100. package/templates/modules/auth/nextjs/lib/auth-schema.ts +27 -2
  101. package/templates/modules/auth/nextjs/lib/auth.ts.hbs +65 -0
  102. package/templates/modules/auth/nextjs/lib/email.ts +43 -0
  103. package/templates/modules/auth/nextjs/lib/session.ts +12 -0
  104. package/templates/modules/auth/tanstack/config.json +25 -19
  105. package/templates/modules/auth/tanstack/src/components/auth/change-password-form.tsx +123 -0
  106. package/templates/modules/auth/tanstack/src/components/auth/email-verification-card.tsx +89 -0
  107. package/templates/modules/auth/tanstack/src/components/auth/forgot-password-form.tsx +131 -0
  108. package/templates/modules/auth/tanstack/src/components/auth/reset-password-form.tsx +149 -0
  109. package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +44 -35
  110. package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +7 -6
  111. package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +37 -34
  112. package/templates/modules/auth/tanstack/src/components/auth/social-buttons.tsx +71 -0
  113. package/templates/modules/auth/tanstack/src/lib/auth-client.ts +2 -2
  114. package/templates/modules/auth/tanstack/src/lib/auth-functions.ts +14 -8
  115. package/templates/modules/auth/tanstack/src/lib/auth-schema.ts +27 -2
  116. package/templates/modules/auth/tanstack/src/lib/auth.ts.hbs +98 -38
  117. package/templates/modules/auth/tanstack/src/lib/email.ts +43 -0
  118. package/templates/modules/auth/tanstack/src/routeTree.gen.ts +61 -3
  119. package/templates/modules/auth/tanstack/src/routes/api/auth/$.ts +15 -15
  120. package/templates/modules/auth/tanstack/src/routes/dashboard.tsx +119 -111
  121. package/templates/modules/auth/tanstack/src/routes/forgot-password.tsx +17 -0
  122. package/templates/modules/auth/tanstack/src/routes/reset-password.tsx +25 -0
  123. package/templates/modules/auth/tanstack/src/routes/sign-in.tsx +44 -37
  124. package/templates/modules/auth/tanstack/src/routes/sign-up.tsx +44 -37
  125. package/templates/modules/docker/.dockerignore +2 -0
  126. package/templates/modules/docker/config.json +15 -15
  127. package/templates/modules/drizzle/express/config.json +26 -26
  128. package/templates/modules/drizzle/express/src/lib/schema.ts.hbs +90 -0
  129. package/templates/modules/drizzle/nestjs/config.json +26 -26
  130. package/templates/modules/drizzle/nestjs/src/database/schema.ts.hbs +90 -0
  131. package/templates/modules/drizzle/nextjs/config.json +26 -26
  132. package/templates/modules/drizzle/nextjs/drizzle.config.ts +13 -13
  133. package/templates/modules/drizzle/tanstack/config.json +26 -26
  134. package/templates/modules/drizzle/tanstack/drizzle.config.ts +12 -12
  135. package/templates/modules/drizzle/tanstack/src/lib/db.ts +13 -13
  136. package/templates/modules/mongoose/express/config.json +15 -15
  137. package/templates/modules/mongoose/express/src/lib/db.ts +43 -43
  138. package/templates/modules/mongoose/express/src/models/User.ts +48 -48
  139. package/templates/modules/mongoose/nestjs/config.json +16 -15
  140. package/templates/modules/mongoose/nextjs/config.json +15 -15
  141. package/templates/modules/mongoose/nextjs/models/User.ts +48 -48
  142. package/templates/modules/mongoose/tanstack/config.json +16 -16
  143. package/templates/modules/mongoose/tanstack/src/models/User.ts +48 -48
  144. package/templates/modules/prisma/express/config.json +27 -27
  145. package/templates/modules/prisma/express/prisma/schema.prisma.hbs +92 -0
  146. package/templates/modules/prisma/nestjs/config.json +27 -27
  147. package/templates/modules/prisma/nestjs/prisma/schema.prisma.hbs +91 -0
  148. package/templates/modules/prisma/nextjs/config.json +28 -28
  149. package/templates/modules/prisma/nextjs/lib/prisma.ts +18 -18
  150. package/templates/modules/prisma/nextjs/prisma.config.ts +14 -14
  151. package/templates/modules/prisma/tanstack/config.json +28 -28
  152. package/templates/modules/prisma/tanstack/prisma/schema.prisma.hbs +91 -91
  153. package/templates/modules/prisma/tanstack/prisma.config.ts +14 -14
  154. package/templates/modules/prisma/tanstack/src/lib/prisma.ts +17 -17
  155. package/templates/modules/rate-limit/express/src/middleware/rateLimit.ts +0 -8
  156. package/templates/modules/vscode/.vscode/extensions.json.hbs +29 -0
  157. package/templates/modules/vscode/.vscode/settings.json.hbs +22 -0
  158. package/templates/modules/vscode/config.json +10 -0
  159. package/templates/modules/auth/express/src/controllers/authController.ts.hbs +0 -116
  160. package/templates/modules/auth/express/src/lib/jwt.ts.hbs +0 -27
  161. package/templates/modules/auth/express/src/middleware/requireAuth.ts.hbs +0 -25
  162. package/templates/modules/auth/express/src/routes/auth.ts.hbs +0 -27
  163. package/templates/modules/auth/express/src/services/userStore.ts.hbs +0 -117
  164. package/templates/modules/auth/nestjs/src/auth/auth.controller.ts.hbs +0 -61
  165. package/templates/modules/auth/nestjs/src/auth/auth.guard.ts +0 -36
  166. package/templates/modules/auth/nestjs/src/auth/auth.schemas.ts +0 -35
  167. package/templates/modules/auth/nestjs/src/auth/auth.service.ts +0 -70
  168. package/templates/modules/auth/nestjs/src/auth/jwt.ts +0 -27
  169. package/templates/modules/auth/nestjs/src/auth/users.service.ts.hbs +0 -144
  170. package/templates/modules/auth/nestjs/src/auth/zod-validation.pipe.ts +0 -16
  171. package/templates/modules/drizzle/express/src/lib/schema.ts +0 -12
  172. package/templates/modules/drizzle/nestjs/src/database/schema.ts +0 -12
  173. package/templates/modules/prisma/express/prisma/schema.prisma +0 -24
  174. package/templates/modules/prisma/nestjs/prisma/schema.prisma +0 -23
@@ -1,9 +1,16 @@
1
1
  import { createFileRoute{{#if useAuth}}, Link{{/if}} } from "@tanstack/react-router";
2
2
  import { ArrowRight } from "lucide-react";
3
3
 
4
- import { buttonVariants } from "~/components/ui/button";
5
- import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
6
- import { cn } from "~/lib/utils";
4
+ import { Alert, AlertDescription } from "~/components/ui/alert";
5
+ import { Badge } from "~/components/ui/badge";
6
+ import { Button } from "~/components/ui/button";
7
+ import {
8
+ Card,
9
+ CardContent,
10
+ CardFooter,
11
+ CardHeader,
12
+ CardTitle,
13
+ } from "~/components/ui/card";
7
14
 
8
15
  export const Route = createFileRoute("/")({
9
16
  component: Home,
@@ -13,7 +20,7 @@ const featureList = [
13
20
  "TanStack Start SSR",
14
21
  "TanStack Router and Query",
15
22
  "React 19 and TypeScript",
16
- "Tailwind CSS v4",
23
+ "Tailwind CSS v4 and shadcn/ui on Base UI",
17
24
  {{#if useAuth}}"Better Auth-ready session flow",{{/if}}
18
25
  {{#if (eq database "prisma")}}"Prisma database tooling",{{/if}}
19
26
  {{#if (eq database "drizzle")}}"Drizzle database tooling",{{/if}}
@@ -22,66 +29,74 @@ const featureList = [
22
29
 
23
30
  function Home() {
24
31
  return (
25
- <main className="min-h-screen bg-background text-foreground">
26
- <div className="mx-auto flex min-h-screen max-w-6xl flex-col justify-center gap-12 px-6 py-16 sm:px-10 lg:px-12">
32
+ <main className="min-h-svh">
33
+ <div className="mx-auto flex min-h-svh max-w-6xl flex-col justify-center gap-12 px-6 py-16 sm:px-10 lg:px-12">
27
34
  <section className="grid gap-10 lg:grid-cols-[1.2fr_0.8fr] lg:items-center">
28
- <div className="space-y-6">
29
- <span className="inline-flex w-fit items-center rounded-full border border-border bg-card px-3 py-1 text-xs font-medium uppercase tracking-[0.24em] text-muted-foreground shadow-sm">
30
- Created with better-ts-stack
31
- </span>
32
- <div className="space-y-4">
35
+ <div className="flex flex-col gap-6">
36
+ <Badge variant="outline">Created with better-ts-stack</Badge>
37
+ <div className="flex flex-col gap-4">
33
38
  <h1 className="max-w-3xl text-4xl font-semibold tracking-tight sm:text-5xl">
34
39
  Ship your stack faster with a polished TypeScript foundation.
35
40
  </h1>
36
41
  <p className="max-w-2xl text-base leading-7 text-muted-foreground sm:text-lg">
37
42
  Your project already includes the core app shell, database wiring,
38
- and a shadcn-compatible UI foundation so you can keep building
39
- instead of setting up boilerplate.
43
+ and shadcn/ui components so you can keep building instead of
44
+ setting up boilerplate.
40
45
  </p>
41
46
  </div>
42
47
  <div className="flex flex-col gap-3 sm:flex-row">
43
48
  {{#if useAuth}}
44
- <Link
45
- to="/sign-up"
46
- className={cn(buttonVariants({ size: "lg" }), "gap-2")}
47
- >
49
+ <Button size="lg" nativeButton={false} render={<Link to="/sign-up" />}>
48
50
  Create an account
49
- <ArrowRight className="size-4" />
50
- </Link>
51
- <Link
52
- to="/sign-in"
53
- className={buttonVariants({ variant: "outline", size: "lg" })}
51
+ <ArrowRight data-icon="inline-end" />
52
+ </Button>
53
+ <Button
54
+ size="lg"
55
+ variant="outline"
56
+ nativeButton={false}
57
+ render={<Link to="/sign-in" />}
54
58
  >
55
59
  Login
56
- </Link>
60
+ </Button>
57
61
  {{else}}
58
- <a
59
- href="https://better-ts-stack.abdullahtech.me/docs"
60
- target="_blank"
61
- rel="noreferrer"
62
- className={cn(buttonVariants({ size: "lg" }), "gap-2")}
62
+ <Button
63
+ size="lg"
64
+ nativeButton={false}
65
+ render={
66
+ <a
67
+ href="https://better-ts-stack.abdullahtech.me/docs"
68
+ target="_blank"
69
+ rel="noreferrer"
70
+ />
71
+ }
63
72
  >
64
73
  Explore the docs
65
- <ArrowRight className="size-4" />
66
- </a>
67
- <a
68
- href="https://tanstack.com/start/latest"
69
- target="_blank"
70
- rel="noreferrer"
71
- className={buttonVariants({ variant: "outline", size: "lg" })}
74
+ <ArrowRight data-icon="inline-end" />
75
+ </Button>
76
+ <Button
77
+ size="lg"
78
+ variant="outline"
79
+ nativeButton={false}
80
+ render={
81
+ <a
82
+ href="https://tanstack.com/start/latest"
83
+ target="_blank"
84
+ rel="noreferrer"
85
+ />
86
+ }
72
87
  >
73
88
  TanStack guide
74
- </a>
89
+ </Button>
75
90
  {{/if}}
76
91
  </div>
77
92
  </div>
78
93
 
79
- <Card className="border-border bg-card shadow-sm">
94
+ <Card>
80
95
  <CardHeader>
81
96
  <CardTitle>Included in this starter</CardTitle>
82
97
  </CardHeader>
83
- <CardContent className="space-y-4">
84
- <ul className="space-y-3 text-sm text-muted-foreground">
98
+ <CardContent>
99
+ <ul className="flex flex-col gap-3 text-muted-foreground">
85
100
  {featureList.map((feature) => (
86
101
  <li key={feature} className="flex items-center gap-3">
87
102
  <span className="size-2 rounded-full bg-primary" />
@@ -89,15 +104,19 @@ function Home() {
89
104
  </li>
90
105
  ))}
91
106
  </ul>
92
- <div className="rounded-2xl border border-border bg-muted px-4 py-3 text-sm text-muted-foreground">
93
- Start editing{" "}
94
- <code className="font-mono">src/routes/index.tsx</code> and
95
- shape the project around your product instead of setup chores.
96
- </div>
97
107
  </CardContent>
108
+ <CardFooter>
109
+ <Alert>
110
+ <AlertDescription>
111
+ Start editing{" "}
112
+ <code className="font-mono">src/routes/index.tsx</code> and
113
+ shape the project around your product instead of setup chores.
114
+ </AlertDescription>
115
+ </Alert>
116
+ </CardFooter>
98
117
  </Card>
99
118
  </section>
100
119
  </div>
101
120
  </main>
102
121
  );
103
- }
122
+ }
@@ -1,49 +1,95 @@
1
1
  @import "tailwindcss" source("../");
2
+ @import "tw-animate-css";
3
+ @import "shadcn/tailwind.css";
4
+ @import "@fontsource-variable/inter";
5
+
6
+ @custom-variant dark (&:is(.dark *));
2
7
 
3
8
  @theme inline {
4
- --color-background: var(--background);
5
- --color-foreground: var(--foreground);
6
- --color-card: var(--card);
7
- --color-card-foreground: var(--card-foreground);
8
- --color-primary: var(--primary);
9
- --color-primary-foreground: var(--primary-foreground);
10
- --color-secondary: var(--secondary);
11
- --color-secondary-foreground: var(--secondary-foreground);
12
- --color-muted: var(--muted);
13
- --color-muted-foreground: var(--muted-foreground);
14
- --color-accent: var(--accent);
15
- --color-accent-foreground: var(--accent-foreground);
16
- --color-destructive: var(--destructive);
17
- --color-border: var(--border);
18
- --color-input: var(--input);
19
- --color-ring: var(--ring);
9
+ --font-heading: var(--font-sans);
10
+ --font-sans: 'Inter Variable', sans-serif;
11
+ --color-sidebar-ring: var(--sidebar-ring);
12
+ --color-sidebar-border: var(--sidebar-border);
13
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
14
+ --color-sidebar-accent: var(--sidebar-accent);
15
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
16
+ --color-sidebar-primary: var(--sidebar-primary);
17
+ --color-sidebar-foreground: var(--sidebar-foreground);
18
+ --color-sidebar: var(--sidebar);
19
+ --color-chart-5: var(--chart-5);
20
+ --color-chart-4: var(--chart-4);
21
+ --color-chart-3: var(--chart-3);
22
+ --color-chart-2: var(--chart-2);
23
+ --color-chart-1: var(--chart-1);
24
+ --color-ring: var(--ring);
25
+ --color-input: var(--input);
26
+ --color-border: var(--border);
27
+ --color-destructive: var(--destructive);
28
+ --color-accent-foreground: var(--accent-foreground);
29
+ --color-accent: var(--accent);
30
+ --color-muted-foreground: var(--muted-foreground);
31
+ --color-muted: var(--muted);
32
+ --color-secondary-foreground: var(--secondary-foreground);
33
+ --color-secondary: var(--secondary);
34
+ --color-primary-foreground: var(--primary-foreground);
35
+ --color-primary: var(--primary);
36
+ --color-popover-foreground: var(--popover-foreground);
37
+ --color-popover: var(--popover);
38
+ --color-card-foreground: var(--card-foreground);
39
+ --color-card: var(--card);
40
+ --color-foreground: var(--foreground);
41
+ --color-background: var(--background);
42
+ --radius-sm: calc(var(--radius) * 0.6);
43
+ --radius-md: calc(var(--radius) * 0.8);
44
+ --radius-lg: var(--radius);
45
+ --radius-xl: calc(var(--radius) * 1.4);
46
+ --radius-2xl: calc(var(--radius) * 1.8);
47
+ --radius-3xl: calc(var(--radius) * 2.2);
48
+ --radius-4xl: calc(var(--radius) * 2.6);
20
49
  }
21
50
 
22
51
  :root {
23
- --background: oklch(0.985 0 0);
24
- --foreground: oklch(0.145 0 0);
25
- --card: oklch(1 0 0);
26
- --card-foreground: oklch(0.145 0 0);
27
- --primary: oklch(0.205 0 0);
28
- --primary-foreground: oklch(0.985 0 0);
29
- --secondary: oklch(0.97 0 0);
30
- --secondary-foreground: oklch(0.205 0 0);
31
- --muted: oklch(0.97 0 0);
32
- --muted-foreground: oklch(0.556 0 0);
33
- --accent: oklch(0.97 0 0);
34
- --accent-foreground: oklch(0.205 0 0);
35
- --destructive: oklch(0.577 0.245 27.325);
36
- --border: oklch(0.922 0 0);
37
- --input: oklch(0.922 0 0);
38
- --ring: oklch(0.708 0 0);
52
+ --background: oklch(1 0 0);
53
+ --foreground: oklch(0.145 0 0);
54
+ --card: oklch(1 0 0);
55
+ --card-foreground: oklch(0.145 0 0);
56
+ --popover: oklch(1 0 0);
57
+ --popover-foreground: oklch(0.145 0 0);
58
+ --primary: oklch(0.205 0 0);
59
+ --primary-foreground: oklch(0.985 0 0);
60
+ --secondary: oklch(0.97 0 0);
61
+ --secondary-foreground: oklch(0.205 0 0);
62
+ --muted: oklch(0.97 0 0);
63
+ --muted-foreground: oklch(0.556 0 0);
64
+ --accent: oklch(0.97 0 0);
65
+ --accent-foreground: oklch(0.205 0 0);
66
+ --destructive: oklch(0.577 0.245 27.325);
67
+ --border: oklch(0.922 0 0);
68
+ --input: oklch(0.922 0 0);
69
+ --ring: oklch(0.708 0 0);
70
+ --chart-1: oklch(0.87 0 0);
71
+ --chart-2: oklch(0.556 0 0);
72
+ --chart-3: oklch(0.439 0 0);
73
+ --chart-4: oklch(0.371 0 0);
74
+ --chart-5: oklch(0.269 0 0);
75
+ --radius: 0.625rem;
76
+ --sidebar: oklch(0.985 0 0);
77
+ --sidebar-foreground: oklch(0.145 0 0);
78
+ --sidebar-primary: oklch(0.205 0 0);
79
+ --sidebar-primary-foreground: oklch(0.985 0 0);
80
+ --sidebar-accent: oklch(0.97 0 0);
81
+ --sidebar-accent-foreground: oklch(0.205 0 0);
82
+ --sidebar-border: oklch(0.922 0 0);
83
+ --sidebar-ring: oklch(0.708 0 0);
39
84
  }
40
85
 
41
- @media (prefers-color-scheme: dark) {
42
- :root {
86
+ .dark {
43
87
  --background: oklch(0.145 0 0);
44
88
  --foreground: oklch(0.985 0 0);
45
89
  --card: oklch(0.205 0 0);
46
90
  --card-foreground: oklch(0.985 0 0);
91
+ --popover: oklch(0.205 0 0);
92
+ --popover-foreground: oklch(0.985 0 0);
47
93
  --primary: oklch(0.922 0 0);
48
94
  --primary-foreground: oklch(0.205 0 0);
49
95
  --secondary: oklch(0.269 0 0);
@@ -56,30 +102,29 @@
56
102
  --border: oklch(1 0 0 / 10%);
57
103
  --input: oklch(1 0 0 / 15%);
58
104
  --ring: oklch(0.556 0 0);
59
- }
60
- }
61
-
62
- @layer base {
63
- *,
64
- ::after,
65
- ::before,
66
- ::backdrop,
67
- ::file-selector-button {
68
- border-color: var(--color-gray-200, currentcolor);
69
- }
105
+ --chart-1: oklch(0.87 0 0);
106
+ --chart-2: oklch(0.556 0 0);
107
+ --chart-3: oklch(0.439 0 0);
108
+ --chart-4: oklch(0.371 0 0);
109
+ --chart-5: oklch(0.269 0 0);
110
+ --sidebar: oklch(0.205 0 0);
111
+ --sidebar-foreground: oklch(0.985 0 0);
112
+ --sidebar-primary: oklch(0.488 0.243 264.376);
113
+ --sidebar-primary-foreground: oklch(0.985 0 0);
114
+ --sidebar-accent: oklch(0.269 0 0);
115
+ --sidebar-accent-foreground: oklch(0.985 0 0);
116
+ --sidebar-border: oklch(1 0 0 / 10%);
117
+ --sidebar-ring: oklch(0.556 0 0);
70
118
  }
71
119
 
72
120
  @layer base {
73
- html {
74
- color-scheme: light dark;
75
- }
76
-
77
121
  * {
78
- @apply border-gray-200 dark:border-gray-800;
79
- }
80
-
81
- html,
122
+ @apply border-border outline-ring/50;
123
+ }
82
124
  body {
83
- @apply bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200;
84
- }
125
+ @apply bg-background text-foreground;
126
+ }
127
+ html {
128
+ @apply font-sans;
129
+ }
85
130
  }
@@ -1,22 +1,22 @@
1
- {
2
- "include": ["**/*.ts", "**/*.tsx"],
3
- "compilerOptions": {
4
- "strict": true,
5
- "esModuleInterop": true,
6
- "jsx": "react-jsx",
7
- "module": "ESNext",
8
- "moduleResolution": "Bundler",
9
- "lib": ["DOM", "DOM.Iterable", "ES2022"],
10
- "isolatedModules": true,
11
- "resolveJsonModule": true,
12
- "skipLibCheck": true,
13
- "target": "ES2022",
14
- "allowJs": true,
15
- "noEmit": true,
16
- "forceConsistentCasingInFileNames": true,
17
- "paths": {
18
- "~/*": ["./src/*"]
19
- }
20
- },
21
- "exclude": ["node_modules"]
22
- }
1
+ {
2
+ "include": ["**/*.ts", "**/*.tsx"],
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "esModuleInterop": true,
6
+ "jsx": "react-jsx",
7
+ "module": "ESNext",
8
+ "moduleResolution": "Bundler",
9
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
10
+ "isolatedModules": true,
11
+ "resolveJsonModule": true,
12
+ "skipLibCheck": true,
13
+ "target": "ES2022",
14
+ "allowJs": true,
15
+ "noEmit": true,
16
+ "forceConsistentCasingInFileNames": true,
17
+ "paths": {
18
+ "~/*": ["./src/*"]
19
+ }
20
+ },
21
+ "exclude": ["node_modules"]
22
+ }
@@ -0,0 +1,245 @@
1
+ # AGENTS.md
2
+
3
+ Instructions for AI coding agents working in {{projectName}}. Read this before making changes.
4
+
5
+ ## Stack
6
+
7
+ {{#if (eq framework 'express')}}
8
+ - Express 5 REST API in TypeScript, compiled to CommonJS with `tsc`
9
+ {{/if}}
10
+ {{#if (eq framework 'nestjs')}}
11
+ - NestJS 12 REST API in TypeScript, using ES modules
12
+ {{/if}}
13
+ {{#if (eq framework 'nextjs')}}
14
+ - Next.js 16 with the App Router, React 19, Tailwind CSS 4, and shadcn/ui on Base UI
15
+ {{/if}}
16
+ {{#if (eq framework 'tanstack')}}
17
+ - TanStack Start with Vite, Nitro, React 19, Tailwind CSS 4, and shadcn/ui on Base UI
18
+ {{/if}}
19
+ {{#if (eq database 'prisma')}}
20
+ - PostgreSQL through Prisma 7
21
+ {{/if}}
22
+ {{#if (eq database 'drizzle')}}
23
+ - PostgreSQL through Drizzle ORM
24
+ {{/if}}
25
+ {{#if (eq database 'mongoose')}}
26
+ - MongoDB through Mongoose
27
+ {{/if}}
28
+ {{#if useAuth}}
29
+ - Better Auth: email and password, email verification, password reset, and GitHub and Google sign-in when their keys are set. Emails go through Resend when `RESEND_API_KEY` is set and are printed to the log otherwise
30
+ {{#if (eq framework 'express')}}
31
+ - Auth routes under `/api/auth` (`POST /api/auth/sign-up/email`, `POST /api/auth/sign-in/email`, ...). Clients without cookies send `Authorization: Bearer <token>`, using the `set-auth-token` header from sign-in
32
+ {{/if}}
33
+ {{#if (eq framework 'nestjs')}}
34
+ - Auth routes under `/api/auth` (`POST /api/auth/sign-up/email`, `POST /api/auth/sign-in/email`, ...). Clients without cookies send `Authorization: Bearer <token>`, using the `set-auth-token` header from sign-in
35
+ {{/if}}
36
+ {{/if}}
37
+ {{#if useRateLimit}}
38
+ - Rate limiting on every route except `/health`
39
+ {{/if}}
40
+ {{#if useDocker}}
41
+ - Docker: `Dockerfile` and `docker-compose.yml`
42
+ {{/if}}
43
+ {{#if useCi}}
44
+ - GitHub Actions CI in `.github/workflows/ci.yml`
45
+ {{/if}}
46
+ - Package manager: {{packageManager}}. Do not use another one or add its lockfile
47
+ - Node.js 24 or later
48
+
49
+ ## Commands
50
+
51
+ - Install: `{{packageManager}} install`
52
+ - Dev server: `{{docker.run}} dev`
53
+ - Build: `{{docker.run}} build`
54
+ - Type-check: `{{docker.run}} type:check`
55
+ - Lint: `{{docker.run}} lint`
56
+ {{#if (eq database 'prisma')}}
57
+ - After changing `prisma/schema.prisma`: `{{docker.run}} prisma:migrate` creates and applies a migration and regenerates the client
58
+ {{/if}}
59
+ {{#if (eq database 'drizzle')}}
60
+ - After changing the schema: `{{docker.run}} db:generate` writes a migration, then `{{docker.run}} db:migrate` applies it
61
+ {{/if}}
62
+
63
+ ## Before you finish
64
+
65
+ Run type-check and lint, and fix every error they report. Run the build too when you changed config, dependencies, or anything the build reads. Do not report a task as done while any of them fails.
66
+
67
+ ## Layout
68
+
69
+ {{#if (eq framework 'express')}}
70
+ - `src/index.ts`: creates the app, adds middleware, and mounts the routers
71
+ - `src/env.ts`: checks the environment variables with Zod at startup, so a missing one stops the server with a clear message
72
+ - `src/routes/`: one router per resource, mounted in `src/index.ts`
73
+ {{#if useAuth}}
74
+ - `src/lib/auth.ts`: the Better Auth server, mounted on `/api/auth` in `src/index.ts`
75
+ - `src/lib/email.ts`: sends the verification and password reset emails
76
+ - `src/middleware/requireAuth.ts`: add it to a route to require a session; `getSession(res)` returns it. `src/routes/me.ts` is an example
77
+ {{/if}}
78
+ {{#if useRateLimit}}
79
+ - `src/middleware/rateLimit.ts`: the rate limiters
80
+ {{/if}}
81
+ {{#if (eq database 'prisma')}}
82
+ - `src/lib/prisma.ts`: the shared Prisma client. Do not create another one
83
+ {{/if}}
84
+ {{#if (eq database 'drizzle')}}
85
+ - `src/lib/db.ts`, `src/lib/schema.ts`: the database client and the tables
86
+ {{/if}}
87
+ {{#if (eq database 'mongoose')}}
88
+ - `src/lib/db.ts`, `src/models/`: the connection and the Mongoose models
89
+ {{/if}}
90
+ {{/if}}
91
+ {{#if (eq framework 'nestjs')}}
92
+ - `src/main.ts`: starts the app
93
+ - `src/app.module.ts`: the root module. Register new modules and controllers here
94
+ - `src/env.ts`: checks the environment variables with Zod at startup, so a missing one stops the server with a clear message
95
+ - `src/health/`: `GET /health`
96
+ {{#if useAuth}}
97
+ - `src/auth/`: the Better Auth server (`auth.ts`), the emails it sends (`email.ts`), and the module that mounts it on `/api/auth` (`auth.module.ts`)
98
+ - Every route needs a session unless it has `@AllowAnonymous()` (or `@OptionalAuth()`). Read the session with `@Session()`, as `src/me/me.controller.ts` does
99
+ {{/if}}
100
+ {{#if (eq database 'prisma')}}
101
+ - `src/prisma/`: `PrismaService`, injected wherever the database is needed
102
+ {{/if}}
103
+ {{#if (eq database 'drizzle')}}
104
+ - `src/database/`: the database module and the tables in `schema.ts`
105
+ {{/if}}
106
+ {{#if (eq database 'mongoose')}}
107
+ - `src/users/user.schema.ts`: the Mongoose schema for users
108
+ {{/if}}
109
+ {{/if}}
110
+ {{#if (eq framework 'nextjs')}}
111
+ - `app/`: routes, layouts, and route handlers. Pages are Server Components unless they need state, effects, or event handlers
112
+ - `components/ui/`: shadcn/ui components built on Base UI (`@base-ui/react`). Add new ones with the shadcn CLI instead of writing them by hand. Use the `render` prop to change the rendered element, not Radix's `asChild`
113
+ - `lib/utils.ts`: the `cn()` class name helper
114
+ - `app/globals.css`: the shadcn theme. Style with its tokens (`bg-primary`, `text-muted-foreground`), not raw Tailwind colors, and space with `flex` + `gap-*`, not `space-y-*`
115
+ {{#if useAuth}}
116
+ - `lib/auth.ts`: the Better Auth server. `lib/auth-client.ts`: the client for components. `lib/email.ts`: sends the verification and password reset emails
117
+ - `lib/session.ts`: `getSession()` reads the session on the server, once per request
118
+ - `app/api/auth/[...all]/route.ts`: the Better Auth endpoints. Do not rename it
119
+ - `proxy.ts`: sends signed-out visitors from `/dashboard` to `/sign-in`. Add other protected paths to its `matcher`
120
+ {{/if}}
121
+ {{#if (eq database 'prisma')}}
122
+ - `lib/prisma.ts`: the shared Prisma client. Do not create another one
123
+ {{/if}}
124
+ {{#if (eq database 'drizzle')}}
125
+ - `lib/db.ts`, `lib/schema.ts`: the database client and the tables
126
+ {{/if}}
127
+ {{#if (eq database 'mongoose')}}
128
+ - `lib/db.ts`, `models/`: the connection and the Mongoose models
129
+ {{/if}}
130
+ {{/if}}
131
+ {{#if (eq framework 'tanstack')}}
132
+ - `src/routes/`: file-based routes. A new file becomes a route
133
+ - `src/routeTree.gen.ts`: generated by the dev server and the build. Never edit it
134
+ - `src/router.tsx`, `src/routes/__root.tsx`: the router and the root layout
135
+ - `src/components/ui/`: shadcn/ui components built on Base UI (`@base-ui/react`). Add new ones with the shadcn CLI instead of writing them by hand. Use the `render` prop to change the rendered element, not Radix's `asChild`
136
+ - `src/lib/utils.ts`: the `cn()` class name helper
137
+ - `src/styles/app.css`: the shadcn theme. Style with its tokens (`bg-primary`, `text-muted-foreground`), not raw Tailwind colors, and space with `flex` + `gap-*`, not `space-y-*`
138
+ {{#if useAuth}}
139
+ - `src/lib/auth.ts`: the Better Auth server. `src/lib/auth-client.ts`: the client for components. `src/lib/email.ts`: sends the verification and password reset emails
140
+ - `src/routes/api/auth/$.ts`: the Better Auth endpoints. Do not rename it
141
+ - `src/routes/dashboard.tsx`: an example of a protected route that redirects in `beforeLoad`
142
+ {{/if}}
143
+ {{#if (eq database 'prisma')}}
144
+ - `src/lib/prisma.ts`: the shared Prisma client. Do not create another one
145
+ {{/if}}
146
+ {{#if (eq database 'drizzle')}}
147
+ - `src/lib/db.ts`, `src/lib/schema.ts`: the database client and the tables
148
+ {{/if}}
149
+ {{#if (eq database 'mongoose')}}
150
+ - `src/lib/db.ts`, `src/models/`: the connection and the Mongoose models
151
+ {{/if}}
152
+ {{/if}}
153
+
154
+ ## Rules
155
+
156
+ - TypeScript is strict. Do not use `any` or `@ts-ignore` to silence an error; fix the type
157
+ {{#if (eq framework 'nestjs')}}
158
+ - Relative imports end in `.js`, as ES modules require: `import { AuthModule } from "./auth/auth.module.js"`
159
+ {{/if}}
160
+ - Keep `.env` out of git. When you add an environment variable, add it to `.env.example` too{{#if (eq framework 'express')}}, and to `src/env.ts`{{/if}}{{#if (eq framework 'nestjs')}}, and to `src/env.ts`{{/if}}
161
+ {{#if (eq database 'prisma')}}
162
+ - The Prisma client is generated into {{#if (eq framework 'express')}}`src/generated/`{{/if}}{{#if (eq framework 'nestjs')}}`src/generated/`{{/if}}{{#if (eq framework 'nextjs')}}`generated/`{{/if}}{{#if (eq framework 'tanstack')}}`generated/`{{/if}}. Never edit or commit it
163
+ {{/if}}
164
+ - Match the existing code style. ESLint settings are in `eslint.config.mjs`{{#if (eq framework 'express')}} and Prettier settings in `.prettierrc`{{/if}}{{#if (eq framework 'nestjs')}} and Prettier settings in `.prettierrc`{{/if}}
165
+ {{#if (eq framework 'nextjs')}}
166
+ - Write Tailwind classes in their canonical form. Use the theme utility when one exists (`rounded-4xl`, not `rounded-[2rem]`), and in an arbitrary value write `_` only where CSS needs a space, never after a comma: `bg-[linear-gradient(180deg,#f8fafc_0%,#eef2ff_100%)]`. The Tailwind CSS VS Code extension warns about the long forms
167
+ {{/if}}
168
+ {{#if (eq framework 'tanstack')}}
169
+ - Write Tailwind classes in their canonical form. Use the theme utility when one exists (`rounded-4xl`, not `rounded-[2rem]`), and in an arbitrary value write `_` only where CSS needs a space, never after a comma: `bg-[linear-gradient(180deg,#f8fafc_0%,#eef2ff_100%)]`. The Tailwind CSS VS Code extension warns about the long forms
170
+ {{/if}}
171
+ - Add a dependency only when the task needs it, and install it with {{packageManager}}
172
+
173
+ ## Production rules
174
+
175
+ - Validate every input from outside the app (request bodies, query strings, form data) with Zod before you use it{{#unless useAuth}}{{#if (eq framework 'nextjs')}} (add `zod` with {{packageManager}} first){{/if}}{{#if (eq framework 'tanstack')}} (add `zod` with {{packageManager}} first){{/if}}{{/unless}}
176
+ - Never log or return secrets, tokens, or passwords. Error responses get a short message and the right status code, never a stack trace
177
+ {{#unless (eq database 'none')}}
178
+ - Query the database through {{#if (eq database 'prisma')}}Prisma{{/if}}{{#if (eq database 'drizzle')}}Drizzle{{/if}}{{#if (eq database 'mongoose')}}Mongoose{{/if}}, never with SQL or queries built from strings
179
+ {{/unless}}
180
+ {{#if (eq framework 'express')}}
181
+ - Keep the security middleware in `src/index.ts` (`helmet`, `cors`{{#if useRateLimit}}, the rate limiter{{/if}}) in place. Set `CORS_ORIGIN` to your real origins in production
182
+ {{/if}}
183
+ {{#if (eq framework 'nestjs')}}
184
+ - Keep the security setup in `src/main.ts` and `src/app.module.ts` in place. Set `CORS_ORIGIN` to your real origins in production
185
+ {{/if}}
186
+ {{#if (eq framework 'nextjs')}}
187
+ - Server first: do not `await` data at the top of a page or layout, which holds back the whole page. Move each `await` into the component that needs the data and wrap that component in `<Suspense>` with a fallback, so the rest of the page is sent at once{{#if useAuth}}. `app/dashboard/page.tsx` shows the pattern with `getSession()`{{/if}}
188
+ - Code that reads secrets or the database stays on the server. Only variables prefixed with `NEXT_PUBLIC_` reach the browser, so never put a secret in one
189
+ {{/if}}
190
+ {{#if (eq framework 'tanstack')}}
191
+ - Code that reads secrets or the database runs in server functions or server routes. Only variables prefixed with `VITE_` reach the browser, so never put a secret in one
192
+ {{/if}}
193
+ {{#if useAuth}}
194
+ {{#if (eq framework 'nextjs')}}
195
+ - Check the session on the server in every protected page, route handler, and server action. Hiding a button in the UI does not protect anything
196
+ {{/if}}
197
+ {{#if (eq framework 'tanstack')}}
198
+ - Check the session on the server in every protected route and server function. Hiding a button in the UI does not protect anything
199
+ {{/if}}
200
+ {{/if}}
201
+ {{#if (eq framework 'express')}}
202
+ {{#if useAuth}}
203
+ - Check the session on the server in every protected route with `requireAuth`
204
+ {{/if}}
205
+ - Keep `GET /health` fast and free of auth{{#if useRateLimit}} and rate limiting{{/if}}, so hosts can check that the app is up
206
+ {{/if}}
207
+ {{#if (eq framework 'nestjs')}}
208
+ - Keep `GET /health` fast and free of auth{{#if useRateLimit}} and rate limiting{{/if}}, so hosts can check that the app is up
209
+ {{/if}}
210
+ - Keep changes small and focused on the task. Do not add layers, abstractions, or files the task does not need
211
+
212
+ ## When you are unsure
213
+
214
+ The libraries here may be newer than what you know. When you are not sure how an API works, read the official docs for the version in `package.json` instead of guessing:
215
+
216
+ {{#if (eq framework 'express')}}
217
+ - Express 5: https://expressjs.com/en/5x/api.html
218
+ {{/if}}
219
+ {{#if (eq framework 'nestjs')}}
220
+ - NestJS: https://docs.nestjs.com
221
+ {{/if}}
222
+ {{#if (eq framework 'nextjs')}}
223
+ - Next.js: https://nextjs.org/docs
224
+ {{/if}}
225
+ {{#if (eq framework 'tanstack')}}
226
+ - TanStack Start: https://tanstack.com/start/latest/docs
227
+ {{/if}}
228
+ {{#if (eq database 'prisma')}}
229
+ - Prisma: https://www.prisma.io/docs
230
+ {{/if}}
231
+ {{#if (eq database 'drizzle')}}
232
+ - Drizzle: https://orm.drizzle.team/docs/overview
233
+ {{/if}}
234
+ {{#if (eq database 'mongoose')}}
235
+ - Mongoose: https://mongoosejs.com/docs
236
+ {{/if}}
237
+ {{#if useAuth}}
238
+ - Better Auth: https://www.better-auth.com/docs
239
+ {{#if (eq framework 'nestjs')}}
240
+ - NestJS Better Auth integration: https://github.com/ThallesP/nestjs-better-auth
241
+ {{/if}}
242
+ {{/if}}
243
+ - Zod: https://zod.dev
244
+
245
+ If the docs and this file disagree, follow the docs and say so.
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "agents",
3
+ "name": "AI agent instructions",
4
+ "description": "An AGENTS.md that tells AI coding agents how this project is built, run, and checked, and a CLAUDE.md that points Claude Code to it",
5
+ "type": "feature",
6
+ "dependencies": {},
7
+ "devDependencies": {},
8
+ "scripts": {},
9
+ "envVars": {}
10
+ }