nodejs-quickstart-structure 2.1.2 → 2.2.1

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 (70) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +12 -17
  3. package/bin/index.js +1 -0
  4. package/lib/generator.js +1 -1
  5. package/lib/modules/app-setup.js +16 -0
  6. package/lib/modules/auth-setup.js +46 -4
  7. package/lib/prompts.js +49 -5
  8. package/package.json +1 -1
  9. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +12 -2
  10. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +27 -0
  11. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +24 -0
  12. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +5 -1
  13. package/templates/clean-architecture/ts/src/config/env.ts.ejs +12 -2
  14. package/templates/clean-architecture/ts/src/domain/user.ts.ejs +14 -0
  15. package/templates/clean-architecture/ts/src/index.ts.ejs +2 -0
  16. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +24 -0
  17. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +43 -45
  18. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +5 -5
  19. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +1 -0
  20. package/templates/common/.env.example.ejs +10 -0
  21. package/templates/common/README.md.ejs +65 -14
  22. package/templates/common/auth/js/controllers/authController.js.ejs +356 -13
  23. package/templates/common/auth/js/controllers/authController.spec.js.ejs +329 -53
  24. package/templates/common/auth/js/middleware/authMiddleware.js.ejs +10 -6
  25. package/templates/common/auth/js/routes/authRoutes.js.ejs +11 -0
  26. package/templates/common/auth/js/services/jwtService.spec.js.ejs +30 -0
  27. package/templates/common/auth/js/services/socialAuthService.js.ejs +175 -0
  28. package/templates/common/auth/js/services/socialAuthService.spec.js.ejs +192 -0
  29. package/templates/common/auth/js/usecases/SocialLoginUseCase.js.ejs +114 -0
  30. package/templates/common/auth/js/usecases/SocialLoginUseCase.spec.js.ejs +143 -0
  31. package/templates/common/auth/ts/controllers/authController.spec.ts.ejs +366 -64
  32. package/templates/common/auth/ts/controllers/authController.ts.ejs +370 -9
  33. package/templates/common/auth/ts/middleware/authMiddleware.ts.ejs +10 -6
  34. package/templates/common/auth/ts/routes/authRoutes.ts.ejs +11 -0
  35. package/templates/common/auth/ts/services/jwtService.spec.ts.ejs +18 -0
  36. package/templates/common/auth/ts/services/jwtService.ts.ejs +3 -3
  37. package/templates/common/auth/ts/services/socialAuthService.spec.ts.ejs +187 -0
  38. package/templates/common/auth/ts/services/socialAuthService.ts.ejs +189 -0
  39. package/templates/common/auth/ts/usecases/SocialLoginUseCase.spec.ts.ejs +143 -0
  40. package/templates/common/auth/ts/usecases/SocialLoginUseCase.ts.ejs +117 -0
  41. package/templates/common/database/js/models/User.js.ejs +13 -5
  42. package/templates/common/database/js/models/User.js.mongoose.ejs +15 -1
  43. package/templates/common/database/ts/models/User.ts.ejs +23 -7
  44. package/templates/common/database/ts/models/User.ts.mongoose.ejs +18 -2
  45. package/templates/common/docker-compose.yml.ejs +21 -0
  46. package/templates/common/ecosystem.config.js.ejs +10 -0
  47. package/templates/common/eslint.config.mjs.ejs +4 -1
  48. package/templates/common/jest.config.js.ejs +1 -1
  49. package/templates/common/kafka/ts/services/kafkaService.ts.ejs +1 -1
  50. package/templates/common/package.json.ejs +4 -0
  51. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +13 -1
  52. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +13 -1
  53. package/templates/common/swagger.yml.ejs +62 -3
  54. package/templates/common/views/ejs/login.ejs.ejs +84 -0
  55. package/templates/common/views/ejs/signup.ejs.ejs +84 -0
  56. package/templates/common/views/pug/login.pug.ejs +78 -0
  57. package/templates/common/views/pug/signup.pug.ejs +78 -0
  58. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +3 -1
  59. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +3 -1
  60. package/templates/mvc/js/src/config/env.js.ejs +12 -2
  61. package/templates/mvc/js/src/controllers/userController.js.ejs +1 -1
  62. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +4 -3
  63. package/templates/mvc/js/src/index.js.ejs +2 -0
  64. package/templates/mvc/js/src/utils/httpCodes.js +1 -0
  65. package/templates/mvc/ts/src/config/env.ts.ejs +12 -2
  66. package/templates/mvc/ts/src/controllers/userController.ts.ejs +1 -0
  67. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +5 -5
  68. package/templates/mvc/ts/src/index.ts.ejs +4 -1
  69. package/templates/mvc/ts/src/utils/httpCodes.ts +1 -0
  70. package/templates/clean-architecture/ts/src/domain/user.ts +0 -9
@@ -3,87 +3,74 @@ import UserModel from '@/infrastructure/database/models/User';
3
3
 
4
4
  export class UserRepository {
5
5
  async save(user: UserEntity): Promise<UserEntity> {
6
+ const userData = {
7
+ name: user.name,
8
+ email: user.email,
9
+ <% if (auth.includes('JWT')) { %>password: user.password,<% } %>
10
+ <% if (typeof socialAuth !== 'undefined' && socialAuth && socialAuth.includes('Google')) { %>googleId: user.googleId,<% } %>
11
+ <% if (typeof socialAuth !== 'undefined' && socialAuth && socialAuth.includes('GitHub')) { %>githubId: user.githubId,<% } %>
12
+ };
13
+
6
14
  <%_ if (database === 'MongoDB') { -%>
7
- const newUser = await UserModel.create({
8
- name: user.name,
9
- email: user.email,
10
- <% if (auth.includes('JWT')) { %>password: user.password<% } %>
11
- });
12
- return { id: newUser._id.toString(), name: newUser.name, email: newUser.email };
15
+ const newUser = await UserModel.create(userData);
16
+ return this.mapToEntity(newUser);
13
17
  <%_ } else if (database === 'None') { -%>
14
- const newUser = await UserModel.create({
15
- name: user.name,
16
- email: user.email,
17
- <% if (auth.includes('JWT')) { %>password: user.password<% } %>
18
- });
19
- return { id: newUser.id, name: newUser.name, email: newUser.email };
18
+ const newUser = await UserModel.create(userData);
19
+ return this.mapToEntity(newUser);
20
20
  <%_ } else { -%>
21
- const newUser = await UserModel.create({
22
- name: user.name,
23
- email: user.email,
24
- <% if (auth.includes('JWT')) { %>password: user.password<% } %>
25
- });
26
- return { id: newUser.id, name: newUser.name, email: newUser.email };
21
+ const newUser = await UserModel.create(userData);
22
+ return this.mapToEntity(newUser);
27
23
  <%_ } -%>
28
24
  }
29
25
 
30
26
  async findById(id: number | string): Promise<UserEntity | null> {
31
27
  <%_ if (database === 'MongoDB') { -%>
32
28
  const user = await UserModel.findById(id);
33
- if (!user) return null;
34
- return { id: user._id.toString(), name: user.name, email: user.email };
35
- <%_ } else if (database === 'None') { -%>
36
- const user = await UserModel.findByPk(id);
37
- if (!user) return null;
38
- return { id: user.id, name: user.name, email: user.email };
39
29
  <%_ } else { -%>
40
30
  const user = await UserModel.findByPk(id);
41
- if (!user) return null;
42
- return { id: user.id, name: user.name, email: user.email };
43
31
  <%_ } -%>
32
+ if (!user) return null;
33
+ return this.mapToEntity(user);
44
34
  }
45
35
 
36
+ async findByEmail(email: string): Promise<UserEntity | null> {
37
+ <%_ if (database === 'MongoDB') { -%>
38
+ const user = await UserModel.findOne({ email });
39
+ <%_ } else if (database === 'None') { -%>
40
+ const user = await UserModel.findOne({ email });
41
+ <%_ } else { -%>
42
+ const user = await UserModel.findOne({ where: { email } });
43
+ <%_ } -%>
44
+ if (!user) return null;
45
+ return this.mapToEntity(user);
46
+ }
46
47
 
47
48
  async getUsers(): Promise<UserEntity[]> {
48
49
  <%_ if (database === 'MongoDB') { -%>
49
50
  const users = await UserModel.find();
50
- return users.map(user => ({
51
- id: user._id.toString(),
52
- name: user.name,
53
- email: user.email
54
- }));
55
51
  <%_ } else if (database === 'None') { -%>
56
52
  const users = await UserModel.find();
57
- return users.map(user => ({
58
- id: user.id,
59
- name: user.name,
60
- email: user.email
61
- }));
62
53
  <%_ } else { -%>
63
54
  const users = await UserModel.findAll();
64
- return users.map(user => ({
65
- id: user.id,
66
- name: user.name,
67
- email: user.email
68
- }));
69
55
  <%_ } -%>
56
+ return users.map(user => this.mapToEntity(user));
70
57
  }
71
58
 
72
59
  async update(id: number | string, data: Partial<UserEntity>): Promise<UserEntity | null> {
73
60
  <%_ if (database === 'MongoDB') { -%>
74
61
  const user = await UserModel.findByIdAndUpdate(id, data, { new: true });
75
62
  if (!user) return null;
76
- return { id: user._id.toString(), name: user.name, email: user.email };
63
+ return this.mapToEntity(user);
77
64
  <%_ } else if (database === 'None') { -%>
78
65
  const { id: _, ...updateData } = data;
79
- const user = await UserModel.update(id, updateData as Parameters<typeof UserModel.update>[1]);
66
+ const user = await UserModel.update(id, updateData as Record<string, unknown>);
80
67
  if (!user) return null;
81
- return { id: user.id, name: user.name, email: user.email };
68
+ return this.mapToEntity(user);
82
69
  <%_ } else { -%>
83
70
  const user = await UserModel.findByPk(id);
84
71
  if (!user) return null;
85
72
  await user.update(data);
86
- return { id: user.id || 0, name: user.name, email: user.email };
73
+ return this.mapToEntity(user);
87
74
  <%_ } -%>
88
75
  }
89
76
 
@@ -100,4 +87,15 @@ export class UserRepository {
100
87
  return true;
101
88
  <%_ } -%>
102
89
  }
90
+
91
+ private mapToEntity(user: { id?: string | number; _id?: { toString(): string }; name: string; email: string; password?: string | null; googleId?: string | null; githubId?: string | null }): UserEntity {
92
+ return {
93
+ id: user.id || user._id?.toString(),
94
+ name: user.name,
95
+ email: user.email,
96
+ password: user.password,
97
+ <% if (typeof socialAuth !== 'undefined' && socialAuth && socialAuth.includes('Google')) { %>googleId: user.googleId,<% } %>
98
+ <% if (typeof socialAuth !== 'undefined' && socialAuth && socialAuth.includes('GitHub')) { %>githubId: user.githubId,<% } %>
99
+ } as UserEntity;
100
+ }
103
101
  }
@@ -25,15 +25,15 @@ export const userResolvers = {
25
25
  const user = await userController.updateUser(id, { name, email });
26
26
  return user;
27
27
  },
28
- deleteUser: async (_: unknown, { id }: { id: string }, <% if (auth.includes('JWT')) { %>context<% } else { %>_context<% } %>: MyContext) => {
29
- <%_ if (auth.includes('JWT')) { -%>
28
+ deleteUser: async (_: unknown, { id }: { id: string }, <%_ if (auth.includes('JWT')) { _%>context<%_ } else { _%>_context<%_ } _%>: MyContext) => {
29
+ <%_ if (auth.includes('JWT')) { _%>
30
30
  if (!context.user) throw new Error('Unauthorized');
31
- <% } %>
31
+ <%_ } _%>
32
32
  const result = await userController.deleteUser(id);
33
33
  return result;
34
34
  }
35
- }<%_ if (database === 'MongoDB') { -%>,
35
+ }<%_ if (database === 'MongoDB') { _%>,
36
36
  User: {
37
37
  id: (parent: { id?: string; _id?: unknown }) => parent.id || parent._id
38
- }<%_ } %>
38
+ }<%_ } _%>
39
39
  };
@@ -3,6 +3,7 @@ export const HTTP_STATUS = {
3
3
  CREATED: 201,
4
4
  BAD_REQUEST: 400,
5
5
  UNAUTHORIZED: 401,
6
+ FORBIDDEN: 403,
6
7
  NOT_FOUND: 404,
7
8
  INTERNAL_SERVER_ERROR: 500
8
9
  } as const;
@@ -45,4 +45,14 @@ JWT_SECRET=your_jwt_secret_key_here_change_it
45
45
  JWT_REFRESH_SECRET=your_jwt_refresh_secret_key_here_change_it
46
46
  JWT_EXPIRES_IN=15m
47
47
  JWT_REFRESH_EXPIRES_IN=7d
48
+ <%_ if (socialAuth && socialAuth.includes('Google')) { -%>
49
+ GOOGLE_CLIENT_ID=your_google_client_id
50
+ GOOGLE_CLIENT_SECRET=your_google_client_secret
51
+ GOOGLE_CALLBACK_URL=http://localhost:3000/api/auth/google/callback
52
+ <%_ } -%>
53
+ <%_ if (socialAuth && socialAuth.includes('GitHub')) { -%>
54
+ GITHUB_CLIENT_ID=your_github_client_id
55
+ GITHUB_CLIENT_SECRET=your_github_client_secret
56
+ GITHUB_CALLBACK_URL=http://localhost:3000/api/auth/github/callback
57
+ <%_ } -%>
48
58
  <% } -%>
@@ -13,7 +13,7 @@ This project follows a strict **7-Step Production-Ready Process** to ensure qual
13
13
 
14
14
  ---
15
15
 
16
- ## 🚀 7-Step Production-Ready Process
16
+ ## 7-Step Production-Ready Process
17
17
 
18
18
  1. **Initialize Git**: `git init` (Required for Husky hooks and security gates).
19
19
  2. **Install Dependencies**: `npm install`.
@@ -25,7 +25,7 @@ This project follows a strict **7-Step Production-Ready Process** to ensure qual
25
25
 
26
26
  ---
27
27
 
28
- ## 🚀 Key Features
28
+ ## Key Features
29
29
 
30
30
  - **Architecture**: <%= architecture %> (<% if (architecture === 'Clean Architecture') { %>Domain, UseCases, Infrastructure<% } else { %>MVC Pattern<% } %>).
31
31
  - **Database**: <%= database %> <% if (database !== 'None') { %>(via <%= database === 'MongoDB' ? 'Mongoose' : 'Sequelize' %>)<% } %>.
@@ -157,20 +157,71 @@ API is exposed via **REST**.
157
157
  A Swagger UI for API documentation is available at:
158
158
  - **URL**: `http://localhost:3000/api-docs` (Dynamic based on PORT)
159
159
 
160
- ### 🛣️ User Endpoints:
160
+ ### User Endpoints:
161
161
  - `GET /api/users`: List all users.
162
162
  - `GET /api/users/:id`: Get a user by ID.
163
163
  - `POST /api/users`: Create a new user.
164
164
  - `PATCH /api/users/:id`: Partially update a user.
165
165
  - `DELETE /api/users/:id`: Delete a user (Soft Delete).
166
166
  <%_ if (auth.includes('JWT')) { %>
167
- ### 🔐 Auth Endpoints:
167
+ ### Auth Endpoints:
168
168
  - `POST /api/auth/login`: Exchange credentials for a short-lived `accessToken` and a long-lived `refreshToken`.
169
169
  - `POST /api/auth/refresh`: Submit a `refreshToken` to receive a new pair of tokens. (Includes theft-detection logic).
170
170
  - `POST /api/auth/logout`: Revoke (blacklist) the active `accessToken` and delete the `refreshToken`.
171
171
  - `POST /api/users`: Acts as Sign Up when password is provided.
172
+ <%_ if (socialAuth && socialAuth.filter(a => a !== 'None').length > 0) { _%>
173
+ ### Social Authentication Flows
174
+
175
+ This project supports two distinct social authentication flows:
176
+
177
+ #### 1. The Redirection Flow (Best for MVC/Web)
178
+ Standard OAuth2 flow using browser redirects.
179
+ - **Start**: `GET /api/auth/google` (or `/github`)
180
+ - **Callback**: Handled automatically by the backend via `/google/callback`.
181
+ - **Result**: User is logged in and redirected to home; `accessToken` and `refreshToken` are securely saved as **HttpOnly cookies** in the browser.
182
+ - **Callback URL**: `http://localhost:3000/api/auth/google/callback` (Standardized for both MVC and Clean Architecture).
183
+
184
+ > [!TIP]
185
+ > **Testing in Swagger**: The "Execute" button in Swagger UI will show a "Failed to fetch" error for this route because browsers block redirects to external domains (like Google) inside AJAX requests. To test this, simply open `http://localhost:3000/api/auth/google` directly in your browser tab.
186
+
187
+ #### 2. The Exchange Flow (Best for SPAs/Mobile Apps)
188
+ A headless flow where the client provides the OAuth code.
189
+ - **API**: `POST /api/auth/social/exchange`
190
+ - **Body**: `{ "code": "AUTH_CODE", "provider": "Google" }`
191
+ - **Result**: Returns JWT tokens (`accessToken`, `refreshToken`) in the JSON response.
192
+
193
+ ---
194
+ <%_ } _%>
172
195
  *Note: To access protected user endpoints (GET/PATCH/DELETE), include `Authorization: Bearer <your_accessToken>` in the headers.*
173
196
  <% } -%>
197
+ <% if (socialAuth && socialAuth.filter(a => a !== 'None').length > 0) { -%>
198
+ ### Social Authentication Setup
199
+ To use social login, you must configure the following in your `.env`:
200
+
201
+ #### Callback URL Configuration
202
+ For the best practice standardized API structure, you **must** configure the Redirect URIs in your developer portals (Google/GitHub) as follows:
203
+
204
+ | Provider | Redirect URI (Callback URL) |
205
+ | :--- | :--- |
206
+ | **Google** | `http://localhost:3000/api/auth/google/callback` |
207
+ | **GitHub** | `http://localhost:3000/api/auth/github/callback` |
208
+
209
+ > [!IMPORTANT]
210
+ > This standardized path works for both **MVC** and **Clean Architecture** templates.
211
+
212
+ #### <% if (socialAuth.includes('Google')) { %>1. Google Integration<% } %>
213
+ 1. Go to [Google Cloud Console](https://console.cloud.google.com/).
214
+ 2. Create/Select a project and go to **APIs & Services > Credentials**.
215
+ 3. Create an **OAuth client ID** for a **Web application**.
216
+ 4. Add Redirect URI: `http://localhost:3000/api/auth/google/callback`.
217
+ 5. Set `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `GOOGLE_CALLBACK_URL` in `.env`.
218
+
219
+ #### <% if (socialAuth.includes('GitHub')) { %>2. GitHub Integration<% } %>
220
+ 1. Go to [GitHub Developer Settings](https://github.com/settings/developers).
221
+ 2. Register a **New OAuth App**.
222
+ 3. Set Callback URL: `http://localhost:3000/api/auth/github/callback`.
223
+ 4. Set `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, and `GITHUB_CALLBACK_URL` in `.env`.
224
+ <% } -%>
174
225
  <% } -%>
175
226
  <% if (communication === 'Kafka') { -%>
176
227
  ## 📡 Testing Kafka Asynchronous Flow
@@ -212,10 +263,10 @@ curl -X PATCH http://localhost:3000/api/users/1 \
212
263
  ```text
213
264
  [Kafka] Producer: Sent USER_CREATED event for 'kafka@example.com'
214
265
  [Kafka] Consumer: Received USER_CREATED.
215
- [Kafka] Consumer: 📧 Sending welcome email to 'kafka@example.com'... Done!
266
+ [Kafka] Consumer: Sending welcome email to 'kafka@example.com'... Done!
216
267
  ```
217
268
 
218
- ### 🛠️ Kafka Troubleshooting
269
+ ### Kafka Troubleshooting
219
270
  If the connection or events are failing:
220
271
  1. **Check Docker**: Ensure Kafka container is running (`docker ps`).
221
272
  2. **Verify Broker**: `KAFKA_BROKER` in `.env` must match your host/port (standard: 9093).
@@ -224,24 +275,24 @@ If the connection or events are failing:
224
275
  <% } -%>
225
276
 
226
277
  <% if (caching === 'Redis') { -%>
227
- ## Caching
278
+ ## Caching
228
279
  This project uses **Redis** for caching.
229
280
  - **Client**: `ioredis`
230
281
  - **Connection**: Configured via `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD` in `.env`.
231
282
  <% } else if (caching === 'Memory Cache') { -%>
232
- ## Caching
283
+ ## Caching
233
284
  This project uses **Memory Cache** for in-memory caching.
234
285
  - **Client**: `node-cache`
235
286
  <% } -%>
236
287
 
237
- ## 📝 Logging
288
+ ## Logging
238
289
  This project uses **Winston** for structured logging.
239
290
  - **Development**: Logs are printed to the console.
240
291
  - **Production**: Logs are saved to files:
241
292
  - `error.log`: Only error level logs.
242
293
  - `combined.log`: All logs.
243
294
 
244
- ## 🐳 Docker Deployment
295
+ ## Docker Deployment
245
296
  This project uses a **Multi-Stage Dockerfile** for optimized production images.
246
297
 
247
298
  <% if (database !== 'None' || caching === 'Redis' || communication === 'Kafka') { -%>
@@ -288,7 +339,7 @@ docker build -t <%= projectName %> .
288
339
  docker run -p 3000:3000 <%= projectName %>
289
340
  ```
290
341
  <% } -%>
291
- ## 🚀 PM2 Deployment (VPS/EC2)
342
+ ## PM2 Deployment (VPS/EC2)
292
343
  This project is pre-configured for direct deployment to a VPS/EC2 instance using **PM2** (via `ecosystem.config.js`).
293
344
  1. Install dependencies
294
345
  ```bash
@@ -324,17 +375,17 @@ docker-compose down
324
375
  - **Rate Limiting**: Protects against DDoS / Brute-force.
325
376
  - **HPP**: Prevents HTTP Parameter Pollution attacks.
326
377
  <% if (includeSecurity) { %>
327
- ### 🛡️ Enterprise Hardening (Big Tech Standard)
378
+ ### Enterprise Hardening (Big Tech Standard)
328
379
  - **Snyk SCA**: Run `npm run snyk:test` for dependency scanning.
329
380
  - **SonarCloud**: Automated SAST on every Push/PR.
330
381
  - **Digital Guardians**: Recommended Gitleaks integration for secret protection.
331
382
  - **Security Policy**: Standard `SECURITY.md` for vulnerability reporting.
332
383
  <% } %>
333
- ## 🤖 AI-Native Development
384
+ ## AI-Native Development
334
385
 
335
386
  This project is "AI-Ready" out of the box. We have pre-configured industry-leading AI context files to bridge the gap between "Generated Code" and "AI-Assisted Development."
336
387
 
337
388
  - **Magic Defaults**: We've automatically tailored your AI context to focus on **<%= projectName %>** and its specific architectural stack (<%= architecture %>, <%= database %>, etc.).
338
389
  - **Use Cursor?** We've configured **`.cursorrules`** at the root. It enforces project standards (80% coverage, MVC/Clean) directly within the editor.
339
- - *Pro-tip*: You can customize the `Project Goal` placeholder in `.cursorrules` to help the AI understand your specific business logic!
390
+ - *Pro-tip*: You can customize the `Project Goal` placeholder in `.cursorrules` to help the AI understand your specific business logic!
340
391
  - **Use ChatGPT/Gemini/Claude?** Check the **`prompts/`** directory. It contains highly-specialized Agent Skill templates. You can copy-paste these into any LLM to give it a "Senior Developer" understanding of your codebase immediately.