red64-cli 0.1.0 → 0.3.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 (125) hide show
  1. package/README.md +1 -2
  2. package/dist/cli/parseArgs.d.ts.map +1 -1
  3. package/dist/cli/parseArgs.js +5 -0
  4. package/dist/cli/parseArgs.js.map +1 -1
  5. package/dist/components/init/CompleteStep.d.ts.map +1 -1
  6. package/dist/components/init/CompleteStep.js +2 -2
  7. package/dist/components/init/CompleteStep.js.map +1 -1
  8. package/dist/components/init/TestCheckStep.d.ts +16 -0
  9. package/dist/components/init/TestCheckStep.d.ts.map +1 -0
  10. package/dist/components/init/TestCheckStep.js +120 -0
  11. package/dist/components/init/TestCheckStep.js.map +1 -0
  12. package/dist/components/init/index.d.ts +1 -0
  13. package/dist/components/init/index.d.ts.map +1 -1
  14. package/dist/components/init/index.js +1 -0
  15. package/dist/components/init/index.js.map +1 -1
  16. package/dist/components/init/types.d.ts +9 -0
  17. package/dist/components/init/types.d.ts.map +1 -1
  18. package/dist/components/screens/InitScreen.d.ts.map +1 -1
  19. package/dist/components/screens/InitScreen.js +69 -6
  20. package/dist/components/screens/InitScreen.js.map +1 -1
  21. package/dist/components/screens/ListScreen.d.ts.map +1 -1
  22. package/dist/components/screens/ListScreen.js +28 -3
  23. package/dist/components/screens/ListScreen.js.map +1 -1
  24. package/dist/components/screens/StartScreen.d.ts.map +1 -1
  25. package/dist/components/screens/StartScreen.js +212 -13
  26. package/dist/components/screens/StartScreen.js.map +1 -1
  27. package/dist/components/ui/ArtifactsSidebar.d.ts +19 -0
  28. package/dist/components/ui/ArtifactsSidebar.d.ts.map +1 -0
  29. package/dist/components/ui/ArtifactsSidebar.js +51 -0
  30. package/dist/components/ui/ArtifactsSidebar.js.map +1 -0
  31. package/dist/components/ui/FeatureSidebar.d.ts.map +1 -1
  32. package/dist/components/ui/FeatureSidebar.js +1 -1
  33. package/dist/components/ui/FeatureSidebar.js.map +1 -1
  34. package/dist/components/ui/index.d.ts +1 -0
  35. package/dist/components/ui/index.d.ts.map +1 -1
  36. package/dist/components/ui/index.js +1 -0
  37. package/dist/components/ui/index.js.map +1 -1
  38. package/dist/services/ClaudeErrorDetector.js +3 -3
  39. package/dist/services/ClaudeErrorDetector.js.map +1 -1
  40. package/dist/services/ConfigService.d.ts +1 -0
  41. package/dist/services/ConfigService.d.ts.map +1 -1
  42. package/dist/services/ConfigService.js.map +1 -1
  43. package/dist/services/ProjectDetector.d.ts +28 -0
  44. package/dist/services/ProjectDetector.d.ts.map +1 -0
  45. package/dist/services/ProjectDetector.js +236 -0
  46. package/dist/services/ProjectDetector.js.map +1 -0
  47. package/dist/services/TestRunner.d.ts +46 -0
  48. package/dist/services/TestRunner.d.ts.map +1 -0
  49. package/dist/services/TestRunner.js +85 -0
  50. package/dist/services/TestRunner.js.map +1 -0
  51. package/dist/services/index.d.ts +2 -0
  52. package/dist/services/index.d.ts.map +1 -1
  53. package/dist/services/index.js +2 -0
  54. package/dist/services/index.js.map +1 -1
  55. package/dist/types/index.d.ts +13 -0
  56. package/dist/types/index.d.ts.map +1 -1
  57. package/dist/types/index.js.map +1 -1
  58. package/framework/.red64/settings/templates/specs/gap-analysis.md +163 -0
  59. package/framework/agents/claude/.claude/agents/red64/spec-impl.md +131 -2
  60. package/framework/agents/claude/.claude/agents/red64/validate-gap.md +13 -7
  61. package/framework/agents/claude/.claude/commands/red64/spec-impl.md +24 -0
  62. package/framework/agents/claude/.claude/commands/red64/validate-gap.md +4 -0
  63. package/framework/agents/codex/.codex/agents/red64/spec-impl.md +131 -2
  64. package/framework/agents/codex/.codex/agents/red64/validate-gap.md +13 -7
  65. package/framework/agents/codex/.codex/commands/red64/spec-impl.md +24 -0
  66. package/framework/agents/codex/.codex/commands/red64/validate-gap.md +4 -0
  67. package/framework/stacks/generic/feedback.md +80 -0
  68. package/framework/stacks/nextjs/accessibility.md +437 -0
  69. package/framework/stacks/nextjs/api.md +431 -0
  70. package/framework/stacks/nextjs/coding-style.md +282 -0
  71. package/framework/stacks/nextjs/commenting.md +226 -0
  72. package/framework/stacks/nextjs/components.md +411 -0
  73. package/framework/stacks/nextjs/conventions.md +333 -0
  74. package/framework/stacks/nextjs/css.md +310 -0
  75. package/framework/stacks/nextjs/error-handling.md +442 -0
  76. package/framework/stacks/nextjs/feedback.md +124 -0
  77. package/framework/stacks/nextjs/migrations.md +332 -0
  78. package/framework/stacks/nextjs/models.md +362 -0
  79. package/framework/stacks/nextjs/queries.md +410 -0
  80. package/framework/stacks/nextjs/responsive.md +338 -0
  81. package/framework/stacks/nextjs/tech-stack.md +177 -0
  82. package/framework/stacks/nextjs/test-writing.md +475 -0
  83. package/framework/stacks/nextjs/validation.md +467 -0
  84. package/framework/stacks/python/api.md +468 -0
  85. package/framework/stacks/python/authentication.md +342 -0
  86. package/framework/stacks/python/code-quality.md +283 -0
  87. package/framework/stacks/python/code-refactoring.md +315 -0
  88. package/framework/stacks/python/coding-style.md +462 -0
  89. package/framework/stacks/python/conventions.md +399 -0
  90. package/framework/stacks/python/error-handling.md +512 -0
  91. package/framework/stacks/python/feedback.md +92 -0
  92. package/framework/stacks/python/implement-ai-llm.md +468 -0
  93. package/framework/stacks/python/migrations.md +388 -0
  94. package/framework/stacks/python/models.md +399 -0
  95. package/framework/stacks/python/python.md +232 -0
  96. package/framework/stacks/python/queries.md +451 -0
  97. package/framework/stacks/python/structure.md +245 -58
  98. package/framework/stacks/python/tech.md +92 -35
  99. package/framework/stacks/python/testing.md +380 -0
  100. package/framework/stacks/python/validation.md +471 -0
  101. package/framework/stacks/rails/authentication.md +176 -0
  102. package/framework/stacks/rails/code-quality.md +287 -0
  103. package/framework/stacks/rails/code-refactoring.md +299 -0
  104. package/framework/stacks/rails/feedback.md +130 -0
  105. package/framework/stacks/rails/implement-ai-llm-with-rubyllm.md +342 -0
  106. package/framework/stacks/rails/rails.md +301 -0
  107. package/framework/stacks/rails/rails8-best-practices.md +498 -0
  108. package/framework/stacks/rails/rails8-css.md +573 -0
  109. package/framework/stacks/rails/structure.md +140 -0
  110. package/framework/stacks/rails/tech.md +108 -0
  111. package/framework/stacks/react/code-quality.md +521 -0
  112. package/framework/stacks/react/components.md +625 -0
  113. package/framework/stacks/react/data-fetching.md +586 -0
  114. package/framework/stacks/react/feedback.md +110 -0
  115. package/framework/stacks/react/forms.md +694 -0
  116. package/framework/stacks/react/performance.md +640 -0
  117. package/framework/stacks/react/product.md +22 -9
  118. package/framework/stacks/react/state-management.md +472 -0
  119. package/framework/stacks/react/structure.md +351 -44
  120. package/framework/stacks/react/tech.md +219 -30
  121. package/framework/stacks/react/testing.md +690 -0
  122. package/package.json +1 -1
  123. package/framework/stacks/node/product.md +0 -27
  124. package/framework/stacks/node/structure.md +0 -82
  125. package/framework/stacks/node/tech.md +0 -63
@@ -0,0 +1,410 @@
1
+ # Prisma Query Patterns
2
+
3
+ Best practices for Prisma Client queries, N+1 prevention, transactions, pagination, and performance optimization.
4
+
5
+ ---
6
+
7
+ ## Philosophy
8
+
9
+ - **Select what you need**: Never fetch entire models when a subset of fields suffices
10
+ - **Prevent N+1 at write time**: Use `include` and `select` deliberately, not as an afterthought
11
+ - **Type safety end-to-end**: Let Prisma's generated types flow from query to UI component
12
+ - **Connection awareness**: Serverless and edge runtimes need connection pooling
13
+
14
+ ---
15
+
16
+ ## Prisma Client Singleton
17
+
18
+ ```typescript
19
+ // lib/prisma.ts
20
+ import { PrismaClient } from "@prisma/client";
21
+
22
+ const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
23
+
24
+ export const prisma =
25
+ globalForPrisma.prisma ||
26
+ new PrismaClient({
27
+ log: process.env.NODE_ENV === "development" ? ["query", "warn", "error"] : ["error"],
28
+ });
29
+
30
+ if (process.env.NODE_ENV !== "production") {
31
+ globalForPrisma.prisma = prisma;
32
+ }
33
+ ```
34
+
35
+ **Why globalThis**: Next.js dev server clears module cache on every request. Without this, each request creates a new `PrismaClient`, exhausting the connection pool.
36
+
37
+ ---
38
+
39
+ ## Basic Queries
40
+
41
+ ### Find Unique
42
+
43
+ ```typescript
44
+ const user = await prisma.user.findUnique({
45
+ where: { id: userId },
46
+ });
47
+
48
+ // With specific fields
49
+ const user = await prisma.user.findUnique({
50
+ where: { id: userId },
51
+ select: { id: true, name: true, email: true },
52
+ });
53
+ ```
54
+
55
+ ### Find Many with Filtering
56
+
57
+ ```typescript
58
+ const publishedPosts = await prisma.post.findMany({
59
+ where: {
60
+ status: "PUBLISHED",
61
+ deletedAt: null,
62
+ author: { isActive: true },
63
+ },
64
+ orderBy: { createdAt: "desc" },
65
+ take: 20,
66
+ });
67
+ ```
68
+
69
+ ### Create
70
+
71
+ ```typescript
72
+ const user = await prisma.user.create({
73
+ data: {
74
+ email: "jane@example.com",
75
+ name: "Jane Doe",
76
+ hashedPassword: await hash(password),
77
+ },
78
+ });
79
+ ```
80
+
81
+ ### Update
82
+
83
+ ```typescript
84
+ const user = await prisma.user.update({
85
+ where: { id: userId },
86
+ data: { name: "Updated Name" },
87
+ });
88
+ ```
89
+
90
+ ### Upsert
91
+
92
+ ```typescript
93
+ const user = await prisma.user.upsert({
94
+ where: { email: "jane@example.com" },
95
+ create: { email: "jane@example.com", name: "Jane", hashedPassword: hash },
96
+ update: { name: "Jane" },
97
+ });
98
+ ```
99
+
100
+ ### Delete
101
+
102
+ ```typescript
103
+ // Hard delete
104
+ await prisma.user.delete({ where: { id: userId } });
105
+
106
+ // Soft delete (preferred)
107
+ await prisma.user.update({
108
+ where: { id: userId },
109
+ data: { deletedAt: new Date() },
110
+ });
111
+ ```
112
+
113
+ ---
114
+
115
+ ## N+1 Prevention
116
+
117
+ ### The Problem
118
+
119
+ ```typescript
120
+ // BAD: N+1 - one query per post to fetch author
121
+ const posts = await prisma.post.findMany();
122
+ for (const post of posts) {
123
+ const author = await prisma.user.findUnique({ where: { id: post.authorId } });
124
+ // This runs N additional queries
125
+ }
126
+ ```
127
+
128
+ ### Solution: include
129
+
130
+ ```typescript
131
+ // GOOD: Single query with JOIN
132
+ const posts = await prisma.post.findMany({
133
+ include: {
134
+ author: { select: { id: true, name: true, avatarUrl: true } },
135
+ },
136
+ });
137
+ // posts[0].author.name is available
138
+ ```
139
+
140
+ ### Solution: select (Leaner)
141
+
142
+ ```typescript
143
+ // BETTER: Only fetch exactly what you need
144
+ const posts = await prisma.post.findMany({
145
+ select: {
146
+ id: true,
147
+ title: true,
148
+ createdAt: true,
149
+ author: { select: { name: true, avatarUrl: true } },
150
+ },
151
+ });
152
+ ```
153
+
154
+ ### Nested Includes
155
+
156
+ ```typescript
157
+ // Fetch post with author and comments (including comment authors)
158
+ const post = await prisma.post.findUnique({
159
+ where: { id: postId },
160
+ include: {
161
+ author: { select: { id: true, name: true } },
162
+ comments: {
163
+ where: { deletedAt: null },
164
+ orderBy: { createdAt: "asc" },
165
+ include: {
166
+ author: { select: { id: true, name: true, avatarUrl: true } },
167
+ },
168
+ },
169
+ },
170
+ });
171
+ ```
172
+
173
+ ### When to Use include vs select
174
+
175
+ | Approach | Use When |
176
+ |---|---|
177
+ | `include` | Need all model fields plus relations |
178
+ | `select` | Need specific fields only (API responses, lists) |
179
+
180
+ **Rule**: Default to `select` for list queries. Use `include` for detail views where you need the full model.
181
+
182
+ ---
183
+
184
+ ## Pagination
185
+
186
+ ### Offset-Based (Simple, for Admin UIs)
187
+
188
+ ```typescript
189
+ interface PaginationParams {
190
+ page: number;
191
+ limit: number;
192
+ }
193
+
194
+ async function getUsers({ page, limit }: PaginationParams) {
195
+ const [users, total] = await Promise.all([
196
+ prisma.user.findMany({
197
+ skip: (page - 1) * limit,
198
+ take: limit,
199
+ orderBy: { createdAt: "desc" },
200
+ select: { id: true, name: true, email: true, createdAt: true },
201
+ }),
202
+ prisma.user.count(),
203
+ ]);
204
+
205
+ return {
206
+ items: users,
207
+ total,
208
+ page,
209
+ limit,
210
+ totalPages: Math.ceil(total / limit),
211
+ };
212
+ }
213
+ ```
214
+
215
+ ### Cursor-Based (Scalable, for Feeds)
216
+
217
+ ```typescript
218
+ async function getPosts(cursor?: string, limit = 20) {
219
+ const posts = await prisma.post.findMany({
220
+ take: limit + 1, // Fetch one extra to check if there's a next page
221
+ ...(cursor && {
222
+ cursor: { id: cursor },
223
+ skip: 1, // Skip the cursor itself
224
+ }),
225
+ orderBy: { createdAt: "desc" },
226
+ select: {
227
+ id: true,
228
+ title: true,
229
+ createdAt: true,
230
+ author: { select: { name: true } },
231
+ },
232
+ });
233
+
234
+ const hasNext = posts.length > limit;
235
+ const items = hasNext ? posts.slice(0, -1) : posts;
236
+
237
+ return {
238
+ items,
239
+ hasNext,
240
+ nextCursor: hasNext ? items[items.length - 1]?.id : null,
241
+ };
242
+ }
243
+ ```
244
+
245
+ ### When to Use Each
246
+
247
+ | Type | Use Case | Tradeoff |
248
+ |---|---|---|
249
+ | Offset | Admin dashboards, small datasets | Slow on large tables (COUNT + OFFSET) |
250
+ | Cursor | Infinite scroll, feeds, public APIs | Cannot jump to arbitrary page |
251
+
252
+ ---
253
+
254
+ ## Transactions
255
+
256
+ ### Interactive Transactions
257
+
258
+ ```typescript
259
+ async function transferCredits(fromId: string, toId: string, amount: number) {
260
+ return prisma.$transaction(async (tx) => {
261
+ const sender = await tx.user.findUnique({ where: { id: fromId } });
262
+ if (!sender || sender.credits < amount) {
263
+ throw new Error("Insufficient credits");
264
+ }
265
+
266
+ await tx.user.update({
267
+ where: { id: fromId },
268
+ data: { credits: { decrement: amount } },
269
+ });
270
+
271
+ await tx.user.update({
272
+ where: { id: toId },
273
+ data: { credits: { increment: amount } },
274
+ });
275
+
276
+ return { fromId, toId, amount };
277
+ });
278
+ }
279
+ ```
280
+
281
+ ### Batch Transactions
282
+
283
+ ```typescript
284
+ // Multiple operations in a single transaction
285
+ const [user, post] = await prisma.$transaction([
286
+ prisma.user.create({ data: userData }),
287
+ prisma.post.create({ data: postData }),
288
+ ]);
289
+ ```
290
+
291
+ ### Transaction Options
292
+
293
+ ```typescript
294
+ await prisma.$transaction(
295
+ async (tx) => { /* ... */ },
296
+ {
297
+ maxWait: 5000, // Max time to wait for a connection from the pool
298
+ timeout: 10000, // Max time for the transaction to complete
299
+ isolationLevel: "Serializable", // Strictest isolation
300
+ }
301
+ );
302
+ ```
303
+
304
+ ---
305
+
306
+ ## Filtering Patterns
307
+
308
+ ### Dynamic Filters
309
+
310
+ ```typescript
311
+ interface PostFilters {
312
+ status?: string;
313
+ authorId?: string;
314
+ search?: string;
315
+ from?: Date;
316
+ to?: Date;
317
+ }
318
+
319
+ async function getPosts(filters: PostFilters) {
320
+ const where: Prisma.PostWhereInput = {
321
+ deletedAt: null,
322
+ ...(filters.status && { status: filters.status as PostStatus }),
323
+ ...(filters.authorId && { authorId: filters.authorId }),
324
+ ...(filters.search && {
325
+ OR: [
326
+ { title: { contains: filters.search, mode: "insensitive" } },
327
+ { body: { contains: filters.search, mode: "insensitive" } },
328
+ ],
329
+ }),
330
+ ...(filters.from && { createdAt: { gte: filters.from } }),
331
+ ...(filters.to && { createdAt: { ...( filters.from ? { gte: filters.from } : {}), lte: filters.to } }),
332
+ };
333
+
334
+ return prisma.post.findMany({ where, orderBy: { createdAt: "desc" } });
335
+ }
336
+ ```
337
+
338
+ ### Sorting
339
+
340
+ ```typescript
341
+ type SortField = "createdAt" | "title" | "updatedAt";
342
+ type SortDirection = "asc" | "desc";
343
+
344
+ function buildOrderBy(sort: string): Prisma.PostOrderByWithRelationInput {
345
+ const desc = sort.startsWith("-");
346
+ const field = (desc ? sort.slice(1) : sort) as SortField;
347
+ return { [field]: desc ? "desc" : "asc" };
348
+ }
349
+ ```
350
+
351
+ ---
352
+
353
+ ## Raw Queries
354
+
355
+ ### When Prisma Client Is Not Enough
356
+
357
+ ```typescript
358
+ // Type-safe raw query with tagged template
359
+ const users = await prisma.$queryRaw<{ id: string; postCount: number }[]>`
360
+ SELECT u.id, COUNT(p.id)::int AS "postCount"
361
+ FROM users u
362
+ LEFT JOIN posts p ON p.author_id = u.id AND p.deleted_at IS NULL
363
+ GROUP BY u.id
364
+ HAVING COUNT(p.id) > ${minPosts}
365
+ ORDER BY "postCount" DESC
366
+ LIMIT ${limit}
367
+ `;
368
+ ```
369
+
370
+ **Rule**: Use raw queries only for complex aggregations, window functions, or CTEs that Prisma Client cannot express. Always use tagged templates (never string interpolation) to prevent SQL injection.
371
+
372
+ ---
373
+
374
+ ## Connection Pooling
375
+
376
+ ### Serverless (Vercel / Neon)
377
+
378
+ ```
379
+ # .env
380
+ DATABASE_URL="postgresql://user:pass@host/db?pgbouncer=true&connection_limit=1"
381
+ DIRECT_URL="postgresql://user:pass@direct-host/db"
382
+ ```
383
+
384
+ ```prisma
385
+ datasource db {
386
+ provider = "postgresql"
387
+ url = env("DATABASE_URL")
388
+ directUrl = env("DIRECT_URL")
389
+ }
390
+ ```
391
+
392
+ **Why**: `DATABASE_URL` goes through a connection pooler (PgBouncer/Neon) for query traffic. `directUrl` connects directly for migrations.
393
+
394
+ ---
395
+
396
+ ## Anti-Patterns
397
+
398
+ | Anti-Pattern | Problem | Correct Approach |
399
+ |---|---|---|
400
+ | Fetching all fields for list views | Over-fetching, slower queries | Use `select` for lists |
401
+ | No `include`/`select` on relations | N+1 queries at runtime | Include relations at query time |
402
+ | String interpolation in raw queries | SQL injection vulnerability | Use tagged template literals |
403
+ | No connection pooling in serverless | Connection exhaustion | Use PgBouncer or Neon pooler |
404
+ | Offset pagination on large tables | Slow due to OFFSET scan | Switch to cursor-based pagination |
405
+ | Transactions for single operations | Unnecessary overhead | Transactions only for multi-step operations |
406
+ | Count queries without filters | Full table scan | Always apply the same `where` clause to count |
407
+
408
+ ---
409
+
410
+ _Queries define performance. Select only what you need, prevent N+1 at write time, and let Prisma's types guarantee correctness._