@qazuor/claude-code-config 0.5.0 → 0.6.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 (57) hide show
  1. package/README.md +106 -41
  2. package/dist/bin.cjs +963 -84
  3. package/dist/bin.cjs.map +1 -1
  4. package/dist/bin.js +963 -84
  5. package/dist/bin.js.map +1 -1
  6. package/dist/index.cjs +73 -56
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +2 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +73 -56
  11. package/dist/index.js.map +1 -1
  12. package/package.json +23 -24
  13. package/templates/CLAUDE.md.template +60 -5
  14. package/templates/agents/README.md +58 -39
  15. package/templates/agents/_registry.json +43 -202
  16. package/templates/agents/engineering/{hono-engineer.md → api-engineer.md} +61 -70
  17. package/templates/agents/engineering/database-engineer.md +253 -0
  18. package/templates/agents/engineering/frontend-engineer.md +302 -0
  19. package/templates/hooks/on-notification.sh +0 -0
  20. package/templates/scripts/add-changelogs.sh +0 -0
  21. package/templates/scripts/generate-code-registry.ts +0 -0
  22. package/templates/scripts/health-check.sh +0 -0
  23. package/templates/scripts/sync-registry.sh +0 -0
  24. package/templates/scripts/telemetry-report.ts +0 -0
  25. package/templates/scripts/validate-docs.sh +0 -0
  26. package/templates/scripts/validate-registry.sh +0 -0
  27. package/templates/scripts/validate-structure.sh +0 -0
  28. package/templates/scripts/worktree-cleanup.sh +0 -0
  29. package/templates/scripts/worktree-create.sh +0 -0
  30. package/templates/skills/README.md +99 -90
  31. package/templates/skills/_registry.json +323 -16
  32. package/templates/skills/api-frameworks/express-patterns.md +411 -0
  33. package/templates/skills/api-frameworks/fastify-patterns.md +419 -0
  34. package/templates/skills/api-frameworks/hono-patterns.md +388 -0
  35. package/templates/skills/api-frameworks/nestjs-patterns.md +497 -0
  36. package/templates/skills/database/drizzle-patterns.md +449 -0
  37. package/templates/skills/database/mongoose-patterns.md +503 -0
  38. package/templates/skills/database/prisma-patterns.md +487 -0
  39. package/templates/skills/frontend-frameworks/astro-patterns.md +415 -0
  40. package/templates/skills/frontend-frameworks/nextjs-patterns.md +470 -0
  41. package/templates/skills/frontend-frameworks/react-patterns.md +516 -0
  42. package/templates/skills/frontend-frameworks/tanstack-start-patterns.md +469 -0
  43. package/templates/skills/patterns/atdd-methodology.md +364 -0
  44. package/templates/skills/patterns/bdd-methodology.md +281 -0
  45. package/templates/skills/patterns/clean-architecture.md +444 -0
  46. package/templates/skills/patterns/hexagonal-architecture.md +567 -0
  47. package/templates/skills/patterns/vertical-slice-architecture.md +502 -0
  48. package/templates/agents/engineering/astro-engineer.md +0 -293
  49. package/templates/agents/engineering/db-drizzle-engineer.md +0 -360
  50. package/templates/agents/engineering/express-engineer.md +0 -316
  51. package/templates/agents/engineering/fastify-engineer.md +0 -399
  52. package/templates/agents/engineering/mongoose-engineer.md +0 -473
  53. package/templates/agents/engineering/nestjs-engineer.md +0 -429
  54. package/templates/agents/engineering/nextjs-engineer.md +0 -451
  55. package/templates/agents/engineering/prisma-engineer.md +0 -432
  56. package/templates/agents/engineering/react-senior-dev.md +0 -394
  57. package/templates/agents/engineering/tanstack-start-engineer.md +0 -447
@@ -0,0 +1,449 @@
1
+ # Drizzle ORM Patterns
2
+
3
+ ## Overview
4
+
5
+ Drizzle is a TypeScript ORM with maximum type safety. This skill provides patterns for database operations with Drizzle.
6
+
7
+ ---
8
+
9
+ ## Schema Definition
10
+
11
+ ### Table with Relations
12
+
13
+ ```typescript
14
+ import {
15
+ pgTable,
16
+ uuid,
17
+ varchar,
18
+ text,
19
+ timestamp,
20
+ integer,
21
+ index,
22
+ check,
23
+ } from 'drizzle-orm/pg-core';
24
+ import { relations, sql } from 'drizzle-orm';
25
+
26
+ // Users table
27
+ export const users = pgTable('users', {
28
+ id: uuid('id').defaultRandom().primaryKey(),
29
+ email: varchar('email', { length: 255 }).notNull().unique(),
30
+ name: varchar('name', { length: 255 }),
31
+ passwordHash: varchar('password_hash', { length: 255 }).notNull(),
32
+ role: varchar('role', { length: 20 }).notNull().default('user'),
33
+ createdAt: timestamp('created_at').defaultNow().notNull(),
34
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
35
+ deletedAt: timestamp('deleted_at'),
36
+ }, (table) => ({
37
+ emailIdx: index('idx_users_email').on(table.email),
38
+ }));
39
+
40
+ // Items table
41
+ export const items = pgTable('items', {
42
+ id: uuid('id').defaultRandom().primaryKey(),
43
+ title: varchar('title', { length: 255 }).notNull(),
44
+ description: text('description'),
45
+ status: varchar('status', { length: 20 }).notNull().default('active'),
46
+ price: integer('price').notNull(),
47
+ authorId: uuid('author_id')
48
+ .notNull()
49
+ .references(() => users.id, { onDelete: 'cascade' }),
50
+ createdAt: timestamp('created_at').defaultNow().notNull(),
51
+ updatedAt: timestamp('updated_at').defaultNow().notNull(),
52
+ deletedAt: timestamp('deleted_at'),
53
+ }, (table) => ({
54
+ authorIdx: index('idx_items_author').on(table.authorId),
55
+ statusIdx: index('idx_items_status').on(table.status),
56
+ priceCheck: check('check_price_positive', sql`${table.price} > 0`),
57
+ }));
58
+
59
+ // Tags table (many-to-many)
60
+ export const tags = pgTable('tags', {
61
+ id: uuid('id').defaultRandom().primaryKey(),
62
+ name: varchar('name', { length: 50 }).notNull().unique(),
63
+ });
64
+
65
+ // Junction table
66
+ export const itemTags = pgTable('item_tags', {
67
+ itemId: uuid('item_id')
68
+ .notNull()
69
+ .references(() => items.id, { onDelete: 'cascade' }),
70
+ tagId: uuid('tag_id')
71
+ .notNull()
72
+ .references(() => tags.id, { onDelete: 'cascade' }),
73
+ }, (table) => ({
74
+ pk: primaryKey({ columns: [table.itemId, table.tagId] }),
75
+ }));
76
+ ```
77
+
78
+ ### Relations Definition
79
+
80
+ ```typescript
81
+ // Relations
82
+ export const usersRelations = relations(users, ({ many }) => ({
83
+ items: many(items),
84
+ }));
85
+
86
+ export const itemsRelations = relations(items, ({ one, many }) => ({
87
+ author: one(users, {
88
+ fields: [items.authorId],
89
+ references: [users.id],
90
+ }),
91
+ tags: many(itemTags),
92
+ }));
93
+
94
+ export const itemTagsRelations = relations(itemTags, ({ one }) => ({
95
+ item: one(items, {
96
+ fields: [itemTags.itemId],
97
+ references: [items.id],
98
+ }),
99
+ tag: one(tags, {
100
+ fields: [itemTags.tagId],
101
+ references: [tags.id],
102
+ }),
103
+ }));
104
+ ```
105
+
106
+ ### Type Inference
107
+
108
+ ```typescript
109
+ // Infer types from schema
110
+ export type User = typeof users.$inferSelect;
111
+ export type NewUser = typeof users.$inferInsert;
112
+ export type Item = typeof items.$inferSelect;
113
+ export type NewItem = typeof items.$inferInsert;
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Query Patterns
119
+
120
+ ### Basic CRUD
121
+
122
+ ```typescript
123
+ import { eq, and, isNull, desc, sql } from 'drizzle-orm';
124
+
125
+ // Create
126
+ const [newItem] = await db
127
+ .insert(items)
128
+ .values({
129
+ title: 'New Item',
130
+ price: 100,
131
+ authorId: userId,
132
+ })
133
+ .returning();
134
+
135
+ // Read one
136
+ const item = await db.query.items.findFirst({
137
+ where: eq(items.id, itemId),
138
+ with: {
139
+ author: true,
140
+ tags: {
141
+ with: {
142
+ tag: true,
143
+ },
144
+ },
145
+ },
146
+ });
147
+
148
+ // Read many
149
+ const activeItems = await db.query.items.findMany({
150
+ where: and(
151
+ eq(items.status, 'active'),
152
+ isNull(items.deletedAt)
153
+ ),
154
+ orderBy: [desc(items.createdAt)],
155
+ limit: 10,
156
+ });
157
+
158
+ // Update
159
+ const [updated] = await db
160
+ .update(items)
161
+ .set({
162
+ title: 'Updated Title',
163
+ updatedAt: new Date(),
164
+ })
165
+ .where(eq(items.id, itemId))
166
+ .returning();
167
+
168
+ // Delete
169
+ await db
170
+ .delete(items)
171
+ .where(eq(items.id, itemId));
172
+ ```
173
+
174
+ ### Pagination
175
+
176
+ ```typescript
177
+ async function findPaginated(input: {
178
+ page: number;
179
+ pageSize: number;
180
+ status?: string;
181
+ }) {
182
+ const { page, pageSize, status } = input;
183
+ const offset = (page - 1) * pageSize;
184
+
185
+ const conditions = [isNull(items.deletedAt)];
186
+ if (status) {
187
+ conditions.push(eq(items.status, status));
188
+ }
189
+
190
+ const [results, [{ count }]] = await Promise.all([
191
+ db.query.items.findMany({
192
+ where: and(...conditions),
193
+ limit: pageSize,
194
+ offset,
195
+ orderBy: [desc(items.createdAt)],
196
+ with: { author: true },
197
+ }),
198
+ db
199
+ .select({ count: sql<number>`count(*)` })
200
+ .from(items)
201
+ .where(and(...conditions)),
202
+ ]);
203
+
204
+ return {
205
+ data: results,
206
+ pagination: {
207
+ total: Number(count),
208
+ page,
209
+ pageSize,
210
+ totalPages: Math.ceil(Number(count) / pageSize),
211
+ },
212
+ };
213
+ }
214
+ ```
215
+
216
+ ### Soft Delete
217
+
218
+ ```typescript
219
+ // Soft delete
220
+ await db
221
+ .update(items)
222
+ .set({ deletedAt: new Date() })
223
+ .where(eq(items.id, itemId));
224
+
225
+ // Restore
226
+ await db
227
+ .update(items)
228
+ .set({ deletedAt: null })
229
+ .where(eq(items.id, itemId));
230
+
231
+ // Query non-deleted only
232
+ const activeItems = await db.query.items.findMany({
233
+ where: isNull(items.deletedAt),
234
+ });
235
+ ```
236
+
237
+ ### Transactions
238
+
239
+ ```typescript
240
+ // Transaction with rollback on error
241
+ const result = await db.transaction(async (tx) => {
242
+ // Create item
243
+ const [item] = await tx
244
+ .insert(items)
245
+ .values({ title: 'New', price: 100, authorId: userId })
246
+ .returning();
247
+
248
+ // Add tags
249
+ await tx.insert(itemTags).values(
250
+ tagIds.map((tagId) => ({
251
+ itemId: item.id,
252
+ tagId,
253
+ }))
254
+ );
255
+
256
+ // Update user stats
257
+ await tx
258
+ .update(users)
259
+ .set({
260
+ itemsCount: sql`${users.itemsCount} + 1`,
261
+ })
262
+ .where(eq(users.id, userId));
263
+
264
+ return item;
265
+ });
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Model Class Pattern
271
+
272
+ ```typescript
273
+ import { eq, and, isNull, desc, sql } from 'drizzle-orm';
274
+ import type { Database } from './client';
275
+
276
+ export class ItemModel {
277
+ constructor(private db: Database) {}
278
+
279
+ async findById(id: string) {
280
+ return this.db.query.items.findFirst({
281
+ where: and(eq(items.id, id), isNull(items.deletedAt)),
282
+ with: { author: true },
283
+ });
284
+ }
285
+
286
+ async findByAuthor(authorId: string) {
287
+ return this.db.query.items.findMany({
288
+ where: and(
289
+ eq(items.authorId, authorId),
290
+ isNull(items.deletedAt)
291
+ ),
292
+ orderBy: [desc(items.createdAt)],
293
+ });
294
+ }
295
+
296
+ async create(data: NewItem) {
297
+ const [item] = await this.db
298
+ .insert(items)
299
+ .values(data)
300
+ .returning();
301
+ return item;
302
+ }
303
+
304
+ async update(id: string, data: Partial<NewItem>) {
305
+ const [item] = await this.db
306
+ .update(items)
307
+ .set({ ...data, updatedAt: new Date() })
308
+ .where(eq(items.id, id))
309
+ .returning();
310
+ return item;
311
+ }
312
+
313
+ async softDelete(id: string) {
314
+ const [item] = await this.db
315
+ .update(items)
316
+ .set({ deletedAt: new Date() })
317
+ .where(eq(items.id, id))
318
+ .returning();
319
+ return item;
320
+ }
321
+ }
322
+ ```
323
+
324
+ ---
325
+
326
+ ## Migrations
327
+
328
+ ### Generate Migration
329
+
330
+ ```bash
331
+ # Generate migration from schema changes
332
+ pnpm drizzle-kit generate
333
+
334
+ # Apply migrations
335
+ pnpm drizzle-kit migrate
336
+
337
+ # Push schema directly (development only)
338
+ pnpm drizzle-kit push
339
+ ```
340
+
341
+ ### Migration File Example
342
+
343
+ ```sql
344
+ -- 0001_create_items.sql
345
+ CREATE TABLE IF NOT EXISTS "items" (
346
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
347
+ "title" varchar(255) NOT NULL,
348
+ "description" text,
349
+ "status" varchar(20) NOT NULL DEFAULT 'active',
350
+ "price" integer NOT NULL,
351
+ "author_id" uuid NOT NULL REFERENCES "users"("id") ON DELETE CASCADE,
352
+ "created_at" timestamp NOT NULL DEFAULT now(),
353
+ "updated_at" timestamp NOT NULL DEFAULT now(),
354
+ "deleted_at" timestamp
355
+ );
356
+
357
+ CREATE INDEX "idx_items_author" ON "items"("author_id");
358
+ CREATE INDEX "idx_items_status" ON "items"("status");
359
+
360
+ ALTER TABLE "items" ADD CONSTRAINT "check_price_positive"
361
+ CHECK ("price" > 0);
362
+ ```
363
+
364
+ ---
365
+
366
+ ## Database Client Setup
367
+
368
+ ```typescript
369
+ // client.ts
370
+ import { drizzle } from 'drizzle-orm/node-postgres';
371
+ import { Pool } from 'pg';
372
+ import * as schema from './schema';
373
+
374
+ const pool = new Pool({
375
+ connectionString: process.env.DATABASE_URL,
376
+ });
377
+
378
+ export const db = drizzle(pool, { schema });
379
+ export type Database = typeof db;
380
+ ```
381
+
382
+ ---
383
+
384
+ ## Testing
385
+
386
+ ```typescript
387
+ import { describe, it, expect, beforeEach, afterAll } from 'vitest';
388
+ import { drizzle } from 'drizzle-orm/node-postgres';
389
+ import { Pool } from 'pg';
390
+ import * as schema from '../schema';
391
+ import { ItemModel } from '../models/item.model';
392
+
393
+ const pool = new Pool({
394
+ connectionString: process.env.TEST_DATABASE_URL,
395
+ });
396
+ const db = drizzle(pool, { schema });
397
+
398
+ describe('ItemModel', () => {
399
+ const model = new ItemModel(db);
400
+
401
+ beforeEach(async () => {
402
+ await db.delete(schema.items);
403
+ });
404
+
405
+ afterAll(async () => {
406
+ await pool.end();
407
+ });
408
+
409
+ describe('create', () => {
410
+ it('should create item', async () => {
411
+ const item = await model.create({
412
+ title: 'Test Item',
413
+ price: 100,
414
+ authorId: testUserId,
415
+ });
416
+
417
+ expect(item.id).toBeDefined();
418
+ expect(item.title).toBe('Test Item');
419
+ });
420
+ });
421
+
422
+ describe('findById', () => {
423
+ it('should return null for non-existent', async () => {
424
+ const item = await model.findById('non-existent-id');
425
+ expect(item).toBeNull();
426
+ });
427
+ });
428
+ });
429
+ ```
430
+
431
+ ---
432
+
433
+ ## Best Practices
434
+
435
+ ### Good
436
+
437
+ - Use type inference from schema (`$inferSelect`, `$inferInsert`)
438
+ - Use `relations` for type-safe joins
439
+ - Use transactions for multi-step operations
440
+ - Use `returning()` to get inserted/updated data
441
+ - Use indexes for frequently queried columns
442
+
443
+ ### Bad
444
+
445
+ - Manually defining types that duplicate schema
446
+ - Using raw SQL when Drizzle has type-safe alternatives
447
+ - Forgetting to add indexes
448
+ - Not using transactions for related operations
449
+ - Ignoring soft delete patterns