start-vibing 2.0.2 → 2.0.3

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 (35) hide show
  1. package/package.json +1 -1
  2. package/template/.claude/agents/01-orchestration/checkpoint-manager.md +1 -1
  3. package/template/.claude/agents/01-orchestration/context-manager.md +1 -1
  4. package/template/.claude/agents/01-orchestration/error-recovery.md +1 -1
  5. package/template/.claude/agents/01-orchestration/orchestrator.md +1 -1
  6. package/template/.claude/agents/01-orchestration/parallel-coordinator.md +1 -1
  7. package/template/.claude/agents/01-orchestration/task-decomposer.md +1 -1
  8. package/template/.claude/agents/01-orchestration/workflow-router.md +1 -1
  9. package/template/.claude/agents/02-typescript/bun-runtime-expert.md +1 -1
  10. package/template/.claude/agents/02-typescript/esm-resolver.md +1 -1
  11. package/template/.claude/agents/02-typescript/import-alias-enforcer.md +1 -1
  12. package/template/.claude/agents/02-typescript/ts-migration-helper.md +1 -1
  13. package/template/.claude/agents/02-typescript/ts-strict-checker.md +1 -1
  14. package/template/.claude/agents/02-typescript/ts-types-analyzer.md +1 -1
  15. package/template/.claude/agents/02-typescript/type-definition-writer.md +1 -1
  16. package/template/.claude/agents/02-typescript/zod-schema-designer.md +1 -1
  17. package/template/.claude/agents/02-typescript/zod-validator.md +1 -1
  18. package/template/.claude/hooks/SETUP.md +85 -11
  19. package/template/.claude/hooks/run-hook.cmd +46 -0
  20. package/template/.claude/hooks/run-hook.sh +43 -0
  21. package/template/.claude/hooks/run-hook.ts +158 -0
  22. package/template/.claude/hooks/stop-validator.ts +339 -0
  23. package/template/.claude/hooks/user-prompt-submit.ts +298 -0
  24. package/template/.claude/settings.json +4 -3
  25. package/template/.claude/skills/bun-runtime/SKILL.md +430 -0
  26. package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +46 -4
  27. package/template/.claude/skills/mongoose-patterns/SKILL.md +512 -0
  28. package/template/.claude/skills/nextjs-app-router/SKILL.md +337 -0
  29. package/template/.claude/skills/playwright-automation/SKILL.md +438 -0
  30. package/template/.claude/skills/react-patterns/SKILL.md +376 -0
  31. package/template/.claude/skills/shadcn-ui/SKILL.md +520 -0
  32. package/template/.claude/skills/tailwind-patterns/SKILL.md +467 -0
  33. package/template/.claude/skills/trpc-api/SKILL.md +435 -0
  34. package/template/.claude/skills/typescript-strict/SKILL.md +368 -0
  35. package/template/.claude/skills/zod-validation/SKILL.md +405 -0
@@ -0,0 +1,512 @@
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