@devmunna/agent-skillkit 0.1.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 (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -0
  3. package/bin/ai-skills.js +5 -0
  4. package/dist/cli/commands/add.d.ts +2 -0
  5. package/dist/cli/commands/add.d.ts.map +1 -0
  6. package/dist/cli/commands/add.js +66 -0
  7. package/dist/cli/commands/add.js.map +1 -0
  8. package/dist/cli/commands/doctor.d.ts +2 -0
  9. package/dist/cli/commands/doctor.d.ts.map +1 -0
  10. package/dist/cli/commands/doctor.js +33 -0
  11. package/dist/cli/commands/doctor.js.map +1 -0
  12. package/dist/cli/commands/init.d.ts +10 -0
  13. package/dist/cli/commands/init.d.ts.map +1 -0
  14. package/dist/cli/commands/init.js +145 -0
  15. package/dist/cli/commands/init.js.map +1 -0
  16. package/dist/cli/commands/list.d.ts +5 -0
  17. package/dist/cli/commands/list.d.ts.map +1 -0
  18. package/dist/cli/commands/list.js +55 -0
  19. package/dist/cli/commands/list.js.map +1 -0
  20. package/dist/cli/commands/update.d.ts +2 -0
  21. package/dist/cli/commands/update.d.ts.map +1 -0
  22. package/dist/cli/commands/update.js +49 -0
  23. package/dist/cli/commands/update.js.map +1 -0
  24. package/dist/cli/commands/validate.d.ts +2 -0
  25. package/dist/cli/commands/validate.d.ts.map +1 -0
  26. package/dist/cli/commands/validate.js +22 -0
  27. package/dist/cli/commands/validate.js.map +1 -0
  28. package/dist/cli/index.d.ts +2 -0
  29. package/dist/cli/index.d.ts.map +1 -0
  30. package/dist/cli/index.js +49 -0
  31. package/dist/cli/index.js.map +1 -0
  32. package/dist/cli/prompts/agent-selector.d.ts +3 -0
  33. package/dist/cli/prompts/agent-selector.d.ts.map +1 -0
  34. package/dist/cli/prompts/agent-selector.js +23 -0
  35. package/dist/cli/prompts/agent-selector.js.map +1 -0
  36. package/dist/cli/prompts/stack-selector.d.ts +3 -0
  37. package/dist/cli/prompts/stack-selector.d.ts.map +1 -0
  38. package/dist/cli/prompts/stack-selector.js +60 -0
  39. package/dist/cli/prompts/stack-selector.js.map +1 -0
  40. package/dist/core/config-manager.d.ts +20 -0
  41. package/dist/core/config-manager.d.ts.map +1 -0
  42. package/dist/core/config-manager.js +107 -0
  43. package/dist/core/config-manager.js.map +1 -0
  44. package/dist/core/detector.d.ts +3 -0
  45. package/dist/core/detector.d.ts.map +1 -0
  46. package/dist/core/detector.js +50 -0
  47. package/dist/core/detector.js.map +1 -0
  48. package/dist/core/doctor.d.ts +12 -0
  49. package/dist/core/doctor.d.ts.map +1 -0
  50. package/dist/core/doctor.js +102 -0
  51. package/dist/core/doctor.js.map +1 -0
  52. package/dist/core/skill-registry.d.ts +11 -0
  53. package/dist/core/skill-registry.d.ts.map +1 -0
  54. package/dist/core/skill-registry.js +174 -0
  55. package/dist/core/skill-registry.js.map +1 -0
  56. package/dist/core/skill-resolver.d.ts +3 -0
  57. package/dist/core/skill-resolver.d.ts.map +1 -0
  58. package/dist/core/skill-resolver.js +36 -0
  59. package/dist/core/skill-resolver.js.map +1 -0
  60. package/dist/core/validator.d.ts +13 -0
  61. package/dist/core/validator.d.ts.map +1 -0
  62. package/dist/core/validator.js +99 -0
  63. package/dist/core/validator.js.map +1 -0
  64. package/dist/generators/agent-installer.d.ts +5 -0
  65. package/dist/generators/agent-installer.d.ts.map +1 -0
  66. package/dist/generators/agent-installer.js +20 -0
  67. package/dist/generators/agent-installer.js.map +1 -0
  68. package/dist/generators/agents-md.d.ts +3 -0
  69. package/dist/generators/agents-md.d.ts.map +1 -0
  70. package/dist/generators/agents-md.js +70 -0
  71. package/dist/generators/agents-md.js.map +1 -0
  72. package/dist/generators/claude-md.d.ts +3 -0
  73. package/dist/generators/claude-md.d.ts.map +1 -0
  74. package/dist/generators/claude-md.js +47 -0
  75. package/dist/generators/claude-md.js.map +1 -0
  76. package/dist/generators/skill-generator.d.ts +5 -0
  77. package/dist/generators/skill-generator.d.ts.map +1 -0
  78. package/dist/generators/skill-generator.js +34 -0
  79. package/dist/generators/skill-generator.js.map +1 -0
  80. package/dist/generators/workflows.d.ts +3 -0
  81. package/dist/generators/workflows.d.ts.map +1 -0
  82. package/dist/generators/workflows.js +57 -0
  83. package/dist/generators/workflows.js.map +1 -0
  84. package/dist/index.d.ts +13 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +13 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/types/index.d.ts +55 -0
  89. package/dist/types/index.d.ts.map +1 -0
  90. package/dist/types/index.js +2 -0
  91. package/dist/types/index.js.map +1 -0
  92. package/dist/utils/file-utils.d.ts +12 -0
  93. package/dist/utils/file-utils.d.ts.map +1 -0
  94. package/dist/utils/file-utils.js +39 -0
  95. package/dist/utils/file-utils.js.map +1 -0
  96. package/dist/utils/logger.d.ts +10 -0
  97. package/dist/utils/logger.d.ts.map +1 -0
  98. package/dist/utils/logger.js +11 -0
  99. package/dist/utils/logger.js.map +1 -0
  100. package/package.json +73 -0
  101. package/skills/clean-architecture/SKILL.md +324 -0
  102. package/skills/express-mvc-prisma/SKILL.md +168 -0
  103. package/skills/express-mvc-prisma/references/auth.md +190 -0
  104. package/skills/express-mvc-prisma/references/boilerplate.md +196 -0
  105. package/skills/express-mvc-prisma/references/error-handling.md +121 -0
  106. package/skills/express-mvc-prisma/references/module-scaffold.md +253 -0
  107. package/skills/express-mvc-prisma/references/prisma-setup.md +97 -0
  108. package/skills/express-mvc-prisma/references/response-helpers.md +157 -0
  109. package/skills/express-mvc-prisma/references/zod-validation.md +157 -0
  110. package/skills/fastify-rest/SKILL.md +287 -0
  111. package/skills/mongoose-odm/SKILL.md +281 -0
  112. package/skills/nextjs-fullstack/SKILL.md +328 -0
  113. package/skills/nextjs-fullstack/references/auth.md +270 -0
  114. package/skills/nextjs-fullstack/references/caching.md +157 -0
  115. package/skills/nextjs-fullstack/references/route-handlers.md +194 -0
  116. package/skills/nextjs-fullstack/references/server-actions.md +214 -0
  117. package/skills/nextjs-fullstack/references/server-components.md +190 -0
  118. package/skills/node-base/SKILL.md +139 -0
  119. package/skills/prisma-orm/SKILL.md +334 -0
  120. package/skills/react-feature-arch/SKILL.md +208 -0
  121. package/skills/react-feature-arch/references/api-layer.md +110 -0
  122. package/skills/react-feature-arch/references/components.md +192 -0
  123. package/skills/react-feature-arch/references/data-fetching.md +198 -0
  124. package/skills/react-feature-arch/references/forms.md +194 -0
  125. package/skills/react-feature-arch/references/routing.md +148 -0
  126. package/skills/react-feature-arch/references/state-management.md +107 -0
  127. package/skills/tailwind-css/SKILL.md +236 -0
  128. package/skills/tailwind-css/references/components.md +340 -0
  129. package/skills/tailwind-css/references/design-tokens.md +230 -0
  130. package/skills/tailwind-css/references/patterns.md +375 -0
  131. package/skills/tailwind-css/references/setup.md +165 -0
  132. package/skills/zod-validation/SKILL.md +267 -0
@@ -0,0 +1,328 @@
1
+ ---
2
+ name: nextjs-fullstack
3
+ version: 2.0.0
4
+ description: >
5
+ Use this skill for any Next.js project task: App Router routing, Server Components, Client Components, Server Actions, Route Handlers, caching, auth, metadata, or deployment. Triggers: "Next.js App Router", "Server Component", "Server Action", "next/navigation", "layout.tsx", "page.tsx", "Route Handler", "next-auth", "Auth.js", "use cache", "after()", "PPR", "params Promise".
6
+ stack: [nextjs, react, typescript, prisma]
7
+ depends: []
8
+ ---
9
+
10
+ # Next.js 15 — Full-Stack Production Skill
11
+
12
+ **Version target:** Next.js 15 · React 19 · TypeScript 5 · Prisma v5 · Auth.js v5 (next-auth)
13
+
14
+ ---
15
+
16
+ ## Tech Stack
17
+
18
+ | Layer | Package |
19
+ |---|---|
20
+ | Framework | next v15 |
21
+ | UI | react v19 + react-dom v19 |
22
+ | ORM | @prisma/client v5 |
23
+ | Auth | next-auth v5 (Auth.js) |
24
+ | Validation | zod v4 |
25
+ | Styling | tailwindcss v4 |
26
+ | Language | TypeScript 5 |
27
+
28
+ ---
29
+
30
+ ## App Router Folder Structure
31
+
32
+ ```
33
+ src/
34
+ ├── app/
35
+ │ ├── layout.tsx # Root HTML shell (Server Component)
36
+ │ ├── page.tsx # Home page
37
+ │ ├── loading.tsx # Suspense fallback (auto-wrapped)
38
+ │ ├── error.tsx # Error boundary (must be 'use client')
39
+ │ ├── not-found.tsx # 404 page
40
+ │ ├── globals.css
41
+ │ ├── (auth)/ # Route group — no URL segment
42
+ │ │ ├── login/page.tsx
43
+ │ │ └── register/page.tsx
44
+ │ ├── (dashboard)/
45
+ │ │ ├── layout.tsx # Shared layout with sidebar
46
+ │ │ ├── dashboard/page.tsx
47
+ │ │ └── users/
48
+ │ │ ├── page.tsx # /users list
49
+ │ │ ├── [id]/
50
+ │ │ │ └── page.tsx # /users/:id detail
51
+ │ │ └── new/page.tsx # /users/new create form
52
+ │ └── api/
53
+ │ └── {resource}/
54
+ │ ├── route.ts # GET /api/resource, POST /api/resource
55
+ │ └── [id]/
56
+ │ └── route.ts # GET, PATCH, DELETE /api/resource/:id
57
+ ├── features/ # Domain modules
58
+ │ └── {feature}/
59
+ │ ├── {feature}.actions.ts # Server Actions ('use server')
60
+ │ ├── {feature}.queries.ts # DB query functions (Prisma calls)
61
+ │ ├── {feature}.schema.ts # Zod schemas + TypeScript types
62
+ │ └── components/
63
+ │ └── {Feature}Form.tsx # Client form components
64
+ ├── components/
65
+ │ ├── ui/ # Shared primitive Client components
66
+ │ └── server/ # Server component compositions
67
+ ├── lib/
68
+ │ ├── db.ts # Prisma singleton
69
+ │ ├── auth.ts # Auth.js config
70
+ │ └── session.ts # Session helpers
71
+ └── types/
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Server vs Client Components
77
+
78
+ **Default: Server Component.** Add `'use client'` ONLY when the component needs:
79
+ - `useState`, `useEffect`, `useRef`, `useContext`
80
+ - Browser APIs (`window`, `localStorage`, `document`)
81
+ - Event handlers (`onClick`, `onChange`, `onSubmit`)
82
+ - `useActionState`, `useOptimistic`, `useFormStatus` (React 19)
83
+ - Third-party client-only libraries
84
+
85
+ ```tsx
86
+ // app/(dashboard)/users/page.tsx — Server Component (no directive)
87
+ import { db } from '@/lib/db';
88
+ import { UserList } from '@/features/users/components/UserList';
89
+
90
+ export default async function UsersPage() {
91
+ const users = await db.user.findMany({
92
+ select: { id: true, name: true, email: true, role: true },
93
+ orderBy: { createdAt: 'desc' },
94
+ });
95
+ return <UserList users={users} />;
96
+ }
97
+
98
+ // features/users/components/DeleteButton.tsx — must be Client Component
99
+ 'use client';
100
+
101
+ import { useTransition } from 'react';
102
+ import { deleteUserAction } from '../users.actions';
103
+
104
+ export function DeleteButton({ userId }: { userId: string }) {
105
+ const [isPending, startTransition] = useTransition();
106
+
107
+ return (
108
+ <button
109
+ onClick={() => startTransition(() => deleteUserAction(userId))}
110
+ disabled={isPending}
111
+ >
112
+ {isPending ? 'Deleting...' : 'Delete'}
113
+ </button>
114
+ );
115
+ }
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Next.js 15 — Critical Changes
121
+
122
+ ### params is now a Promise in dynamic routes
123
+ ```tsx
124
+ // Next.js 15 — params AND searchParams are Promises, must be awaited
125
+ interface Props {
126
+ params: Promise<{ id: string }>;
127
+ searchParams: Promise<{ page?: string }>;
128
+ }
129
+
130
+ export default async function UserPage({ params, searchParams }: Props) {
131
+ const { id } = await params;
132
+ const { page } = await searchParams;
133
+
134
+ const user = await db.user.findUnique({ where: { id } });
135
+ if (!user) notFound();
136
+
137
+ return <UserDetail user={user} />;
138
+ }
139
+ ```
140
+
141
+ ### `use cache` directive — function-level caching (Next.js 15)
142
+ ```ts
143
+ // features/users/users.queries.ts
144
+ 'use cache'; // caches this module's exports
145
+
146
+ import { cacheTag, cacheLife } from 'next/cache';
147
+ import { db } from '@/lib/db';
148
+
149
+ export async function getUsers() {
150
+ cacheTag('users');
151
+ cacheLife('hours');
152
+ return db.user.findMany({ orderBy: { createdAt: 'desc' } });
153
+ }
154
+
155
+ export async function getUserById(id: string) {
156
+ cacheTag('users', `user-${id}`);
157
+ cacheLife('minutes');
158
+ return db.user.findUnique({ where: { id } });
159
+ }
160
+ ```
161
+
162
+ On-demand invalidation (in Server Action or Route Handler):
163
+ ```ts
164
+ import { revalidateTag } from 'next/cache';
165
+ revalidateTag('users'); // invalidates all 'users' tagged cache
166
+ revalidateTag(`user-${id}`); // invalidate specific user cache
167
+ ```
168
+
169
+ ### `after()` — post-response side effects
170
+ ```ts
171
+ import { after } from 'next/server';
172
+
173
+ export async function createUserAction(formData: FormData) {
174
+ const user = await db.user.create({ data: /* ... */ });
175
+
176
+ // Runs AFTER response is sent — non-blocking, no delay to user
177
+ after(async () => {
178
+ await sendWelcomeEmail(user.email);
179
+ await createAuditLog('user.created', { userId: user.id });
180
+ });
181
+
182
+ revalidateTag('users');
183
+ }
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Server Actions
189
+
190
+ Handle all mutations in Server Actions. Call from Client Components or HTML forms.
191
+
192
+ ```ts
193
+ // features/users/users.actions.ts
194
+ 'use server';
195
+
196
+ import { db } from '@/lib/db';
197
+ import { auth } from '@/lib/auth';
198
+ import { revalidateTag } from 'next/cache';
199
+ import { redirect } from 'next/navigation';
200
+ import { createUserSchema } from './users.schema';
201
+
202
+ export async function createUserAction(_prev: unknown, formData: FormData) {
203
+ const session = await auth();
204
+ if (!session) return { error: 'Unauthorized' };
205
+
206
+ const result = createUserSchema.safeParse({
207
+ name: formData.get('name'),
208
+ email: formData.get('email'),
209
+ });
210
+
211
+ if (!result.success) {
212
+ return { error: result.error.issues[0].message };
213
+ }
214
+
215
+ try {
216
+ await db.user.create({ data: result.data });
217
+ } catch {
218
+ return { error: 'Failed to create user' };
219
+ }
220
+
221
+ revalidateTag('users');
222
+ redirect('/users');
223
+ }
224
+
225
+ export async function deleteUserAction(id: string) {
226
+ const session = await auth();
227
+ if (!session?.user || session.user.role !== 'ADMIN') {
228
+ throw new Error('Forbidden');
229
+ }
230
+
231
+ await db.user.delete({ where: { id } });
232
+ revalidateTag('users');
233
+ revalidateTag(`user-${id}`);
234
+ }
235
+ ```
236
+
237
+ Client form using `useActionState` (React 19):
238
+ ```tsx
239
+ 'use client';
240
+
241
+ import { useActionState } from 'react';
242
+ import { createUserAction } from '../users.actions';
243
+
244
+ export function CreateUserForm() {
245
+ const [state, formAction, isPending] = useActionState(createUserAction, null);
246
+
247
+ return (
248
+ <form action={formAction}>
249
+ <input name="name" required />
250
+ <input name="email" type="email" required />
251
+ {state?.error && <p className="error">{state.error}</p>}
252
+ <button type="submit" disabled={isPending}>
253
+ {isPending ? 'Creating...' : 'Create User'}
254
+ </button>
255
+ </form>
256
+ );
257
+ }
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Error Handling
263
+
264
+ ```tsx
265
+ // app/error.tsx — must be 'use client'
266
+ 'use client';
267
+
268
+ export default function Error({
269
+ error,
270
+ reset,
271
+ }: {
272
+ error: Error & { digest?: string };
273
+ reset: () => void;
274
+ }) {
275
+ return (
276
+ <div>
277
+ <h2>Something went wrong</h2>
278
+ <button onClick={reset}>Try again</button>
279
+ </div>
280
+ );
281
+ }
282
+ ```
283
+
284
+ In Server Components: throw for unexpected errors, `notFound()` for 404s:
285
+ ```tsx
286
+ import { notFound } from 'next/navigation';
287
+
288
+ if (!user) notFound(); // renders not-found.tsx
289
+ if (!user) throw new Error('x'); // renders error.tsx
290
+ ```
291
+
292
+ ---
293
+
294
+ ## Metadata
295
+
296
+ ```tsx
297
+ import type { Metadata } from 'next';
298
+
299
+ // Static metadata
300
+ export const metadata: Metadata = {
301
+ title: { template: '%s | My App', default: 'My App' },
302
+ description: 'App description',
303
+ openGraph: { type: 'website' },
304
+ };
305
+
306
+ // Dynamic metadata (Next.js 15 — params is a Promise)
307
+ export async function generateMetadata({
308
+ params,
309
+ }: {
310
+ params: Promise<{ id: string }>;
311
+ }): Promise<Metadata> {
312
+ const { id } = await params;
313
+ const user = await db.user.findUnique({ where: { id } });
314
+ return { title: user?.name ?? 'User not found' };
315
+ }
316
+ ```
317
+
318
+ ---
319
+
320
+ ## Core References
321
+
322
+ | Topic | File |
323
+ |---|---|
324
+ | Server Components + data fetching patterns | `references/server-components.md` |
325
+ | Server Actions + useActionState | `references/server-actions.md` |
326
+ | Route Handlers (REST API) | `references/route-handlers.md` |
327
+ | Caching — use cache, revalidate, tags | `references/caching.md` |
328
+ | Auth.js v5 (next-auth) setup | `references/auth.md` |
@@ -0,0 +1,270 @@
1
+ # Auth Reference — Auth.js v5 (next-auth)
2
+
3
+ JWT-based session auth. `auth()` reads the session in Server Components, Server Actions, and Route Handlers.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install next-auth@beta
11
+ npx auth secret # generates AUTH_SECRET in .env
12
+ ```
13
+
14
+ ---
15
+
16
+ ## lib/auth.ts — Configuration
17
+
18
+ ```ts
19
+ // src/lib/auth.ts
20
+ import NextAuth from 'next-auth';
21
+ import CredentialsProvider from 'next-auth/providers/credentials';
22
+ import { PrismaAdapter } from '@auth/prisma-adapter';
23
+ import { db } from './db';
24
+ import bcrypt from 'bcrypt';
25
+ import { z } from 'zod';
26
+
27
+ const loginSchema = z.object({
28
+ email: z.string().email(),
29
+ password: z.string().min(1),
30
+ });
31
+
32
+ export const { handlers, signIn, signOut, auth } = NextAuth({
33
+ adapter: PrismaAdapter(db),
34
+
35
+ session: { strategy: 'jwt' },
36
+
37
+ pages: {
38
+ signIn: '/auth/login',
39
+ },
40
+
41
+ providers: [
42
+ CredentialsProvider({
43
+ async authorize(credentials) {
44
+ const parsed = loginSchema.safeParse(credentials);
45
+ if (!parsed.success) return null;
46
+
47
+ const user = await db.user.findUnique({
48
+ where: { email: parsed.data.email },
49
+ });
50
+ if (!user || !user.password) return null;
51
+
52
+ const valid = await bcrypt.compare(parsed.data.password, user.password);
53
+ if (!valid) return null;
54
+
55
+ return { id: user.id, name: user.name, email: user.email, role: user.role };
56
+ },
57
+ }),
58
+ ],
59
+
60
+ callbacks: {
61
+ // Add role to JWT
62
+ jwt({ token, user }) {
63
+ if (user) token.role = (user as any).role;
64
+ return token;
65
+ },
66
+ // Expose role in session
67
+ session({ session, token }) {
68
+ session.user.role = token.role as string;
69
+ return session;
70
+ },
71
+ },
72
+ });
73
+ ```
74
+
75
+ ---
76
+
77
+ ## app/api/auth/[...nextauth]/route.ts
78
+
79
+ ```ts
80
+ import { handlers } from '@/lib/auth';
81
+ export const { GET, POST } = handlers;
82
+ ```
83
+
84
+ ---
85
+
86
+ ## .env
87
+
88
+ ```
89
+ AUTH_SECRET=your-generated-secret-at-least-32-chars
90
+ AUTH_URL=http://localhost:3000
91
+ ```
92
+
93
+ ---
94
+
95
+ ## TypeScript — Extend Session Types
96
+
97
+ ```ts
98
+ // types/next-auth.d.ts
99
+ import 'next-auth';
100
+ import 'next-auth/jwt';
101
+
102
+ declare module 'next-auth' {
103
+ interface Session {
104
+ user: { id: string; name: string; email: string; role: string };
105
+ }
106
+ }
107
+
108
+ declare module 'next-auth/jwt' {
109
+ interface JWT {
110
+ role: string;
111
+ }
112
+ }
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Usage — Server Components & Actions
118
+
119
+ ```ts
120
+ // Reading session in Server Component / Action / Route Handler
121
+ import { auth } from '@/lib/auth';
122
+ import { redirect } from 'next/navigation';
123
+
124
+ // Server Component
125
+ export default async function ProtectedPage() {
126
+ const session = await auth();
127
+ if (!session) redirect('/auth/login');
128
+
129
+ return <div>Hello {session.user.name}</div>;
130
+ }
131
+
132
+ // Server Action
133
+ export async function sensitiveAction() {
134
+ const session = await auth();
135
+ if (!session) throw new Error('Unauthorized');
136
+ if (session.user.role !== 'ADMIN') throw new Error('Forbidden');
137
+ // ... proceed
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Middleware — Route Protection
144
+
145
+ ```ts
146
+ // middleware.ts (root of project, not inside src/)
147
+ import { auth } from '@/lib/auth';
148
+ import { NextResponse } from 'next/server';
149
+
150
+ export default auth((req) => {
151
+ const isLoggedIn = !!req.auth;
152
+ const isAuthRoute = req.nextUrl.pathname.startsWith('/auth');
153
+ const isApiRoute = req.nextUrl.pathname.startsWith('/api');
154
+
155
+ if (isAuthRoute) {
156
+ if (isLoggedIn) return NextResponse.redirect(new URL('/dashboard', req.url));
157
+ return NextResponse.next();
158
+ }
159
+
160
+ if (!isLoggedIn && !isApiRoute) {
161
+ return NextResponse.redirect(new URL('/auth/login', req.url));
162
+ }
163
+
164
+ return NextResponse.next();
165
+ });
166
+
167
+ export const config = {
168
+ matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
169
+ };
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Login Form (Client Component)
175
+
176
+ ```tsx
177
+ // features/auth/components/LoginForm.tsx
178
+ 'use client';
179
+
180
+ import { useActionState } from 'react';
181
+ import { signIn } from 'next-auth/react';
182
+ import { useRouter } from 'next/navigation';
183
+
184
+ async function loginAction(_prev: any, formData: FormData) {
185
+ const result = await signIn('credentials', {
186
+ email: formData.get('email'),
187
+ password: formData.get('password'),
188
+ redirect: false,
189
+ });
190
+
191
+ if (result?.error) return { error: 'Invalid email or password' };
192
+ return { success: true };
193
+ }
194
+
195
+ export function LoginForm() {
196
+ const router = useRouter();
197
+ const [state, formAction, isPending] = useActionState(loginAction, null);
198
+
199
+ if (state?.success) {
200
+ router.push('/dashboard');
201
+ return null;
202
+ }
203
+
204
+ return (
205
+ <form action={formAction}>
206
+ <input name="email" type="email" required placeholder="Email" />
207
+ <input name="password" type="password" required placeholder="Password" />
208
+ {state?.error && <p className="error">{state.error}</p>}
209
+ <button type="submit" disabled={isPending}>
210
+ {isPending ? 'Signing in...' : 'Sign in'}
211
+ </button>
212
+ </form>
213
+ );
214
+ }
215
+ ```
216
+
217
+ ---
218
+
219
+ ## Prisma Schema for Auth.js
220
+
221
+ ```prisma
222
+ model User {
223
+ id String @id @default(uuid())
224
+ name String?
225
+ email String @unique
226
+ emailVerified DateTime?
227
+ image String?
228
+ password String? // null for OAuth users
229
+ role Role @default(USER)
230
+ accounts Account[]
231
+ sessions Session[]
232
+ createdAt DateTime @default(now())
233
+ updatedAt DateTime @updatedAt
234
+
235
+ @@map("users")
236
+ }
237
+
238
+ model Account {
239
+ id String @id @default(uuid())
240
+ userId String
241
+ type String
242
+ provider String
243
+ providerAccountId String
244
+ refresh_token String?
245
+ access_token String?
246
+ expires_at Int?
247
+ token_type String?
248
+ scope String?
249
+ id_token String?
250
+ session_state String?
251
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
252
+
253
+ @@unique([provider, providerAccountId])
254
+ @@index([userId])
255
+ @@map("accounts")
256
+ }
257
+
258
+ model Session {
259
+ id String @id @default(uuid())
260
+ sessionToken String @unique
261
+ userId String
262
+ expires DateTime
263
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
264
+
265
+ @@index([userId])
266
+ @@map("sessions")
267
+ }
268
+
269
+ enum Role { USER ADMIN MANAGER }
270
+ ```