start-vibing 2.0.8 → 2.0.10

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 (139) hide show
  1. package/README.md +177 -176
  2. package/dist/cli.js +38 -11
  3. package/package.json +42 -42
  4. package/template/.claude/CLAUDE.md +174 -152
  5. package/template/.claude/agents/01-orchestration/agent-selector.md +130 -123
  6. package/template/.claude/agents/01-orchestration/checkpoint-manager.md +142 -131
  7. package/template/.claude/agents/01-orchestration/context-manager.md +138 -124
  8. package/template/.claude/agents/01-orchestration/error-recovery.md +182 -176
  9. package/template/.claude/agents/01-orchestration/orchestrator.md +114 -107
  10. package/template/.claude/agents/01-orchestration/parallel-coordinator.md +141 -130
  11. package/template/.claude/agents/01-orchestration/task-decomposer.md +121 -118
  12. package/template/.claude/agents/01-orchestration/workflow-router.md +114 -111
  13. package/template/.claude/agents/02-typescript/bun-runtime-expert.md +197 -180
  14. package/template/.claude/agents/02-typescript/esm-resolver.md +193 -187
  15. package/template/.claude/agents/02-typescript/import-alias-enforcer.md +158 -149
  16. package/template/.claude/agents/02-typescript/ts-generics-helper.md +183 -165
  17. package/template/.claude/agents/02-typescript/ts-migration-helper.md +238 -227
  18. package/template/.claude/agents/02-typescript/ts-strict-checker.md +180 -162
  19. package/template/.claude/agents/02-typescript/ts-types-analyzer.md +199 -185
  20. package/template/.claude/agents/02-typescript/type-definition-writer.md +187 -183
  21. package/template/.claude/agents/02-typescript/zod-schema-designer.md +212 -197
  22. package/template/.claude/agents/02-typescript/zod-validator.md +158 -153
  23. package/template/.claude/agents/03-testing/playwright-assertions.md +265 -255
  24. package/template/.claude/agents/03-testing/playwright-e2e.md +247 -245
  25. package/template/.claude/agents/03-testing/playwright-fixtures.md +234 -240
  26. package/template/.claude/agents/03-testing/playwright-multi-viewport.md +256 -261
  27. package/template/.claude/agents/03-testing/playwright-page-objects.md +247 -247
  28. package/template/.claude/agents/03-testing/test-cleanup-manager.md +248 -256
  29. package/template/.claude/agents/03-testing/test-data-generator.md +254 -266
  30. package/template/.claude/agents/03-testing/tester-integration.md +278 -278
  31. package/template/.claude/agents/03-testing/tester-unit.md +207 -204
  32. package/template/.claude/agents/03-testing/vitest-config.md +287 -289
  33. package/template/.claude/agents/04-docker/container-health.md +255 -239
  34. package/template/.claude/agents/04-docker/deployment-validator.md +225 -217
  35. package/template/.claude/agents/04-docker/docker-compose-designer.md +281 -268
  36. package/template/.claude/agents/04-docker/docker-env-manager.md +235 -228
  37. package/template/.claude/agents/04-docker/docker-multi-stage.md +241 -229
  38. package/template/.claude/agents/04-docker/dockerfile-optimizer.md +208 -204
  39. package/template/.claude/agents/05-database/data-migration.md +0 -293
  40. package/template/.claude/agents/05-database/database-seeder.md +273 -270
  41. package/template/.claude/agents/05-database/mongodb-query-optimizer.md +230 -219
  42. package/template/.claude/agents/05-database/mongoose-aggregation.md +306 -280
  43. package/template/.claude/agents/05-database/mongoose-index-optimizer.md +182 -174
  44. package/template/.claude/agents/05-database/mongoose-schema-designer.md +267 -267
  45. package/template/.claude/agents/06-security/auth-session-validator.md +68 -65
  46. package/template/.claude/agents/06-security/input-sanitizer.md +80 -81
  47. package/template/.claude/agents/06-security/owasp-checker.md +97 -87
  48. package/template/.claude/agents/06-security/permission-auditor.md +100 -95
  49. package/template/.claude/agents/06-security/security-auditor.md +84 -82
  50. package/template/.claude/agents/06-security/sensitive-data-scanner.md +83 -85
  51. package/template/.claude/agents/07-documentation/api-documenter.md +136 -131
  52. package/template/.claude/agents/07-documentation/changelog-manager.md +105 -96
  53. package/template/.claude/agents/07-documentation/documenter.md +76 -73
  54. package/template/.claude/agents/07-documentation/domain-updater.md +81 -74
  55. package/template/.claude/agents/07-documentation/jsdoc-generator.md +114 -114
  56. package/template/.claude/agents/07-documentation/readme-generator.md +135 -132
  57. package/template/.claude/agents/08-git/branch-manager.md +58 -58
  58. package/template/.claude/agents/08-git/commit-manager.md +63 -61
  59. package/template/.claude/agents/08-git/pr-creator.md +76 -72
  60. package/template/.claude/agents/09-quality/code-reviewer.md +71 -64
  61. package/template/.claude/agents/09-quality/quality-checker.md +67 -67
  62. package/template/.claude/agents/10-research/best-practices-finder.md +89 -82
  63. package/template/.claude/agents/10-research/competitor-analyzer.md +106 -96
  64. package/template/.claude/agents/10-research/pattern-researcher.md +93 -86
  65. package/template/.claude/agents/10-research/research-cache-manager.md +76 -75
  66. package/template/.claude/agents/10-research/research-web.md +98 -91
  67. package/template/.claude/agents/10-research/tech-evaluator.md +101 -94
  68. package/template/.claude/agents/11-ui-ux/accessibility-auditor.md +136 -128
  69. package/template/.claude/agents/11-ui-ux/design-system-enforcer.md +125 -116
  70. package/template/.claude/agents/11-ui-ux/skeleton-generator.md +118 -120
  71. package/template/.claude/agents/11-ui-ux/ui-desktop.md +132 -126
  72. package/template/.claude/agents/11-ui-ux/ui-mobile.md +98 -94
  73. package/template/.claude/agents/11-ui-ux/ui-tablet.md +110 -111
  74. package/template/.claude/agents/12-performance/api-latency-analyzer.md +156 -149
  75. package/template/.claude/agents/12-performance/bundle-analyzer.md +113 -107
  76. package/template/.claude/agents/12-performance/memory-leak-detector.md +137 -126
  77. package/template/.claude/agents/12-performance/performance-profiler.md +115 -108
  78. package/template/.claude/agents/12-performance/query-optimizer.md +124 -116
  79. package/template/.claude/agents/12-performance/render-optimizer.md +154 -148
  80. package/template/.claude/agents/13-debugging/build-error-fixer.md +207 -188
  81. package/template/.claude/agents/13-debugging/debugger.md +149 -137
  82. package/template/.claude/agents/13-debugging/error-stack-analyzer.md +141 -131
  83. package/template/.claude/agents/13-debugging/network-debugger.md +208 -185
  84. package/template/.claude/agents/13-debugging/runtime-error-fixer.md +181 -173
  85. package/template/.claude/agents/13-debugging/type-error-resolver.md +185 -173
  86. package/template/.claude/agents/14-validation/final-validator.md +93 -83
  87. package/template/.claude/agents/_backup/analyzer.md +134 -125
  88. package/template/.claude/agents/_backup/code-reviewer.md +279 -272
  89. package/template/.claude/agents/_backup/commit-manager.md +219 -212
  90. package/template/.claude/agents/_backup/debugger.md +280 -271
  91. package/template/.claude/agents/_backup/documenter.md +237 -220
  92. package/template/.claude/agents/_backup/domain-updater.md +197 -194
  93. package/template/.claude/agents/_backup/final-validator.md +169 -164
  94. package/template/.claude/agents/_backup/orchestrator.md +149 -138
  95. package/template/.claude/agents/_backup/performance.md +232 -228
  96. package/template/.claude/agents/_backup/quality-checker.md +240 -240
  97. package/template/.claude/agents/_backup/research.md +315 -299
  98. package/template/.claude/agents/_backup/security-auditor.md +192 -186
  99. package/template/.claude/agents/_backup/tester.md +566 -564
  100. package/template/.claude/agents/_backup/ui-ux-reviewer.md +247 -242
  101. package/template/.claude/commands/feature.md +48 -48
  102. package/template/.claude/config/README.md +30 -30
  103. package/template/.claude/config/mcp-config.json +344 -330
  104. package/template/.claude/config/project-config.json +53 -53
  105. package/template/.claude/config/quality-gates.json +46 -46
  106. package/template/.claude/config/security-rules.json +45 -45
  107. package/template/.claude/config/testing-config.json +164 -168
  108. package/template/.claude/hooks/SETUP.md +126 -126
  109. package/template/.claude/hooks/run-hook.ts +176 -172
  110. package/template/.claude/hooks/stop-validator.ts +825 -353
  111. package/template/.claude/hooks/user-prompt-submit.ts +886 -794
  112. package/template/.claude/scripts/mcp-quick-install.ts +151 -151
  113. package/template/.claude/scripts/setup-mcps.ts +651 -628
  114. package/template/.claude/settings.json +275 -276
  115. package/template/.claude/skills/bun-runtime/SKILL.md +430 -430
  116. package/template/.claude/skills/codebase-knowledge/SKILL.md +145 -145
  117. package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +431 -403
  118. package/template/.claude/skills/codebase-knowledge/domains/mcp-integration.md +295 -281
  119. package/template/.claude/skills/debugging-patterns/SKILL.md +485 -484
  120. package/template/.claude/skills/docker-patterns/SKILL.md +555 -547
  121. package/template/.claude/skills/docs-tracker/SKILL.md +239 -239
  122. package/template/.claude/skills/final-check/SKILL.md +284 -284
  123. package/template/.claude/skills/git-workflow/SKILL.md +454 -454
  124. package/template/.claude/skills/mongoose-patterns/SKILL.md +499 -512
  125. package/template/.claude/skills/nextjs-app-router/SKILL.md +327 -337
  126. package/template/.claude/skills/performance-patterns/SKILL.md +547 -549
  127. package/template/.claude/skills/playwright-automation/SKILL.md +438 -438
  128. package/template/.claude/skills/quality-gate/SKILL.md +294 -294
  129. package/template/.claude/skills/react-patterns/SKILL.md +389 -376
  130. package/template/.claude/skills/research-cache/SKILL.md +222 -207
  131. package/template/.claude/skills/security-scan/SKILL.md +222 -222
  132. package/template/.claude/skills/shadcn-ui/SKILL.md +511 -520
  133. package/template/.claude/skills/tailwind-patterns/SKILL.md +465 -467
  134. package/template/.claude/skills/test-coverage/SKILL.md +467 -464
  135. package/template/.claude/skills/trpc-api/SKILL.md +434 -435
  136. package/template/.claude/skills/typescript-strict/SKILL.md +367 -368
  137. package/template/.claude/skills/ui-ux-audit/SKILL.md +254 -254
  138. package/template/.claude/skills/zod-validation/SKILL.md +403 -405
  139. package/template/CLAUDE.md +25 -25
@@ -1,512 +1,499 @@
1
- ---
2
- name: mongoose-patterns
3
- description: Mongoose/MongoDB patterns for schema design, queries, indexes, aggregations. Use when working with MongoDB through Mongoose.
4
- allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
- ---
6
-
7
- # Mongoose Patterns - MongoDB ODM Best Practices
8
-
9
- ## Purpose
10
-
11
- Expert guidance for Mongoose/MongoDB:
12
-
13
- - **Schema Design** - Proper typing and validation
14
- - **Queries** - Efficient query patterns
15
- - **Indexes** - Performance optimization
16
- - **Aggregations** - Complex data operations
17
- - **Relationships** - References vs embedding
18
-
19
- ---
20
-
21
- ## Project Structure
22
-
23
- ```
24
- server/
25
- ├── db/
26
- │ ├── connection.ts # MongoDB connection
27
- │ └── index.ts # Export db utilities
28
- ├── models/
29
- │ ├── user.model.ts
30
- │ ├── post.model.ts
31
- │ └── index.ts # Export all models
32
- types/
33
- └── models/
34
- ├── user.types.ts # User interfaces
35
- └── post.types.ts # Post interfaces
36
- ```
37
-
38
- ---
39
-
40
- ## Schema Design
41
-
42
- ### Basic Schema with TypeScript
43
-
44
- ```typescript
45
- // types/models/user.types.ts
46
- import { Types, Document } from 'mongoose';
47
-
48
- export interface IUser {
49
- email: string;
50
- name: string;
51
- passwordHash: string;
52
- role: 'admin' | 'user' | 'guest';
53
- profile?: {
54
- bio?: string;
55
- avatar?: string;
56
- };
57
- createdAt: Date;
58
- updatedAt: Date;
59
- }
60
-
61
- export interface IUserDocument extends IUser, Document {
62
- _id: Types.ObjectId;
63
- comparePassword(password: string): Promise<boolean>;
64
- toPublic(): Omit<IUser, 'passwordHash'>;
65
- }
66
-
67
- // server/models/user.model.ts
68
- import mongoose, { Schema, Model } from 'mongoose';
69
- import { IUser, IUserDocument } from '$types/models/user.types';
70
-
71
- const userSchema = new Schema<IUserDocument>(
72
- {
73
- email: {
74
- type: String,
75
- required: [true, 'Email is required'],
76
- unique: true,
77
- lowercase: true,
78
- trim: true,
79
- validate: {
80
- validator: (v: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
81
- message: 'Invalid email format',
82
- },
83
- },
84
- name: {
85
- type: String,
86
- required: [true, 'Name is required'],
87
- minlength: [2, 'Name must be at least 2 characters'],
88
- maxlength: [100, 'Name cannot exceed 100 characters'],
89
- trim: true,
90
- },
91
- passwordHash: {
92
- type: String,
93
- required: true,
94
- select: false, // Never include by default
95
- },
96
- role: {
97
- type: String,
98
- enum: ['admin', 'user', 'guest'],
99
- default: 'user',
100
- },
101
- profile: {
102
- bio: { type: String, maxlength: 500 },
103
- avatar: { type: String },
104
- },
105
- },
106
- {
107
- timestamps: true,
108
- toJSON: { virtuals: true },
109
- toObject: { virtuals: true },
110
- }
111
- );
112
-
113
- // Indexes
114
- userSchema.index({ email: 1 }, { unique: true });
115
- userSchema.index({ role: 1 });
116
- userSchema.index({ createdAt: -1 });
117
-
118
- // Instance methods
119
- userSchema.methods.comparePassword = async function (password: string): Promise<boolean> {
120
- return Bun.password.verify(password, this.passwordHash);
121
- };
122
-
123
- userSchema.methods.toPublic = function () {
124
- const obj = this.toObject();
125
- delete obj.passwordHash;
126
- return obj;
127
- };
128
-
129
- // Static methods
130
- userSchema.statics.findByEmail = function (email: string) {
131
- return this.findOne({ email: email.toLowerCase() });
132
- };
133
-
134
- export const UserModel: Model<IUserDocument> = mongoose.model('User', userSchema);
135
- ```
136
-
137
- ---
138
-
139
- ## Connection Management
140
-
141
- ```typescript
142
- // server/db/connection.ts
143
- import mongoose from 'mongoose';
144
- import { logger } from '@common';
145
-
146
- const MONGODB_URI = process.env['MONGODB_URI']!;
147
-
148
- if (!MONGODB_URI) {
149
- throw new Error('MONGODB_URI environment variable not set');
150
- }
151
-
152
- let isConnected = false;
153
-
154
- export async function connectDB(): Promise<void> {
155
- if (isConnected) {
156
- logger.info('Using existing MongoDB connection');
157
- return;
158
- }
159
-
160
- try {
161
- await mongoose.connect(MONGODB_URI, {
162
- maxPoolSize: 10,
163
- serverSelectionTimeoutMS: 5000,
164
- socketTimeoutMS: 45000,
165
- });
166
-
167
- isConnected = true;
168
- logger.info('Connected to MongoDB');
169
- } catch (error) {
170
- logger.error('MongoDB connection error:', error);
171
- throw error;
172
- }
173
- }
174
-
175
- export async function disconnectDB(): Promise<void> {
176
- if (!isConnected) return;
177
-
178
- await mongoose.disconnect();
179
- isConnected = false;
180
- logger.info('Disconnected from MongoDB');
181
- }
182
-
183
- // Graceful shutdown
184
- process.on('SIGINT', async () => {
185
- await disconnectDB();
186
- process.exit(0);
187
- });
188
- ```
189
-
190
- ---
191
-
192
- ## Query Patterns
193
-
194
- ### Find with Typing
195
-
196
- ```typescript
197
- // Find one
198
- const user = await UserModel.findById(id);
199
- const userByEmail = await UserModel.findOne({ email });
200
-
201
- // Find with projection
202
- const users = await UserModel.find({ role: 'user' })
203
- .select('name email role')
204
- .lean(); // Returns plain objects (faster)
205
-
206
- // Find with pagination
207
- const page = 1;
208
- const limit = 20;
209
- const users = await UserModel.find()
210
- .sort({ createdAt: -1 })
211
- .skip((page - 1) * limit)
212
- .limit(limit)
213
- .lean();
214
-
215
- // Count
216
- const total = await UserModel.countDocuments({ role: 'user' });
217
- ```
218
-
219
- ### Update Patterns
220
-
221
- ```typescript
222
- // Update one (returns updated doc)
223
- const updated = await UserModel.findByIdAndUpdate(
224
- id,
225
- { $set: { name: 'New Name' } },
226
- { new: true, runValidators: true }
227
- );
228
-
229
- // Update many
230
- const result = await UserModel.updateMany(
231
- { role: 'guest' },
232
- { $set: { role: 'user' } }
233
- );
234
- console.log(`Updated ${result.modifiedCount} documents`);
235
-
236
- // Upsert
237
- const user = await UserModel.findOneAndUpdate(
238
- { email },
239
- { $setOnInsert: { createdAt: new Date() }, $set: { lastLogin: new Date() } },
240
- { upsert: true, new: true }
241
- );
242
- ```
243
-
244
- ### Delete Patterns
245
-
246
- ```typescript
247
- // Delete one
248
- await UserModel.findByIdAndDelete(id);
249
-
250
- // Delete many
251
- const result = await UserModel.deleteMany({ role: 'guest' });
252
- console.log(`Deleted ${result.deletedCount} documents`);
253
-
254
- // Soft delete pattern
255
- const softDeleteSchema = new Schema({
256
- deletedAt: { type: Date, default: null },
257
- });
258
-
259
- // Query middleware to exclude soft-deleted
260
- userSchema.pre('find', function () {
261
- this.where({ deletedAt: null });
262
- });
263
- ```
264
-
265
- ---
266
-
267
- ## Aggregation Pipeline
268
-
269
- ```typescript
270
- // Complex aggregation
271
- const stats = await UserModel.aggregate([
272
- // Match stage
273
- { $match: { createdAt: { $gte: startDate } } },
274
-
275
- // Group stage
276
- {
277
- $group: {
278
- _id: '$role',
279
- count: { $sum: 1 },
280
- avgAge: { $avg: '$age' },
281
- },
282
- },
283
-
284
- // Sort stage
285
- { $sort: { count: -1 } },
286
-
287
- // Project stage
288
- {
289
- $project: {
290
- role: '$_id',
291
- count: 1,
292
- avgAge: { $round: ['$avgAge', 1] },
293
- _id: 0,
294
- },
295
- },
296
- ]);
297
-
298
- // Lookup (join)
299
- const postsWithAuthors = await PostModel.aggregate([
300
- {
301
- $lookup: {
302
- from: 'users',
303
- localField: 'authorId',
304
- foreignField: '_id',
305
- as: 'author',
306
- },
307
- },
308
- { $unwind: '$author' },
309
- {
310
- $project: {
311
- title: 1,
312
- content: 1,
313
- 'author.name': 1,
314
- 'author.email': 1,
315
- },
316
- },
317
- ]);
318
- ```
319
-
320
- ---
321
-
322
- ## Indexes
323
-
324
- ### Index Types
325
-
326
- ```typescript
327
- // Single field
328
- userSchema.index({ email: 1 }); // Ascending
329
- userSchema.index({ createdAt: -1 }); // Descending
330
-
331
- // Compound index
332
- userSchema.index({ role: 1, createdAt: -1 });
333
-
334
- // Unique index
335
- userSchema.index({ email: 1 }, { unique: true });
336
-
337
- // Sparse index (only index docs with field)
338
- userSchema.index({ nickname: 1 }, { sparse: true });
339
-
340
- // TTL index (auto-delete after time)
341
- sessionSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
342
-
343
- // Text index (full-text search)
344
- postSchema.index({ title: 'text', content: 'text' });
345
-
346
- // Partial index
347
- userSchema.index(
348
- { email: 1 },
349
- {
350
- partialFilterExpression: { isActive: true },
351
- }
352
- );
353
- ```
354
-
355
- ### Check Indexes
356
-
357
- ```bash
358
- # List indexes
359
- db.users.getIndexes()
360
-
361
- # Explain query
362
- db.users.find({ email: "test@test.com" }).explain("executionStats")
363
- ```
364
-
365
- ---
366
-
367
- ## Relationships
368
-
369
- ### Reference (Normalized)
370
-
371
- ```typescript
372
- // Post references User
373
- const postSchema = new Schema({
374
- title: String,
375
- authorId: {
376
- type: Schema.Types.ObjectId,
377
- ref: 'User',
378
- required: true,
379
- index: true,
380
- },
381
- });
382
-
383
- // Populate
384
- const post = await PostModel.findById(id).populate('authorId', 'name email');
385
-
386
- // Virtual populate
387
- userSchema.virtual('posts', {
388
- ref: 'Post',
389
- localField: '_id',
390
- foreignField: 'authorId',
391
- });
392
-
393
- const user = await UserModel.findById(id).populate('posts');
394
- ```
395
-
396
- ### Embedded (Denormalized)
397
-
398
- ```typescript
399
- // Comments embedded in Post
400
- const postSchema = new Schema({
401
- title: String,
402
- comments: [
403
- {
404
- author: String,
405
- content: String,
406
- createdAt: { type: Date, default: Date.now },
407
- },
408
- ],
409
- });
410
-
411
- // Add comment
412
- await PostModel.findByIdAndUpdate(id, {
413
- $push: { comments: { author: 'John', content: 'Great post!' } },
414
- });
415
- ```
416
-
417
- ---
418
-
419
- ## Middleware (Hooks)
420
-
421
- ```typescript
422
- // Pre-save hook
423
- userSchema.pre('save', async function (next) {
424
- if (this.isModified('passwordHash')) {
425
- this.passwordHash = await Bun.password.hash(this.passwordHash);
426
- }
427
- next();
428
- });
429
-
430
- // Post-save hook
431
- userSchema.post('save', function (doc) {
432
- logger.info(`User saved: ${doc.email}`);
433
- });
434
-
435
- // Pre-find hook
436
- userSchema.pre('find', function () {
437
- // Always exclude deleted documents
438
- this.where({ deletedAt: null });
439
- });
440
-
441
- // Error handling hook
442
- userSchema.post('save', function (error: Error, doc: IUserDocument, next: () => void) {
443
- if (error.name === 'MongoServerError' && (error as any).code === 11000) {
444
- next(new Error('Email already exists'));
445
- } else {
446
- next();
447
- }
448
- });
449
- ```
450
-
451
- ---
452
-
453
- ## Transactions
454
-
455
- ```typescript
456
- import mongoose from 'mongoose';
457
-
458
- async function transferCredits(fromId: string, toId: string, amount: number) {
459
- const session = await mongoose.startSession();
460
-
461
- try {
462
- session.startTransaction();
463
-
464
- await UserModel.findByIdAndUpdate(
465
- fromId,
466
- { $inc: { credits: -amount } },
467
- { session }
468
- );
469
-
470
- await UserModel.findByIdAndUpdate(
471
- toId,
472
- { $inc: { credits: amount } },
473
- { session }
474
- );
475
-
476
- await session.commitTransaction();
477
- } catch (error) {
478
- await session.abortTransaction();
479
- throw error;
480
- } finally {
481
- session.endSession();
482
- }
483
- }
484
- ```
485
-
486
- ---
487
-
488
- ## Agent Integration
489
-
490
- This skill is used by:
491
-
492
- - **mongoose-schema-designer** agent
493
- - **mongoose-index-optimizer** agent
494
- - **mongoose-aggregation** agent
495
- - **mongodb-query-optimizer** agent
496
- - **database-seeder** agent
497
-
498
- ---
499
-
500
- ## FORBIDDEN
501
-
502
- 1. **User ID from input** - Always use session/context
503
- 2. **No indexes on query fields** - Always index filtered fields
504
- 3. **Returning passwordHash** - Use `select: false`
505
- 4. **N+1 queries** - Use `.populate()` or aggregation
506
- 5. **Unbounded queries** - Always use `.limit()`
507
-
508
- ---
509
-
510
- ## Version
511
-
512
- - **v1.0.0** - Initial implementation based on Mongoose 8.x patterns
1
+ ---
2
+ name: mongoose-patterns
3
+ description: Mongoose/MongoDB patterns for schema design, queries, indexes, aggregations. Use when working with MongoDB through Mongoose.
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, Glob
5
+ ---
6
+
7
+ # Mongoose Patterns - MongoDB ODM Best Practices
8
+
9
+ ## Purpose
10
+
11
+ Expert guidance for Mongoose/MongoDB:
12
+
13
+ - **Schema Design** - Proper typing and validation
14
+ - **Queries** - Efficient query patterns
15
+ - **Indexes** - Performance optimization
16
+ - **Aggregations** - Complex data operations
17
+ - **Relationships** - References vs embedding
18
+
19
+ ---
20
+
21
+ ## Project Structure
22
+
23
+ ```
24
+ server/
25
+ ├── db/
26
+ │ ├── connection.ts # MongoDB connection
27
+ │ └── index.ts # Export db utilities
28
+ ├── models/
29
+ │ ├── user.model.ts
30
+ │ ├── post.model.ts
31
+ │ └── index.ts # Export all models
32
+ types/
33
+ └── models/
34
+ ├── user.types.ts # User interfaces
35
+ └── post.types.ts # Post interfaces
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Schema Design
41
+
42
+ ### Basic Schema with TypeScript
43
+
44
+ ```typescript
45
+ // types/models/user.types.ts
46
+ import { Types, Document } from 'mongoose';
47
+
48
+ export interface IUser {
49
+ email: string;
50
+ name: string;
51
+ passwordHash: string;
52
+ role: 'admin' | 'user' | 'guest';
53
+ profile?: {
54
+ bio?: string;
55
+ avatar?: string;
56
+ };
57
+ createdAt: Date;
58
+ updatedAt: Date;
59
+ }
60
+
61
+ export interface IUserDocument extends IUser, Document {
62
+ _id: Types.ObjectId;
63
+ comparePassword(password: string): Promise<boolean>;
64
+ toPublic(): Omit<IUser, 'passwordHash'>;
65
+ }
66
+
67
+ // server/models/user.model.ts
68
+ import mongoose, { Schema, Model } from 'mongoose';
69
+ import { IUser, IUserDocument } from '$types/models/user.types';
70
+
71
+ const userSchema = new Schema<IUserDocument>(
72
+ {
73
+ email: {
74
+ type: String,
75
+ required: [true, 'Email is required'],
76
+ unique: true,
77
+ lowercase: true,
78
+ trim: true,
79
+ validate: {
80
+ validator: (v: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
81
+ message: 'Invalid email format',
82
+ },
83
+ },
84
+ name: {
85
+ type: String,
86
+ required: [true, 'Name is required'],
87
+ minlength: [2, 'Name must be at least 2 characters'],
88
+ maxlength: [100, 'Name cannot exceed 100 characters'],
89
+ trim: true,
90
+ },
91
+ passwordHash: {
92
+ type: String,
93
+ required: true,
94
+ select: false, // Never include by default
95
+ },
96
+ role: {
97
+ type: String,
98
+ enum: ['admin', 'user', 'guest'],
99
+ default: 'user',
100
+ },
101
+ profile: {
102
+ bio: { type: String, maxlength: 500 },
103
+ avatar: { type: String },
104
+ },
105
+ },
106
+ {
107
+ timestamps: true,
108
+ toJSON: { virtuals: true },
109
+ toObject: { virtuals: true },
110
+ }
111
+ );
112
+
113
+ // Indexes
114
+ userSchema.index({ email: 1 }, { unique: true });
115
+ userSchema.index({ role: 1 });
116
+ userSchema.index({ createdAt: -1 });
117
+
118
+ // Instance methods
119
+ userSchema.methods.comparePassword = async function (password: string): Promise<boolean> {
120
+ return Bun.password.verify(password, this.passwordHash);
121
+ };
122
+
123
+ userSchema.methods.toPublic = function () {
124
+ const obj = this.toObject();
125
+ delete obj.passwordHash;
126
+ return obj;
127
+ };
128
+
129
+ // Static methods
130
+ userSchema.statics.findByEmail = function (email: string) {
131
+ return this.findOne({ email: email.toLowerCase() });
132
+ };
133
+
134
+ export const UserModel: Model<IUserDocument> = mongoose.model('User', userSchema);
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Connection Management
140
+
141
+ ```typescript
142
+ // server/db/connection.ts
143
+ import mongoose from 'mongoose';
144
+ import { logger } from '@common';
145
+
146
+ const MONGODB_URI = process.env['MONGODB_URI']!;
147
+
148
+ if (!MONGODB_URI) {
149
+ throw new Error('MONGODB_URI environment variable not set');
150
+ }
151
+
152
+ let isConnected = false;
153
+
154
+ export async function connectDB(): Promise<void> {
155
+ if (isConnected) {
156
+ logger.info('Using existing MongoDB connection');
157
+ return;
158
+ }
159
+
160
+ try {
161
+ await mongoose.connect(MONGODB_URI, {
162
+ maxPoolSize: 10,
163
+ serverSelectionTimeoutMS: 5000,
164
+ socketTimeoutMS: 45000,
165
+ });
166
+
167
+ isConnected = true;
168
+ logger.info('Connected to MongoDB');
169
+ } catch (error) {
170
+ logger.error('MongoDB connection error:', error);
171
+ throw error;
172
+ }
173
+ }
174
+
175
+ export async function disconnectDB(): Promise<void> {
176
+ if (!isConnected) return;
177
+
178
+ await mongoose.disconnect();
179
+ isConnected = false;
180
+ logger.info('Disconnected from MongoDB');
181
+ }
182
+
183
+ // Graceful shutdown
184
+ process.on('SIGINT', async () => {
185
+ await disconnectDB();
186
+ process.exit(0);
187
+ });
188
+ ```
189
+
190
+ ---
191
+
192
+ ## Query Patterns
193
+
194
+ ### Find with Typing
195
+
196
+ ```typescript
197
+ // Find one
198
+ const user = await UserModel.findById(id);
199
+ const userByEmail = await UserModel.findOne({ email });
200
+
201
+ // Find with projection
202
+ const users = await UserModel.find({ role: 'user' }).select('name email role').lean(); // Returns plain objects (faster)
203
+
204
+ // Find with pagination
205
+ const page = 1;
206
+ const limit = 20;
207
+ const users = await UserModel.find()
208
+ .sort({ createdAt: -1 })
209
+ .skip((page - 1) * limit)
210
+ .limit(limit)
211
+ .lean();
212
+
213
+ // Count
214
+ const total = await UserModel.countDocuments({ role: 'user' });
215
+ ```
216
+
217
+ ### Update Patterns
218
+
219
+ ```typescript
220
+ // Update one (returns updated doc)
221
+ const updated = await UserModel.findByIdAndUpdate(
222
+ id,
223
+ { $set: { name: 'New Name' } },
224
+ { new: true, runValidators: true }
225
+ );
226
+
227
+ // Update many
228
+ const result = await UserModel.updateMany({ role: 'guest' }, { $set: { role: 'user' } });
229
+ console.log(`Updated ${result.modifiedCount} documents`);
230
+
231
+ // Upsert
232
+ const user = await UserModel.findOneAndUpdate(
233
+ { email },
234
+ { $setOnInsert: { createdAt: new Date() }, $set: { lastLogin: new Date() } },
235
+ { upsert: true, new: true }
236
+ );
237
+ ```
238
+
239
+ ### Delete Patterns
240
+
241
+ ```typescript
242
+ // Delete one
243
+ await UserModel.findByIdAndDelete(id);
244
+
245
+ // Delete many
246
+ const result = await UserModel.deleteMany({ role: 'guest' });
247
+ console.log(`Deleted ${result.deletedCount} documents`);
248
+
249
+ // Soft delete pattern
250
+ const softDeleteSchema = new Schema({
251
+ deletedAt: { type: Date, default: null },
252
+ });
253
+
254
+ // Query middleware to exclude soft-deleted
255
+ userSchema.pre('find', function () {
256
+ this.where({ deletedAt: null });
257
+ });
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Aggregation Pipeline
263
+
264
+ ```typescript
265
+ // Complex aggregation
266
+ const stats = await UserModel.aggregate([
267
+ // Match stage
268
+ { $match: { createdAt: { $gte: startDate } } },
269
+
270
+ // Group stage
271
+ {
272
+ $group: {
273
+ _id: '$role',
274
+ count: { $sum: 1 },
275
+ avgAge: { $avg: '$age' },
276
+ },
277
+ },
278
+
279
+ // Sort stage
280
+ { $sort: { count: -1 } },
281
+
282
+ // Project stage
283
+ {
284
+ $project: {
285
+ role: '$_id',
286
+ count: 1,
287
+ avgAge: { $round: ['$avgAge', 1] },
288
+ _id: 0,
289
+ },
290
+ },
291
+ ]);
292
+
293
+ // Lookup (join)
294
+ const postsWithAuthors = await PostModel.aggregate([
295
+ {
296
+ $lookup: {
297
+ from: 'users',
298
+ localField: 'authorId',
299
+ foreignField: '_id',
300
+ as: 'author',
301
+ },
302
+ },
303
+ { $unwind: '$author' },
304
+ {
305
+ $project: {
306
+ title: 1,
307
+ content: 1,
308
+ 'author.name': 1,
309
+ 'author.email': 1,
310
+ },
311
+ },
312
+ ]);
313
+ ```
314
+
315
+ ---
316
+
317
+ ## Indexes
318
+
319
+ ### Index Types
320
+
321
+ ```typescript
322
+ // Single field
323
+ userSchema.index({ email: 1 }); // Ascending
324
+ userSchema.index({ createdAt: -1 }); // Descending
325
+
326
+ // Compound index
327
+ userSchema.index({ role: 1, createdAt: -1 });
328
+
329
+ // Unique index
330
+ userSchema.index({ email: 1 }, { unique: true });
331
+
332
+ // Sparse index (only index docs with field)
333
+ userSchema.index({ nickname: 1 }, { sparse: true });
334
+
335
+ // TTL index (auto-delete after time)
336
+ sessionSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
337
+
338
+ // Text index (full-text search)
339
+ postSchema.index({ title: 'text', content: 'text' });
340
+
341
+ // Partial index
342
+ userSchema.index(
343
+ { email: 1 },
344
+ {
345
+ partialFilterExpression: { isActive: true },
346
+ }
347
+ );
348
+ ```
349
+
350
+ ### Check Indexes
351
+
352
+ ```bash
353
+ # List indexes
354
+ db.users.getIndexes()
355
+
356
+ # Explain query
357
+ db.users.find({ email: "test@test.com" }).explain("executionStats")
358
+ ```
359
+
360
+ ---
361
+
362
+ ## Relationships
363
+
364
+ ### Reference (Normalized)
365
+
366
+ ```typescript
367
+ // Post references User
368
+ const postSchema = new Schema({
369
+ title: String,
370
+ authorId: {
371
+ type: Schema.Types.ObjectId,
372
+ ref: 'User',
373
+ required: true,
374
+ index: true,
375
+ },
376
+ });
377
+
378
+ // Populate
379
+ const post = await PostModel.findById(id).populate('authorId', 'name email');
380
+
381
+ // Virtual populate
382
+ userSchema.virtual('posts', {
383
+ ref: 'Post',
384
+ localField: '_id',
385
+ foreignField: 'authorId',
386
+ });
387
+
388
+ const user = await UserModel.findById(id).populate('posts');
389
+ ```
390
+
391
+ ### Embedded (Denormalized)
392
+
393
+ ```typescript
394
+ // Comments embedded in Post
395
+ const postSchema = new Schema({
396
+ title: String,
397
+ comments: [
398
+ {
399
+ author: String,
400
+ content: String,
401
+ createdAt: { type: Date, default: Date.now },
402
+ },
403
+ ],
404
+ });
405
+
406
+ // Add comment
407
+ await PostModel.findByIdAndUpdate(id, {
408
+ $push: { comments: { author: 'John', content: 'Great post!' } },
409
+ });
410
+ ```
411
+
412
+ ---
413
+
414
+ ## Middleware (Hooks)
415
+
416
+ ```typescript
417
+ // Pre-save hook
418
+ userSchema.pre('save', async function (next) {
419
+ if (this.isModified('passwordHash')) {
420
+ this.passwordHash = await Bun.password.hash(this.passwordHash);
421
+ }
422
+ next();
423
+ });
424
+
425
+ // Post-save hook
426
+ userSchema.post('save', function (doc) {
427
+ logger.info(`User saved: ${doc.email}`);
428
+ });
429
+
430
+ // Pre-find hook
431
+ userSchema.pre('find', function () {
432
+ // Always exclude deleted documents
433
+ this.where({ deletedAt: null });
434
+ });
435
+
436
+ // Error handling hook
437
+ userSchema.post('save', function (error: Error, doc: IUserDocument, next: () => void) {
438
+ if (error.name === 'MongoServerError' && (error as any).code === 11000) {
439
+ next(new Error('Email already exists'));
440
+ } else {
441
+ next();
442
+ }
443
+ });
444
+ ```
445
+
446
+ ---
447
+
448
+ ## Transactions
449
+
450
+ ```typescript
451
+ import mongoose from 'mongoose';
452
+
453
+ async function transferCredits(fromId: string, toId: string, amount: number) {
454
+ const session = await mongoose.startSession();
455
+
456
+ try {
457
+ session.startTransaction();
458
+
459
+ await UserModel.findByIdAndUpdate(fromId, { $inc: { credits: -amount } }, { session });
460
+
461
+ await UserModel.findByIdAndUpdate(toId, { $inc: { credits: amount } }, { session });
462
+
463
+ await session.commitTransaction();
464
+ } catch (error) {
465
+ await session.abortTransaction();
466
+ throw error;
467
+ } finally {
468
+ session.endSession();
469
+ }
470
+ }
471
+ ```
472
+
473
+ ---
474
+
475
+ ## Agent Integration
476
+
477
+ This skill is used by:
478
+
479
+ - **mongoose-schema-designer** agent
480
+ - **mongoose-index-optimizer** agent
481
+ - **mongoose-aggregation** agent
482
+ - **mongodb-query-optimizer** agent
483
+ - **database-seeder** agent
484
+
485
+ ---
486
+
487
+ ## FORBIDDEN
488
+
489
+ 1. **User ID from input** - Always use session/context
490
+ 2. **No indexes on query fields** - Always index filtered fields
491
+ 3. **Returning passwordHash** - Use `select: false`
492
+ 4. **N+1 queries** - Use `.populate()` or aggregation
493
+ 5. **Unbounded queries** - Always use `.limit()`
494
+
495
+ ---
496
+
497
+ ## Version
498
+
499
+ - **v1.0.0** - Initial implementation based on Mongoose 8.x patterns