@dennisrongo/skills 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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +230 -0
  3. package/bin/claude-skills.js +189 -0
  4. package/lib/install.js +111 -0
  5. package/lib/list.js +63 -0
  6. package/lib/paths.js +39 -0
  7. package/lib/remove.js +52 -0
  8. package/lib/skills.js +121 -0
  9. package/package.json +48 -0
  10. package/skills/_template/SKILL.md +34 -0
  11. package/skills/conventional-commits/SKILL.md +136 -0
  12. package/skills/diagnose/SKILL.md +140 -0
  13. package/skills/dotnet-onion-api/SKILL.md +267 -0
  14. package/skills/dotnet-onion-api/references/anti-patterns.md +155 -0
  15. package/skills/dotnet-onion-api/references/solution-layout.md +113 -0
  16. package/skills/dotnet-onion-api/references/templates/appsettings.md +75 -0
  17. package/skills/dotnet-onion-api/references/templates/base-controller.cs.md +90 -0
  18. package/skills/dotnet-onion-api/references/templates/csproj-files.md +178 -0
  19. package/skills/dotnet-onion-api/references/templates/dbcontext.cs.md +149 -0
  20. package/skills/dotnet-onion-api/references/templates/exception-middleware.cs.md +101 -0
  21. package/skills/dotnet-onion-api/references/templates/feature-slice.md +349 -0
  22. package/skills/dotnet-onion-api/references/templates/program-cs.md +171 -0
  23. package/skills/dotnet-onion-api/references/templates/worker-program.cs.md +166 -0
  24. package/skills/grill-with-docs/SKILL.md +203 -0
  25. package/skills/handoff/SKILL.md +155 -0
  26. package/skills/improve-codebase-architecture/DEEPENING.md +37 -0
  27. package/skills/improve-codebase-architecture/INTERFACE-DESIGN.md +44 -0
  28. package/skills/improve-codebase-architecture/LANGUAGE.md +53 -0
  29. package/skills/improve-codebase-architecture/SKILL.md +121 -0
  30. package/skills/nextjs-app-router/SKILL.md +328 -0
  31. package/skills/nextjs-app-router/references/anti-patterns.md +203 -0
  32. package/skills/nextjs-app-router/references/folder-layout.md +151 -0
  33. package/skills/nextjs-app-router/references/good-patterns.md +212 -0
  34. package/skills/nextjs-app-router/references/templates/api-base.md +62 -0
  35. package/skills/nextjs-app-router/references/templates/api-slice.md +75 -0
  36. package/skills/nextjs-app-router/references/templates/auth-slice.md +95 -0
  37. package/skills/nextjs-app-router/references/templates/ci-and-hooks.md +94 -0
  38. package/skills/nextjs-app-router/references/templates/components-json.md +41 -0
  39. package/skills/nextjs-app-router/references/templates/env-and-utils.md +110 -0
  40. package/skills/nextjs-app-router/references/templates/eslint-prettier.md +82 -0
  41. package/skills/nextjs-app-router/references/templates/feature-slice.md +184 -0
  42. package/skills/nextjs-app-router/references/templates/form-with-zod.md +192 -0
  43. package/skills/nextjs-app-router/references/templates/middleware.md +88 -0
  44. package/skills/nextjs-app-router/references/templates/next-config.md +42 -0
  45. package/skills/nextjs-app-router/references/templates/package.md +99 -0
  46. package/skills/nextjs-app-router/references/templates/providers-and-store.md +79 -0
  47. package/skills/nextjs-app-router/references/templates/root-layout.md +146 -0
  48. package/skills/nextjs-app-router/references/templates/route-group-layouts.md +90 -0
  49. package/skills/nextjs-app-router/references/templates/tailwind-config.md +89 -0
  50. package/skills/nextjs-app-router/references/templates/testing.md +137 -0
  51. package/skills/nextjs-app-router/references/templates/tsconfig.md +40 -0
  52. package/skills/plan-and-build/SKILL.md +214 -0
  53. package/skills/pr-review/SKILL.md +132 -0
  54. package/skills/write-a-skill/SKILL.md +191 -0
@@ -0,0 +1,328 @@
1
+ ---
2
+ name: nextjs-app-router
3
+ description: Scaffold a new Next.js (App Router) frontend with TypeScript, Redux Toolkit + RTK Query, Tailwind + shadcn/ui (Radix), React Hook Form + Zod, and a curated set of conventions. Use this skill whenever the user asks to "create a new Next.js project", "scaffold a frontend", "new React app", "Next.js app router project", "RTK Query frontend", "shadcn project", or mentions "the frontend patterns I like" / "my Next.js conventions". Three modes — (1) full project scaffold, (2) add a feature slice (route group + page + RTK Query endpoints + form), (3) add an RTK Query API slice for an existing domain. Codifies the good patterns (route groups, injected RTK endpoints, schema-driven forms, per-feature `_components`/`_hooks`, server-side auth middleware) and forbids the common pitfalls (`'use client'` on root pages, `router.push` in `useEffect` for redirects, `serializableCheck: false`, `@ts-ignore`, mixed date libraries, `dangerouslySetInnerHTML` without sanitization, case-sensitive folder dupes, missing tests/CI/middleware).
4
+ ---
5
+
6
+ # Next.js App Router Frontend Scaffolder
7
+
8
+ Generate a production-grade Next.js frontend project that keeps the **good** modern-stack patterns (App Router with route-group auth boundaries, single RTK Query API with injected endpoints, shadcn/ui on Tailwind + Radix, React Hook Form + Zod with schema-introspected defaults, per-feature `_components`/`_hooks` co-location, typed Redux hooks, server-side auth middleware) and eliminates the **bad** ones often seen in real-world Next.js codebases (`'use client'` on layouts and root pages, `router.push` in `useEffect` instead of server `redirect()`, `serializableCheck: false`, `@ts-ignore` in the store, mixed `moment` + `date-fns`, leftover `styled-components` in a Tailwind project, case-sensitive duplicate model folders, `dangerouslySetInnerHTML` without sanitization, sessionStorage for non-transient state, tokens in non-`httpOnly` cookies, no `middleware.ts`, no tests, no error/loading boundaries).
9
+
10
+ ## When to use this skill
11
+
12
+ Trigger on any of:
13
+
14
+ - "create a new Next.js project" / "scaffold a frontend" / "new React app"
15
+ - "Next.js App Router project" / "App Router scaffold"
16
+ - "RTK Query frontend" / "Redux Toolkit Next.js"
17
+ - "shadcn/ui project" / "Tailwind + shadcn + Radix scaffold"
18
+ - "use my frontend patterns" / "the Next.js conventions I like"
19
+ - "add a feature slice end-to-end" (route + RTK Query endpoints + form) in a Next.js context
20
+ - "add a new RTK Query API slice for `<domain>`"
21
+ - The user pastes a feature spec for a screen and asks you to wire it through routing + state + form
22
+
23
+ If unsure whether the user wants a brand-new project vs. an addition to an existing one, ask once — don't guess.
24
+
25
+ ## Three operating modes
26
+
27
+ Pick the mode from the user's request. If ambiguous, ask.
28
+
29
+ | Mode | Trigger | Output |
30
+ |------|---------|--------|
31
+ | **`scaffold-project`** | "new project", "scaffold frontend", empty directory | Full Next.js App Router project: route groups, providers, store, base RTK api, auth slice, middleware, shadcn/ui init, Vitest + Playwright config, ESLint/Prettier/Husky, GitHub Actions CI. |
32
+ | **`add-feature`** | "add `<feature>` end-to-end", "wire up `<screen>` through routing + state + form" | New route folder under the appropriate group with `page.tsx`, `_components/`, `_hooks/`, `loading.tsx`, an RTK Query endpoint file, a Zod schema, and a form component. |
33
+ | **`add-api-slice`** | "add an API slice for `<domain>`", "new RTK Query endpoints for X" | New `src/redux/api/<domain>Api.ts` injecting endpoints into the base `api`, plus its tag types and typed hooks. |
34
+
35
+ ## Workflow
36
+
37
+ ### Step 1 — Determine versions (don't hard-code)
38
+
39
+ The user does **not** want hard-coded package versions baked into the skill. Before writing `package.json`:
40
+
41
+ 1. Check the user's environment first: run `node --version` and `npm --version` (or `pnpm --version` / `yarn --version`) to see what's installed.
42
+ 2. Resolve the latest stable versions of the core stack via context7 — never hand-paste:
43
+ - `next`, `react`, `react-dom`, `typescript`, `@types/react`, `@types/node`
44
+ - `@reduxjs/toolkit`, `react-redux`
45
+ - `tailwindcss`, `postcss`, `autoprefixer`
46
+ - `@radix-ui/*`, `class-variance-authority`, `clsx`, `tailwind-merge`, `tailwindcss-animate`, `lucide-react`
47
+ - `react-hook-form`, `@hookform/resolvers`, `zod`
48
+ - `date-fns` (do **not** also add `moment`)
49
+ - `vitest`, `@testing-library/react`, `@testing-library/jest-dom`, `jsdom`, `@playwright/test`
50
+ - `eslint`, `eslint-config-next`, `@typescript-eslint/*`, `eslint-plugin-react-hooks`, `eslint-plugin-jsx-a11y`, `prettier`, `husky`, `lint-staged`
51
+ 3. Quote the resolved versions back to the user before generating, so they can object.
52
+ 4. Prefer the latest **stable** Next.js (App Router is GA from 13.4 onward; ensure the chosen version is current at scaffold time).
53
+ 5. Default to **pnpm** if `pnpm` is present, otherwise **npm**. Match `packageManager` in `package.json`.
54
+
55
+ ### Step 2 — Gather inputs (ask once, in one batch)
56
+
57
+ For `scaffold-project`, use `AskUserQuestion` to collect:
58
+
59
+ - **Project name** (kebab-case, used for the directory and `package.json` `name`).
60
+ - **Route groups**: which auth/role boundaries does the app need? Default offer: `(public)` for auth pages + `(app)` for authenticated content. Optionally add `(admin)` for role-gated routes.
61
+ - **Auth flavor**: JWT in `httpOnly` cookie (recommended; middleware sets the cookie, server reads it) **or** JWT in JS-accessible cookie + `Authorization: Bearer` header **or** none-yet.
62
+ - **API base URL** env var name (default `NEXT_PUBLIC_API_BASE_URL`) — and whether requests should go through a Next.js rewrite to hide the backend host.
63
+ - **Real-time** (Pusher / WebSocket / SSE / none).
64
+ - **Optional extras**: Storybook? i18n? Sentry? Dockerfile? GitHub Actions CI? PM2 ecosystem file? (Default: skip — only add if asked. CI defaults ON.)
65
+ - **First feature/route** (optional, e.g. `dashboard`) — if provided, also run `add-feature` for it after scaffold.
66
+
67
+ For `add-feature`: feature name, route group it belongs to, list of fields (name + Zod type + required/optional), whether it has a list view + detail view or just one screen.
68
+
69
+ For `add-api-slice`: domain name (e.g. `customers`), list of endpoints (verb + path + request type + response type), invalidation tags.
70
+
71
+ ### Step 3 — Generate files
72
+
73
+ Use the **templates in [`references/templates/`](references/templates/)** as the source of truth. Apply these rules:
74
+
75
+ - Use `Write` for new files. Never use `Edit` on files you're creating fresh.
76
+ - Replace all `{{ProjectName}}`, `{{Feature}}`, `{{Domain}}` placeholders consistently. `{{ProjectName}}` is kebab-case for files/dirs and PascalCase only where namespacing requires (e.g. tag-type constants).
77
+ - Create directories before files. On Windows shell use PowerShell `New-Item -ItemType Directory -Force`.
78
+ - Do **not** run `create-next-app` to bootstrap — write the files directly from templates so the layout matches the conventions in [`references/folder-layout.md`](references/folder-layout.md). Use `npm`/`pnpm install` after the `package.json` is written.
79
+ - Initialize shadcn/ui by writing `components.json` from [`references/templates/components.json.md`](references/templates/components.json.md) and `src/components/ui/` components incrementally as needed (button, input, form, label, dialog, toast, etc.) — do not blanket-install every Radix primitive up front.
80
+
81
+ ### Step 4 — Verify and report
82
+
83
+ - Run the package manager install (`pnpm install` / `npm install`).
84
+ - Run `pnpm typecheck` (or `npx tsc --noEmit`). Must exit 0.
85
+ - Run `pnpm lint`. Must exit 0.
86
+ - Run `pnpm test` (Vitest) if any tests were generated. Must exit 0.
87
+ - Run `pnpm build`. Must succeed (this catches client/server boundary errors that `tsc` misses).
88
+ - Reply with a short summary: project path, versions chosen, route groups, env vars to set, next steps (e.g. "set `NEXT_PUBLIC_API_BASE_URL` in `.env.local`", "run `pnpm dev`").
89
+
90
+ ## Project layout (canonical)
91
+
92
+ See [`references/folder-layout.md`](references/folder-layout.md) for the full tree, file-by-file purpose, and the rules that govern it.
93
+
94
+ Top-level shape:
95
+
96
+ ```
97
+ {{project-name}}/
98
+ package.json
99
+ tsconfig.json # strict: true, paths: { "@/*": ["./src/*"] }
100
+ next.config.ts # rewrites (if any), images, experimental flags
101
+ tailwind.config.ts # darkMode: ['class'], shadcn theme tokens
102
+ postcss.config.js
103
+ components.json # shadcn config; rsc: true
104
+ .eslintrc.json (or eslint.config.mjs)
105
+ .prettierrc
106
+ .env.example # NEVER .env — only .env.example committed
107
+ middleware.ts # AUTH GUARD lives here (not in src/proxy.ts)
108
+ src/
109
+ app/
110
+ layout.tsx # server component; <html><body><Providers>
111
+ page.tsx # server component; redirect() — NOT 'use client' + router.push
112
+ globals.css
113
+ error.tsx # global error boundary
114
+ not-found.tsx
115
+ (public)/ # unauthenticated routes (login, signup, reset)
116
+ layout.tsx
117
+ auth/
118
+ (app)/ # authenticated routes
119
+ layout.tsx # server component; reads session, renders shell
120
+ _components/ # private; only nested routes can import
121
+ _hooks/
122
+ loading.tsx
123
+ error.tsx
124
+ <feature>/
125
+ page.tsx
126
+ loading.tsx
127
+ _components/
128
+ _hooks/
129
+ schema.ts # Zod schemas for this feature
130
+ (admin)/ # optional; role-gated
131
+ components/
132
+ ui/ # shadcn primitives (button, input, form, ...)
133
+ forms/ # composed form fields (FormInputField, ...)
134
+ Notifications.tsx
135
+ UnsavedChangesWarning.tsx
136
+ redux/
137
+ store.ts # typed; NO @ts-ignore, NO serializableCheck: false
138
+ providers.tsx # 'use client'; wraps <Provider>, <Toaster>, <ProgressBar>
139
+ hooks.ts # typed useAppDispatch, useAppSelector
140
+ api/
141
+ api.ts # base createApi with injectEndpoints; auth-aware baseQuery
142
+ tags.ts # tag-type union as const
143
+ <domain>Api.ts # one file per domain; injectEndpoints
144
+ features/
145
+ authSlice.ts
146
+ <other>Slice.ts # ONLY for genuinely shared async/global state
147
+ lib/
148
+ utils.ts # cn() — clsx + tailwind-merge
149
+ zod-utils.ts # getDefaultValuesFromSchema, unwrapZodEffects, getMaxLengthsFromSchema
150
+ formatters/
151
+ hooks/
152
+ types/ # cross-cutting TS types (NOT per-domain; those live with their feature)
153
+ config/
154
+ env.ts # runtime-validated env (Zod parsed at startup)
155
+ tests/
156
+ unit/ # Vitest + RTL
157
+ e2e/ # Playwright
158
+ .github/workflows/ci.yml # lint + typecheck + test + build
159
+ ```
160
+
161
+ **Dependency rules (enforced):**
162
+ - `app/` routes import from `components/`, `redux/`, `lib/`, `config/` — never the other way.
163
+ - `redux/features/` may import from `redux/api/` (for endpoint matchers); the reverse is forbidden.
164
+ - `components/ui/` (shadcn primitives) must not import from `app/`, `redux/`, or feature code.
165
+ - Feature `_components/` and `_hooks/` are **private**: only routes inside the same feature folder may import them. If a component is needed by two features, promote it to `src/components/`.
166
+
167
+ ## Required code patterns
168
+
169
+ Full templates are in [`references/templates/`](references/templates/). The full Keep/Eliminate rationale (with the failure modes that motivated each rule) is in [`references/good-patterns.md`](references/good-patterns.md) and [`references/anti-patterns.md`](references/anti-patterns.md).
170
+
171
+ ### Keep
172
+
173
+ - **Server root layout and server root page**. `app/layout.tsx` is a server component that renders `<html><body><Providers>{children}</Providers></body></html>`. `app/page.tsx` is a server component that calls `redirect('/dashboard')` (or whichever default) — never a `'use client'` component that calls `router.push` in `useEffect`. See [`references/templates/root-layout.md`](references/templates/root-layout.md).
174
+ - **Route groups for auth boundaries**: `(public)`, `(app)`, optional `(admin)`. Each group has its own `layout.tsx`. The shell (header/sidebar) lives in `(app)/layout.tsx`.
175
+ - **Server-side auth in `middleware.ts`** at project root. The middleware checks the session cookie and 302s unauthenticated requests to `/auth/login`. Client-side redirects are a fallback for token expiry during a session, not the primary guard. See [`references/templates/middleware.md`](references/templates/middleware.md).
176
+ - **One RTK Query base `api`** in `redux/api/api.ts` with `endpoints: () => ({})`, plus domain slices that call `api.injectEndpoints(...)`. Tag types are a `const` array reused via `tagTypes`. See [`references/templates/api-base.md`](references/templates/api-base.md) and [`references/templates/api-slice.md`](references/templates/api-slice.md).
177
+ - **Auth-aware `baseQuery`** that injects the bearer token from cookies/state and, on a 401, dispatches a clean logout (clears state via the store's root reducer, then routes to `/auth/login`). No hard `window.location.href = ...` from inside the base query.
178
+ - **Typed Redux hooks**: `useAppDispatch`/`useAppSelector` exported from `redux/hooks.ts`. Components never use the raw `useDispatch`/`useSelector`.
179
+ - **Strict store config**: `serializableCheck` left at the default; if a specific path or action genuinely needs an exception, configure `ignoredPaths` / `ignoredActions` explicitly with a comment explaining why. No `@ts-ignore`.
180
+ - **Logout clears state via the root reducer**: a `combinedReducer` is wrapped so that on the logout-fulfilled action, state is reset before delegating to the combined reducer. Pattern is fine; the implementation must be fully typed (no `// @ts-ignore`).
181
+ - **shadcn/ui + Tailwind + Radix**: shadcn primitives in `components/ui/` (generated, owned by the project, not a node_modules dependency). Composed form fields in `components/forms/`. `cn()` util in `lib/utils.ts` is the single source of class merging.
182
+ - **React Hook Form + Zod**: every form uses `useForm({ resolver: zodResolver(schema), defaultValues: getDefaultValuesFromSchema(unwrapZodEffects(schema)) })`. Zod is the source of truth for defaults, max lengths, and validation. See [`references/templates/form-with-zod.md`](references/templates/form-with-zod.md).
183
+ - **`UnsavedChangesWarning`** component — wired to React Hook Form's `formState.isDirty`, intercepts in-app and `beforeunload` navigation.
184
+ - **Per-feature `_components/` and `_hooks/`** folders, private to that route.
185
+ - **`error.tsx`, `loading.tsx`, `not-found.tsx` at every meaningful route segment**. The `(app)` group must have a `loading.tsx` and `error.tsx` minimum.
186
+ - **Runtime env validation** in `src/config/env.ts` using Zod, parsed at module load — fail fast on misconfiguration.
187
+ - **Single date library: `date-fns`**. No `moment`. Format helpers live in `lib/formatters/`.
188
+ - **One typography & color theme via Tailwind tokens + CSS variables** (the shadcn default). Custom colors named with a project prefix (e.g. `brand-primary`).
189
+ - **`tsconfig.json` strict + `@/*` path alias** — see [`references/templates/tsconfig.md`](references/templates/tsconfig.md).
190
+ - **Vitest + RTL for unit tests, Playwright for E2E**, configured but lightweight by default. At least one smoke test per layer (a reducer test, a component test, an E2E auth flow) so CI has something real to run.
191
+ - **Husky + lint-staged** pre-commit: `prettier --write` + `eslint --fix` + `tsc --noEmit` on staged files.
192
+ - **GitHub Actions CI** running install → lint → typecheck → test → build on every PR.
193
+
194
+ ### Eliminate (anti-patterns)
195
+
196
+ Every one of these is forbidden in generated code. Rationale for each is in [`references/anti-patterns.md`](references/anti-patterns.md).
197
+
198
+ - ❌ `'use client'` on `app/page.tsx`, `app/layout.tsx`, or any route-group `layout.tsx` that doesn't actually need client-only hooks. Layouts should be server components by default; only mark interactive children client.
199
+ - ❌ Redirecting from the root by mounting `'use client'` + `useEffect(() => router.push('/dashboard'))`. Use server `redirect('/dashboard')` from `next/navigation` inside a server `page.tsx`, or do the redirect in `middleware.ts`.
200
+ - ❌ `serializableCheck: false` on the store config without targeted `ignoredPaths`/`ignoredActions` and a comment explaining the exception. Silencing the global check hides real bugs.
201
+ - ❌ `@ts-ignore` / `as any` anywhere in the store, providers, or API layer. If a type is awkward, fix the type; don't suppress the compiler.
202
+ - ❌ Commented-out reducers, endpoints, or imports left in `store.ts`/`api.ts`. Either wire it up or delete it.
203
+ - ❌ Mixing `moment` and `date-fns`. Pick `date-fns` (smaller, tree-shakeable, ESM, immutable). Never add `moment` to a new project.
204
+ - ❌ `styled-components` (or Emotion) in a Tailwind project. The stack is Tailwind + Radix + cva + shadcn — adding a CSS-in-JS runtime is duplicate machinery.
205
+ - ❌ Two folders or files that differ only by case (e.g. `stateProjectReports/` and `stateprojectreport/`). They break on case-sensitive filesystems (Linux CI, Docker) even if they work on Windows/macOS. Enforce one canonical name.
206
+ - ❌ `dangerouslySetInnerHTML` with API-sourced content that hasn't been server-sanitized. If HTML rendering is genuinely needed, route through a sanitizer (DOMPurify on the client, or server-side rendering through a strict allowlist) and add an inline comment naming the sanitization point.
207
+ - ❌ `sessionStorage` / `localStorage` for state that isn't transient client-only UI state. Page-to-page handoff goes through URL search params or Redux. Tokens never go in `localStorage`.
208
+ - ❌ JWT tokens stored in JS-accessible cookies (`js-cookie` `Cookies.set('token', ...)`) when you can put them in `httpOnly` cookies set by the server / middleware. If a JS-accessible token is unavoidable for the chosen auth flow, document the XSS surface and lock down `dangerouslySetInnerHTML` use accordingly.
209
+ - ❌ A `proxy.ts` (or similarly-named file) at `src/` root that *looks* like middleware but isn't wired through Next.js's `middleware.ts` convention. There is exactly one place auth lives: `middleware.ts` at the project root.
210
+ - ❌ Inline `fetch` calls in components for backend data. All backend calls go through RTK Query (or a typed wrapper if RTK Query is genuinely the wrong fit for that case — but justify it). One way to call the API per project.
211
+ - ❌ Redux slices for state that should be local component state, URL state, or React Hook Form state. Reach for `useState` / `useReducer` first, then `useSearchParams`, then Redux.
212
+ - ❌ Many independent `createApi()` instances. One base `api` + `injectEndpoints` per domain. Multiple reducerPaths fragment the cache.
213
+ - ❌ Hard `window.location.href = '/auth'` redirects from inside the base query as the primary 401 handler. Dispatch a clean logout action, let the router or middleware handle the navigation.
214
+ - ❌ Skipping `loading.tsx` / `error.tsx` / `not-found.tsx` on authenticated route groups. Users hit blank screens during data fetches and silent failures on RTK Query errors.
215
+ - ❌ No tests at all (no Vitest, no Playwright, no `test` script). Even a smoke test gives CI something to enforce; zero tests means every change is a regression risk.
216
+ - ❌ ESLint extending only `next/core-web-vitals`. Add `@typescript-eslint/recommended`, `react-hooks`, `jsx-a11y`, and project rules (`no-console: warn`, `no-explicit-any: error`, `prefer-const: error`, `react-hooks/exhaustive-deps: error`).
217
+ - ❌ Hand-rolled spacing/animation extensions in `tailwind.config.ts` with dozens of commented-out values. Extend Tailwind only for genuine design-system tokens (brand colors, fonts). Use Tailwind's defaults for spacing and timing.
218
+ - ❌ Per-feature client guards (e.g. an `<AuthExpirationHandler>` component) as the only auth check. Server-side `middleware.ts` is the primary guard; client guards are belt-and-suspenders for token expiry mid-session, not the gate.
219
+
220
+ ## Operating-mode playbooks
221
+
222
+ ### Mode 1 — `scaffold-project`
223
+
224
+ 1. Resolve versions per **Step 1**. Quote them.
225
+ 2. Ask the inputs per **Step 2**. Wait for answers.
226
+ 3. Generate, in this order:
227
+ 1. `package.json`, `tsconfig.json`, `next.config.ts`, `tailwind.config.ts`, `postcss.config.js`, `components.json`, `.eslintrc.json` (or `eslint.config.mjs`), `.prettierrc`, `.gitignore`, `.env.example`, `README.md`.
228
+ 2. `middleware.ts` at project root (auth guard).
229
+ 3. `src/app/layout.tsx` (server), `src/app/page.tsx` (server, `redirect()`), `src/app/globals.css`, `src/app/error.tsx`, `src/app/not-found.tsx`.
230
+ 4. Route groups: `src/app/(public)/layout.tsx` + a basic `auth/login/page.tsx`, `src/app/(app)/layout.tsx` + `loading.tsx` + `error.tsx`, plus `(admin)/` if requested.
231
+ 5. `src/redux/store.ts`, `src/redux/providers.tsx`, `src/redux/hooks.ts`.
232
+ 6. `src/redux/api/api.ts` (base), `src/redux/api/tags.ts`, `src/redux/api/authApi.ts`.
233
+ 7. `src/redux/features/authSlice.ts`.
234
+ 8. `src/components/ui/` with the shadcn primitives actually needed by the templates (start with button, input, label, form, dialog, toast — add more on demand).
235
+ 9. `src/components/forms/` composed fields (`FormInputField`, `FormPasswordField`, `FormSelectField`, `FormDatePickerField` — only those needed by generated forms).
236
+ 10. `src/components/UnsavedChangesWarning.tsx`, `src/components/Notifications.tsx`.
237
+ 11. `src/lib/utils.ts` (`cn()`), `src/lib/zod-utils.ts`.
238
+ 12. `src/config/env.ts` (Zod-validated runtime env).
239
+ 13. `tests/unit/` with one reducer test + one component test. `tests/e2e/` with one Playwright smoke spec.
240
+ 14. `vitest.config.ts`, `playwright.config.ts`.
241
+ 15. `.husky/pre-commit`, `lint-staged` config in `package.json`.
242
+ 16. `.github/workflows/ci.yml`.
243
+ 4. `pnpm install` (or chosen PM).
244
+ 5. `pnpm typecheck && pnpm lint && pnpm test && pnpm build` — must all pass.
245
+ 6. If a first feature was requested, immediately run **Mode 2** for that feature.
246
+ 7. Report.
247
+
248
+ ### Mode 2 — `add-feature`
249
+
250
+ For feature `{{Feature}}` in route group `{{Group}}` (e.g. `(app)`):
251
+
252
+ 1. **Routing**:
253
+ - `src/app/{{Group}}/{{feature}}/page.tsx` — server component by default. If the feature needs client interactivity, the page is server and renders a `'use client'` child from `_components/`.
254
+ - `src/app/{{Group}}/{{feature}}/loading.tsx` (Suspense fallback).
255
+ - `src/app/{{Group}}/{{feature}}/error.tsx` (error boundary).
256
+ 2. **Schema**: `src/app/{{Group}}/{{feature}}/schema.ts` — exports `{{feature}}Schema` (Zod) and inferred `{{Feature}}FormValues` type.
257
+ 3. **Private components**: `src/app/{{Group}}/{{feature}}/_components/{{Feature}}Form.tsx` — `'use client'`, uses `useForm` + `zodResolver`, wraps `<UnsavedChangesWarning>`, dispatches RTK Query mutation on submit.
258
+ 4. **Private hooks**: `src/app/{{Group}}/{{feature}}/_hooks/use{{Feature}}.ts` — if there's per-feature derived state.
259
+ 5. **API endpoints**: extend `src/redux/api/{{domain}}Api.ts` (or run Mode 3 first if no slice exists yet) with query + mutation for this feature.
260
+ 6. **Tests**: `tests/unit/{{feature}}.schema.test.ts` (validates the Zod schema with valid + invalid inputs).
261
+
262
+ After generating: `pnpm typecheck && pnpm test && pnpm build`.
263
+
264
+ ### Mode 3 — `add-api-slice`
265
+
266
+ For domain `{{domain}}` (e.g. `customers`):
267
+
268
+ 1. Create `src/redux/api/{{domain}}Api.ts` that imports the base `api` from `./api` and calls `api.injectEndpoints(...)`.
269
+ 2. Each endpoint:
270
+ - Strongly typed request/response (no `any`).
271
+ - `providesTags` on queries, `invalidatesTags` on mutations, both referencing constants from `tags.ts`.
272
+ - `transformResponse` / `transformErrorResponse` only when the API contract demands it; otherwise return raw.
273
+ 3. Add new tag types to `src/redux/api/tags.ts`. The `tagTypes` array on the base `api` reads from this constant.
274
+ 4. Export the generated hooks (`use{{Domain}}GetXQuery`, `use{{Domain}}CreateXMutation`, etc.) from the slice file.
275
+ 5. **Do not** create a new `createApi(...)`. One base `api`, many injected slices.
276
+
277
+ After generating: `pnpm typecheck` must pass.
278
+
279
+ ## Verification checklist before reporting "done"
280
+
281
+ - [ ] `pnpm install` succeeds.
282
+ - [ ] `pnpm typecheck` exits 0.
283
+ - [ ] `pnpm lint` exits 0 (no warnings either, on a fresh scaffold).
284
+ - [ ] `pnpm test` exits 0 (Vitest).
285
+ - [ ] `pnpm build` succeeds (catches RSC boundary errors).
286
+ - [ ] `pnpm exec playwright test --list` lists the smoke spec (don't run the browser in CI without a server unless asked).
287
+ - [ ] No file contains any pattern from the **Eliminate** list — verify by grepping for: `'use client'` in `app/page.tsx` and `app/layout.tsx`, `router.push` inside a `useEffect` body in `app/page.tsx`, `serializableCheck: false`, `@ts-ignore`, `as any`, `import .* from ['"]moment['"]`, `import .* from ['"]styled-components['"]`, `dangerouslySetInnerHTML` (any hit must have a sanitization comment), `Cookies.set\(['"]token['"]` (must be `httpOnly` cookie set by middleware unless documented), inline `fetch(` in `app/**/page.tsx`.
288
+ - [ ] `middleware.ts` exists at project root and is wired (has a `config.matcher` that covers the `(app)` and `(admin)` groups).
289
+ - [ ] `src/app/page.tsx` does **not** contain `'use client'` and does **not** contain `useEffect`. It either renders content or calls `redirect()`.
290
+ - [ ] No duplicate-by-case folders anywhere in `src/`.
291
+ - [ ] `.env` is **not** committed; `.env.example` is.
292
+
293
+ If any check fails, fix before reporting. Don't claim success with a known-broken scaffold.
294
+
295
+ ## Examples
296
+
297
+ ### Example 1: Fresh project
298
+
299
+ **User:** "Scaffold a new Next.js frontend using my App Router patterns. Call it `acme-portal`. Add a `dashboard` feature too."
300
+
301
+ **Claude:**
302
+ 1. Runs `node --version` / `pnpm --version`. Resolves latest stable Next.js, React, RTK, Tailwind, Zod versions via context7.
303
+ 2. Reports chosen versions and waits for confirmation if anything looks off.
304
+ 3. Asks the Step-2 questions (route groups, auth flavor, real-time, optional extras).
305
+ 4. Generates the full project + the `dashboard` feature slice in `(app)/dashboard/`.
306
+ 5. Runs install, typecheck, lint, test, build — all must pass.
307
+ 6. Reports the tree, env vars to set, and `pnpm dev` next step.
308
+
309
+ ### Example 2: Add a feature
310
+
311
+ **User:** "Add a `customers` feature end-to-end under the (app) group — list view + create form."
312
+
313
+ **Claude:** Runs Mode 2. If no `customersApi` exists yet, runs Mode 3 first to create the slice with `getCustomers`, `getCustomer`, `createCustomer`, `updateCustomer` endpoints and a `Customers` tag type. Then generates `(app)/customers/page.tsx` (server, fetches list via the query hook from a `'use client'` child), `(app)/customers/_components/CustomersTable.tsx`, `(app)/customers/new/page.tsx` + `_components/CustomerForm.tsx` with Zod schema. Adds a schema unit test. Builds and tests.
314
+
315
+ ### Example 3: Add an API slice
316
+
317
+ **User:** "Add an RTK Query slice for `invoices` with list, get, create, mark-paid endpoints."
318
+
319
+ **Claude:** Runs Mode 3. Creates `src/redux/api/invoicesApi.ts` injecting four endpoints into the base `api`, adds `'Invoices'` + `'Invoice'` to `tags.ts`, exports the typed hooks. Typechecks.
320
+
321
+ ## Notes
322
+
323
+ - **Don't over-engineer**. Don't add Storybook, i18n, Sentry, MSW, NextAuth, tRPC, or Server Actions wrappers unless the user asks. The default scaffold is intentionally lean.
324
+ - **Don't rewrite the user's existing project** as part of this skill. This skill is for *new* scaffolds (and additive feature/slice modes), not migrations. If the user wants a migration plan, that's a different conversation.
325
+ - **Server vs client**: default to server components. Add `'use client'` only when you actually use `useState`, `useEffect`, `useRouter` (client), event handlers, browser-only APIs, or Redux hooks. The fact that a component is interactive in production doesn't make its parent need `'use client'`.
326
+ - **Auth**: prefer `httpOnly` cookies set by `middleware.ts` (server-readable, JS-unreadable). If the chosen backend forces a JS-readable token, document the XSS exposure and harden `dangerouslySetInnerHTML` usage.
327
+ - **Versions**: always quote the resolved package versions before writing `package.json`. The user explicitly asked not to hard-code them.
328
+ - **Naming**: kebab-case for files and folders inside `src/`, except React component files which match the component name in PascalCase (`CustomerForm.tsx`). Never have two paths that differ only by case.
@@ -0,0 +1,203 @@
1
+ # Anti-patterns to Eliminate
2
+
3
+ Each entry below explains **what** the pattern looks like in the wild, **why** it's bad, and **what to do instead**. The skill must not emit any of these. If a user explicitly requests one, push back with the rationale before complying.
4
+
5
+ ## 1. `'use client'` on the root page with a `useEffect(() => router.push(...))` redirect
6
+
7
+ ```tsx
8
+ // app/page.tsx — BAD
9
+ 'use client';
10
+ import { useRouter } from 'next/navigation';
11
+ import { useEffect } from 'react';
12
+ export default function Home() {
13
+ const router = useRouter();
14
+ useEffect(() => { router.push('/dashboard'); });
15
+ }
16
+ ```
17
+
18
+ **Why bad:**
19
+ - The page renders blank HTML on first load (no SSR content), so Open Graph crawlers, SEO, and link-preview tools see nothing.
20
+ - Causes a visible flash (empty page → JS hydrate → navigate).
21
+ - Costs an extra client navigation that could have been a 308 from the server.
22
+ - Defeats Next.js's built-in `redirect()` machinery.
23
+
24
+ **Do instead:**
25
+
26
+ ```tsx
27
+ // app/page.tsx — GOOD
28
+ import { redirect } from 'next/navigation';
29
+ export default function Home() {
30
+ redirect('/dashboard');
31
+ }
32
+ ```
33
+
34
+ Or handle it in `middleware.ts` if the redirect target depends on auth/session.
35
+
36
+ ## 2. `'use client'` on `app/layout.tsx` or route-group `layout.tsx`
37
+
38
+ Marking a layout `'use client'` opts the entire subtree into client rendering and disables streaming, server data fetches, and `metadata` exports from descendants. Layouts should stay server components; push the client-only chrome (`AppShell`, `Sidebar`) into a `'use client'` child component imported from `_components/`.
39
+
40
+ ## 3. `serializableCheck: false` (or `immutableCheck: false`)
41
+
42
+ ```ts
43
+ // store.ts — BAD
44
+ middleware: (getDefaultMiddleware) =>
45
+ getDefaultMiddleware({ serializableCheck: false }).concat(api.middleware),
46
+ ```
47
+
48
+ **Why bad:** silences a class of real bugs (Dates, Maps, class instances, callbacks accidentally placed in actions or state). Once off, you stop noticing the next non-serializable value that slips in.
49
+
50
+ **Do instead:** keep the default. If one specific path or action genuinely holds a non-serializable value, scope the exception:
51
+
52
+ ```ts
53
+ serializableCheck: {
54
+ ignoredPaths: ['auth.expiresAt'],
55
+ ignoredActions: ['someApi/executeMutation/fulfilled'],
56
+ },
57
+ ```
58
+
59
+ …and leave a comment naming what's stored there and why it can't be a plain value.
60
+
61
+ ## 4. `@ts-ignore` / `as any` in the store, providers, or API layer
62
+
63
+ These are the load-bearing files of the entire app. If TypeScript is unhappy with them, the types are wrong — fix them. Use `satisfies`, narrow the inferred type, or extract a typed helper. `@ts-ignore` here hides real type breakage from refactors.
64
+
65
+ ## 5. Commented-out reducers, endpoints, imports
66
+
67
+ `store.ts` that imports `authApi` and `dashboardApi` but commented out the lines that register their reducers in `combineReducers` is a half-finished refactor pretending to be code. Either wire it up or delete it. The git history is the right place for "we used to do this."
68
+
69
+ ## 6. `moment` alongside `date-fns`
70
+
71
+ `moment` is in maintenance mode, mutable, and ships ~300 KB minified. `date-fns` is tree-shakeable, immutable, and ESM. Mixing them means two date libraries to learn, two timezone behaviors to reconcile, and two bundles in the user's tab. Pick `date-fns` for new projects.
72
+
73
+ ## 7. `styled-components` (or Emotion) in a Tailwind project
74
+
75
+ The stack is Tailwind utilities + Radix primitives + `class-variance-authority` for variants + `cn()` for merging. Adding a CSS-in-JS runtime gives you two styling systems, two specificity stories, and a hydration cost. If a `styled-components` import sneaks in (often from copied legacy code), remove it.
76
+
77
+ ## 8. Duplicate-by-case folders or files
78
+
79
+ `src/models/stateProjectReports/` and `src/models/stateprojectreport/` work on Windows (case-insensitive) and break on Linux (case-sensitive). Imports resolve to one or the other depending on which the bundler sees first, producing "works on my machine" bugs that surface only in CI or production. Enforce one canonical casing.
80
+
81
+ ## 9. `dangerouslySetInnerHTML` without server-side sanitization
82
+
83
+ ```tsx
84
+ <div dangerouslySetInnerHTML={{ __html: apiResponse.body }} />
85
+ ```
86
+
87
+ If `apiResponse.body` is anything other than provably safe (e.g., a static template you control), this is an XSS vector. The fix is layered:
88
+
89
+ 1. Prefer rendering as JSX / Markdown via a strict renderer (no raw HTML).
90
+ 2. If raw HTML is genuinely required, sanitize at the source — server-side allowlist, or DOMPurify on the client with a configured profile.
91
+ 3. Add an inline comment naming the sanitization point so the next reviewer knows it isn't accidental.
92
+
93
+ A scaffolded project should not contain any `dangerouslySetInnerHTML` calls; if a feature genuinely needs one, add it with the comment above.
94
+
95
+ ## 10. `sessionStorage` / `localStorage` for non-transient state
96
+
97
+ `sessionStorage.setItem('wizardStep', JSON.stringify(state))` is a shortcut that almost always grows into a bug:
98
+ - Not SSR-safe (`ReferenceError: sessionStorage is not defined`).
99
+ - Survives tab refreshes but not new tabs — confusing for users.
100
+ - Easy to forget to clear, leading to stale state in later sessions.
101
+
102
+ **Do instead:**
103
+ - Multi-step wizard state → URL search params (`useSearchParams`) so it's shareable and back-button-safe, or Redux if it must be shared across routes.
104
+ - Token persistence → `httpOnly` cookies (server-set), never `localStorage`.
105
+ - Truly transient UI state → component state.
106
+
107
+ ## 11. JWT in a JS-accessible cookie or `localStorage`
108
+
109
+ A token in `js-cookie` or `localStorage` is reachable from any XSS payload that lands on the page. The mitigation is `httpOnly` cookies (the browser sends them with requests, but JS cannot read them):
110
+
111
+ - The login endpoint should set the cookie via a server response header.
112
+ - `middleware.ts` reads the cookie on the server to gate routes.
113
+ - RTK Query sends the cookie automatically via `credentials: 'include'` on `fetch`.
114
+
115
+ If the chosen backend cannot set cookies and forces a `Authorization: Bearer` flow with a client-readable token, document the XSS surface in the project README and treat every `dangerouslySetInnerHTML`, `eval`, or untrusted third-party script as a P1 risk.
116
+
117
+ ## 12. A `src/proxy.ts` (or equivalent) standing in for `middleware.ts`
118
+
119
+ Next.js has exactly one place that intercepts requests at the network boundary: `middleware.ts` at the project root. A file named `src/proxy.ts` that imports `next/server` and exports a function but isn't wired into the framework is dead code that *looks* like a guard. Either move its logic into `middleware.ts` or delete it.
120
+
121
+ ## 13. Inline `fetch` in components
122
+
123
+ ```tsx
124
+ useEffect(() => {
125
+ fetch('/api/customers').then(r => r.json()).then(setData);
126
+ }, []);
127
+ ```
128
+
129
+ This bypasses the cache, retries, error handling, devtools integration, and tag-based invalidation that RTK Query gives you for free. It also produces inconsistent loading/error UX across the app. All backend calls go through RTK Query. If a single specific case genuinely needs raw `fetch` (e.g., streaming a file download), justify it in a comment.
130
+
131
+ ## 14. Redux for local / URL / form state
132
+
133
+ If a piece of state is:
134
+ - Only read inside one component → `useState` / `useReducer`.
135
+ - Shareable via URL (filters, page numbers, selected tab) → `useSearchParams`.
136
+ - Form values → React Hook Form's internal state.
137
+
138
+ Reach for Redux only when state is genuinely shared across routes/components and isn't already covered by an RTK Query cache. `directoryResultsSlice`-style slices that hold the last list-view filter set are usually a smell.
139
+
140
+ ## 15. Many independent `createApi(...)` instances
141
+
142
+ ```ts
143
+ export const authApi = createApi({ reducerPath: 'auth', ... });
144
+ export const customersApi = createApi({ reducerPath: 'customers', ... });
145
+ export const invoicesApi = createApi({ reducerPath: 'invoices', ... });
146
+ ```
147
+
148
+ Each instance has its own reducer path, middleware, cache, and tag-type set. They cannot invalidate each other's queries by tag. The right pattern is one base `api` with `injectEndpoints` per domain.
149
+
150
+ ## 16. `window.location.href = '/auth/login'` from inside `baseQuery` for 401
151
+
152
+ It works, but:
153
+ - Loses unsaved form state.
154
+ - Forces a full page reload (re-downloads JS, re-hydrates Redux).
155
+ - Race conditions when multiple in-flight requests all hit 401 at once.
156
+
157
+ **Do instead:** dispatch a `logout` action; let the root reducer reset state cleanly; let `middleware.ts` (which now sees no session) handle the redirect on the next request, or call `router.replace('/auth/login')` from a single coordinator. Keep the hard `window.location` redirect only as a last-resort fallback.
158
+
159
+ ## 17. Missing `error.tsx` / `loading.tsx` / `not-found.tsx`
160
+
161
+ The `(app)` group needs at minimum:
162
+ - `loading.tsx` — shown by Suspense while server components fetch.
163
+ - `error.tsx` — caught for runtime errors and RTK Query failures bubbling through `unwrap()`.
164
+ - `not-found.tsx` — for 404s from `notFound()` calls and unmatched routes.
165
+
166
+ Without these, the user sees a blank screen or the global error boundary, both of which are worse UX than a typed empty state.
167
+
168
+ ## 18. No tests at all
169
+
170
+ A `package.json` with no `test` script tells you nobody runs anything before merging. Even a single reducer test, a single form-renders test, and a single Playwright login spec give CI something to enforce and break loudly when something regresses. The bar is "smoke tests exist," not "100% coverage."
171
+
172
+ ## 19. ESLint extending only `next/core-web-vitals`
173
+
174
+ The Next preset is good but minimal. Add at least:
175
+ - `@typescript-eslint/recommended` (or `recommended-type-checked` if your CI has the budget).
176
+ - `plugin:react-hooks/recommended` — catches the missing-deps and Rules-of-Hooks bugs that `next/core-web-vitals` does not.
177
+ - `plugin:jsx-a11y/recommended` — accessibility lint.
178
+ - Project rules: `no-console: ['warn', { allow: ['warn', 'error'] }]`, `@typescript-eslint/no-explicit-any: 'error'`, `react-hooks/exhaustive-deps: 'error'`, `prefer-const: 'error'`.
179
+
180
+ ## 20. Over-extended Tailwind config
181
+
182
+ `tailwind.config.ts` that adds 50+ custom spacing tokens, half of them commented out, and three custom animation timing functions, is design-system debt waiting to bite. Extend Tailwind for:
183
+ - Brand colors (named with a project prefix).
184
+ - Custom fonts.
185
+ - Genuine design tokens you reuse.
186
+
187
+ Use the defaults for everything else.
188
+
189
+ ## 21. Per-feature client guards as the only auth check
190
+
191
+ A `<AuthExpirationHandler>` that the app shell mounts and that runs `if (!token) router.push('/auth/login')` on an interval is a belt; it is not the only line of defense. The primary auth check is `middleware.ts`, server-side, before the route renders. Client guards exist for session expiry while the user is already inside the app.
192
+
193
+ ## 22. PM2 / `ecosystem.config.js` without considering simpler hosting
194
+
195
+ If the target deploy is Vercel / Cloudflare / Netlify / any container orchestrator, the PM2 process file is unused weight. Add it only when the user explicitly asks for a VM-style `node`/`next start` deployment.
196
+
197
+ ## 23. Hand-rolled custom `proxy.ts` config when Next.js rewrites would do
198
+
199
+ If the goal is "the frontend calls `/api/...` and Next.js forwards to a backend," use `next.config.ts` `rewrites()` — not a custom request-interception file.
200
+
201
+ ## 24. Mixing client navigation libraries
202
+
203
+ `next-nprogress-bar` is fine for the top loading bar; don't also pull in `nprogress` or a second router-event listener. Pick one progress UI.