@girardmedia/bootspring 3.3.2 → 3.4.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 (171) hide show
  1. package/assets/agents/accessibility-auditor.md +39 -0
  2. package/assets/agents/api-designer.md +40 -0
  3. package/assets/agents/auth-implementer.md +64 -0
  4. package/assets/agents/bug-hunter.md +42 -0
  5. package/assets/agents/bundle-analyzer.md +40 -0
  6. package/assets/agents/cache-optimizer.md +55 -0
  7. package/assets/agents/changelog-writer.md +55 -0
  8. package/assets/agents/ci-cd-builder.md +40 -0
  9. package/assets/agents/code-explainer.md +39 -0
  10. package/assets/agents/code-reviewer.md +39 -0
  11. package/assets/agents/cost-optimizer.md +57 -0
  12. package/assets/agents/cron-scheduler.md +51 -0
  13. package/assets/agents/data-seeder.md +56 -0
  14. package/assets/agents/database-architect.md +40 -0
  15. package/assets/agents/dependency-updater.md +40 -0
  16. package/assets/agents/deploy-checker.md +40 -0
  17. package/assets/agents/docker-optimizer.md +40 -0
  18. package/assets/agents/documentation-writer.md +40 -0
  19. package/assets/agents/email-builder.md +55 -0
  20. package/assets/agents/env-setup.md +40 -0
  21. package/assets/agents/error-handler.md +40 -0
  22. package/assets/agents/eslint-fixer.md +46 -0
  23. package/assets/agents/feature-flagger.md +69 -0
  24. package/assets/agents/git-detective.md +39 -0
  25. package/assets/agents/graphql-builder.md +60 -0
  26. package/assets/agents/incident-responder.md +59 -0
  27. package/assets/agents/log-analyzer.md +39 -0
  28. package/assets/agents/migration-planner.md +41 -0
  29. package/assets/agents/monorepo-navigator.md +39 -0
  30. package/assets/agents/nextjs-expert.md +57 -0
  31. package/assets/agents/notification-builder.md +56 -0
  32. package/assets/agents/onboarding-guide.md +39 -0
  33. package/assets/agents/performance-profiler.md +40 -0
  34. package/assets/agents/prisma-expert.md +57 -0
  35. package/assets/agents/rate-limiter.md +58 -0
  36. package/assets/agents/react-expert.md +58 -0
  37. package/assets/agents/refactorer.md +42 -0
  38. package/assets/agents/regex-builder.md +46 -0
  39. package/assets/agents/release-manager.md +40 -0
  40. package/assets/agents/s3-manager.md +58 -0
  41. package/assets/agents/schema-validator.md +40 -0
  42. package/assets/agents/search-builder.md +62 -0
  43. package/assets/agents/security-auditor.md +39 -0
  44. package/assets/agents/sitemap-generator.md +53 -0
  45. package/assets/agents/stripe-integrator.md +59 -0
  46. package/assets/agents/tailwind-expert.md +55 -0
  47. package/assets/agents/tech-debt-tracker.md +39 -0
  48. package/assets/agents/test-writer.md +42 -0
  49. package/assets/agents/type-fixer.md +45 -0
  50. package/assets/agents/webhook-builder.md +54 -0
  51. package/assets/rules/cpp.md +53 -0
  52. package/assets/rules/css.md +52 -0
  53. package/assets/rules/go.md +50 -0
  54. package/assets/rules/html.md +52 -0
  55. package/assets/rules/java.md +51 -0
  56. package/assets/rules/kotlin.md +50 -0
  57. package/assets/rules/php.md +51 -0
  58. package/assets/rules/python.md +51 -0
  59. package/assets/rules/ruby.md +51 -0
  60. package/assets/rules/rust.md +49 -0
  61. package/assets/rules/shell.md +52 -0
  62. package/assets/rules/sql.md +49 -0
  63. package/assets/rules/swift.md +50 -0
  64. package/assets/rules/typescript.md +52 -0
  65. package/assets/rules/yaml-json.md +51 -0
  66. package/assets/skills/accessibility.md +210 -0
  67. package/assets/skills/agent-patterns.md +387 -0
  68. package/assets/skills/ai-integration.md +263 -0
  69. package/assets/skills/animation-patterns.md +224 -0
  70. package/assets/skills/api-design.md +218 -0
  71. package/assets/skills/api-gateway.md +341 -0
  72. package/assets/skills/api-versioning.md +226 -0
  73. package/assets/skills/astro-patterns.md +233 -0
  74. package/assets/skills/auth-patterns.md +248 -0
  75. package/assets/skills/aws-patterns.md +171 -0
  76. package/assets/skills/background-jobs.md +162 -0
  77. package/assets/skills/browser-extensions.md +309 -0
  78. package/assets/skills/caching-patterns.md +253 -0
  79. package/assets/skills/ci-cd.md +251 -0
  80. package/assets/skills/cli-development.md +296 -0
  81. package/assets/skills/code-review.md +185 -0
  82. package/assets/skills/cron-patterns.md +327 -0
  83. package/assets/skills/data-fetching.md +231 -0
  84. package/assets/skills/database-migrations.md +346 -0
  85. package/assets/skills/database-patterns.md +219 -0
  86. package/assets/skills/debugging.md +281 -0
  87. package/assets/skills/design-system.md +289 -0
  88. package/assets/skills/django-patterns.md +182 -0
  89. package/assets/skills/docker-patterns.md +235 -0
  90. package/assets/skills/e2e-testing.md +287 -0
  91. package/assets/skills/edge-computing.md +268 -0
  92. package/assets/skills/electron-patterns.md +266 -0
  93. package/assets/skills/email-templates.md +206 -0
  94. package/assets/skills/error-handling.md +265 -0
  95. package/assets/skills/event-driven.md +232 -0
  96. package/assets/skills/express-patterns.md +239 -0
  97. package/assets/skills/fastapi-patterns.md +198 -0
  98. package/assets/skills/feature-flags.md +212 -0
  99. package/assets/skills/figma-to-code.md +298 -0
  100. package/assets/skills/file-upload.md +228 -0
  101. package/assets/skills/forms-patterns.md +264 -0
  102. package/assets/skills/gcp-patterns.md +189 -0
  103. package/assets/skills/git-workflow.md +187 -0
  104. package/assets/skills/golang-patterns.md +185 -0
  105. package/assets/skills/graphql-patterns.md +244 -0
  106. package/assets/skills/i18n-patterns.md +172 -0
  107. package/assets/skills/image-processing.md +350 -0
  108. package/assets/skills/java-springboot.md +226 -0
  109. package/assets/skills/kotlin-patterns.md +207 -0
  110. package/assets/skills/kubernetes-patterns.md +326 -0
  111. package/assets/skills/laravel-patterns.md +261 -0
  112. package/assets/skills/llm-fine-tuning.md +335 -0
  113. package/assets/skills/load-testing.md +303 -0
  114. package/assets/skills/logging-observability.md +228 -0
  115. package/assets/skills/markdown-processing.md +318 -0
  116. package/assets/skills/mcp-server-patterns.md +292 -0
  117. package/assets/skills/microservices.md +272 -0
  118. package/assets/skills/migration-patterns.md +239 -0
  119. package/assets/skills/mongodb-patterns.md +189 -0
  120. package/assets/skills/monorepo-patterns.md +287 -0
  121. package/assets/skills/nextjs-app-router.md +237 -0
  122. package/assets/skills/notification-patterns.md +348 -0
  123. package/assets/skills/oauth-patterns.md +246 -0
  124. package/assets/skills/payment-integration.md +222 -0
  125. package/assets/skills/pdf-generation.md +307 -0
  126. package/assets/skills/performance-optimization.md +277 -0
  127. package/assets/skills/php-patterns.md +210 -0
  128. package/assets/skills/prisma-patterns.md +241 -0
  129. package/assets/skills/prompt-engineering.md +193 -0
  130. package/assets/skills/pwa-patterns.md +247 -0
  131. package/assets/skills/python-patterns.md +158 -0
  132. package/assets/skills/python-testing.md +172 -0
  133. package/assets/skills/queue-patterns.md +295 -0
  134. package/assets/skills/rag-patterns.md +159 -0
  135. package/assets/skills/rate-limiting.md +319 -0
  136. package/assets/skills/react-components.md +201 -0
  137. package/assets/skills/react-native-patterns.md +299 -0
  138. package/assets/skills/real-time-patterns.md +181 -0
  139. package/assets/skills/redis-patterns.md +188 -0
  140. package/assets/skills/refactoring.md +218 -0
  141. package/assets/skills/regex-patterns.md +191 -0
  142. package/assets/skills/remix-patterns.md +262 -0
  143. package/assets/skills/responsive-design.md +199 -0
  144. package/assets/skills/ruby-rails-patterns.md +178 -0
  145. package/assets/skills/rust-patterns.md +211 -0
  146. package/assets/skills/search-patterns.md +227 -0
  147. package/assets/skills/security-hardening.md +237 -0
  148. package/assets/skills/seo-patterns.md +179 -0
  149. package/assets/skills/serverless-patterns.md +223 -0
  150. package/assets/skills/sql-optimization.md +154 -0
  151. package/assets/skills/state-management.md +254 -0
  152. package/assets/skills/storybook-patterns.md +330 -0
  153. package/assets/skills/svelte-patterns.md +258 -0
  154. package/assets/skills/swift-patterns.md +227 -0
  155. package/assets/skills/tailwind-patterns.md +272 -0
  156. package/assets/skills/tdd-workflow.md +199 -0
  157. package/assets/skills/terraform-patterns.md +270 -0
  158. package/assets/skills/testing-react.md +240 -0
  159. package/assets/skills/testing-vitest.md +232 -0
  160. package/assets/skills/typescript-strict.md +159 -0
  161. package/assets/skills/video-processing.md +340 -0
  162. package/assets/skills/vue-patterns.md +247 -0
  163. package/assets/skills/web-workers.md +327 -0
  164. package/assets/skills/webhooks-patterns.md +283 -0
  165. package/assets/skills/websocket-patterns.md +306 -0
  166. package/dist/cli/index.js +941 -958
  167. package/dist/core/index.d.ts +341 -11
  168. package/dist/core.js +58 -95
  169. package/dist/mcp/index.d.ts +33 -1
  170. package/dist/mcp-server.js +177 -255
  171. package/package.json +4 -1
@@ -0,0 +1,241 @@
1
+ ---
2
+ name: prisma-patterns
3
+ description: Use Prisma ORM effectively — relations, transactions, raw queries, migrations, and soft deletes.
4
+ ---
5
+
6
+ # Prisma ORM Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns when using Prisma as your database layer. Prisma's type
11
+ safety is powerful but has sharp edges around performance, transactions, and
12
+ advanced queries. These patterns keep you out of trouble as your schema grows.
13
+
14
+ ## How It Works
15
+
16
+ ### 1. Relations — Explicit Over Implicit
17
+
18
+ Define both sides of every relation. Use `@relation` with named references when
19
+ a model has multiple relations to the same table.
20
+
21
+ ```prisma
22
+ model User {
23
+ id String @id @default(cuid())
24
+ email String @unique
25
+ posts Post[] @relation("author")
26
+ reviews Post[] @relation("reviewer")
27
+ profile Profile?
28
+ }
29
+
30
+ model Post {
31
+ id String @id @default(cuid())
32
+ authorId String
33
+ author User @relation("author", fields: [authorId], references: [id])
34
+ reviewerId String?
35
+ reviewer User? @relation("reviewer", fields: [reviewerId], references: [id])
36
+ }
37
+
38
+ model Profile {
39
+ id String @id @default(cuid())
40
+ userId String @unique
41
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
42
+ bio String @default("")
43
+ }
44
+ ```
45
+
46
+ Always set `onDelete` explicitly. The default is `SetNull` for optional relations
47
+ and `Restrict` for required ones — both can surprise you.
48
+
49
+ ### 2. Select Only What You Need
50
+
51
+ Avoid fetching entire objects when you need two fields.
52
+
53
+ ```typescript
54
+ // Bad — fetches every column
55
+ const users = await prisma.user.findMany({ include: { posts: true } });
56
+
57
+ // Good — fetches only what the API response needs
58
+ const users = await prisma.user.findMany({
59
+ select: {
60
+ id: true,
61
+ email: true,
62
+ posts: {
63
+ select: { id: true, title: true },
64
+ where: { published: true },
65
+ take: 5,
66
+ orderBy: { createdAt: 'desc' },
67
+ },
68
+ },
69
+ });
70
+ ```
71
+
72
+ ### 3. Transactions
73
+
74
+ Use interactive transactions for multi-step operations that must be atomic.
75
+
76
+ ```typescript
77
+ const [order, payment] = await prisma.$transaction(async (tx) => {
78
+ const order = await tx.order.create({
79
+ data: { userId, items: { create: lineItems } },
80
+ });
81
+
82
+ const payment = await tx.payment.create({
83
+ data: {
84
+ orderId: order.id,
85
+ amount: calculateTotal(lineItems),
86
+ status: 'pending',
87
+ },
88
+ });
89
+
90
+ // If this throws, both order and payment are rolled back
91
+ await chargePaymentProvider(payment);
92
+
93
+ return [order, payment];
94
+ }, {
95
+ maxWait: 5000, // max time to acquire a connection
96
+ timeout: 10000, // max transaction duration
97
+ });
98
+ ```
99
+
100
+ For simple batch writes, use the array form:
101
+
102
+ ```typescript
103
+ await prisma.$transaction([
104
+ prisma.user.update({ where: { id }, data: { credits: { decrement: 10 } } }),
105
+ prisma.auditLog.create({ data: { action: 'credit_deduction', userId: id } }),
106
+ ]);
107
+ ```
108
+
109
+ ### 4. Raw Queries for Complex Operations
110
+
111
+ When Prisma's query builder can't express what you need.
112
+
113
+ ```typescript
114
+ // Upsert with conflict handling
115
+ await prisma.$executeRaw`
116
+ INSERT INTO usage (user_id, month, api_calls)
117
+ VALUES (${userId}, ${month}, 1)
118
+ ON CONFLICT (user_id, month)
119
+ DO UPDATE SET api_calls = usage.api_calls + 1
120
+ `;
121
+
122
+ // Complex aggregate
123
+ const stats = await prisma.$queryRaw<{ day: Date; count: bigint }[]>`
124
+ SELECT date_trunc('day', created_at) AS day, COUNT(*) AS count
125
+ FROM orders
126
+ WHERE created_at > ${startDate}
127
+ GROUP BY day
128
+ ORDER BY day
129
+ `;
130
+
131
+ // Note: $queryRaw returns bigint for COUNT — convert to Number if safe
132
+ ```
133
+
134
+ Always use tagged template literals (not string concatenation) for SQL injection
135
+ protection.
136
+
137
+ ### 5. Migrations Workflow
138
+
139
+ ```bash
140
+ # Development: create and apply migration
141
+ npx prisma migrate dev --name add_user_profile
142
+
143
+ # Production: apply pending migrations (no interactive prompts)
144
+ npx prisma migrate deploy
145
+
146
+ # Reset dev database (drops all data)
147
+ npx prisma migrate reset
148
+
149
+ # Generate client after schema changes
150
+ npx prisma generate
151
+ ```
152
+
153
+ Name migrations descriptively: `add_user_profile`, `make_email_unique`,
154
+ `add_order_status_index`.
155
+
156
+ ### 6. Seeding
157
+
158
+ ```typescript
159
+ // prisma/seed.ts
160
+ import { PrismaClient } from '@prisma/client';
161
+ const prisma = new PrismaClient();
162
+
163
+ async function main() {
164
+ await prisma.user.upsert({
165
+ where: { email: 'admin@example.com' },
166
+ update: {},
167
+ create: {
168
+ email: 'admin@example.com',
169
+ name: 'Admin',
170
+ role: 'ADMIN',
171
+ },
172
+ });
173
+ }
174
+
175
+ main()
176
+ .catch(console.error)
177
+ .finally(() => prisma.$disconnect());
178
+ ```
179
+
180
+ Use `upsert` in seeds so they're idempotent — safe to run multiple times.
181
+
182
+ ### 7. Soft Deletes with Middleware
183
+
184
+ ```typescript
185
+ // Middleware approach
186
+ prisma.$use(async (params, next) => {
187
+ if (params.model === 'Post') {
188
+ if (params.action === 'delete') {
189
+ params.action = 'update';
190
+ params.args.data = { deletedAt: new Date() };
191
+ }
192
+ if (params.action === 'findMany') {
193
+ params.args.where = { ...params.args.where, deletedAt: null };
194
+ }
195
+ }
196
+ return next(params);
197
+ });
198
+ ```
199
+
200
+ For new projects, prefer Prisma Client Extensions over middleware:
201
+
202
+ ```typescript
203
+ const xprisma = prisma.$extends({
204
+ query: {
205
+ post: {
206
+ async delete({ args, query }) {
207
+ return prisma.post.update({
208
+ ...args,
209
+ data: { deletedAt: new Date() },
210
+ });
211
+ },
212
+ },
213
+ },
214
+ });
215
+ ```
216
+
217
+ ### 8. Indexes for Performance
218
+
219
+ ```prisma
220
+ model Order {
221
+ id String @id @default(cuid())
222
+ userId String
223
+ status String
224
+ createdAt DateTime @default(now())
225
+
226
+ @@index([userId, status]) // composite for filtered user queries
227
+ @@index([createdAt(sort: Desc)]) // sorted listing
228
+ }
229
+ ```
230
+
231
+ ## Checklist
232
+
233
+ - [ ] Every relation has explicit `onDelete` behavior
234
+ - [ ] List queries use `select` instead of `include` when possible
235
+ - [ ] Multi-step mutations use `$transaction` with appropriate timeouts
236
+ - [ ] Raw queries use tagged template literals, never string concatenation
237
+ - [ ] Migrations have descriptive names matching the change
238
+ - [ ] Seed script is idempotent (uses `upsert`)
239
+ - [ ] Composite indexes exist for common query patterns (check with `EXPLAIN`)
240
+ - [ ] `prisma generate` runs in CI after schema changes
241
+ - [ ] Soft deletes are implemented via client extension, not scattered `where` clauses
@@ -0,0 +1,193 @@
1
+ ---
2
+ name: prompt-engineering
3
+ description: Write effective LLM prompts with system instructions, few-shot examples, chain of thought, structured output, and guardrails.
4
+ ---
5
+
6
+ # Prompt Engineering
7
+
8
+ ## When to Use
9
+
10
+ Apply when building any LLM-powered feature — chatbots, code generation,
11
+ classification, extraction, summarization, or tool-using agents. Good prompts
12
+ are the difference between a demo and a product.
13
+
14
+ ## How It Works
15
+
16
+ ### 1. System Prompts
17
+
18
+ Set identity, constraints, and output format upfront. Be explicit about what
19
+ the model should and should not do.
20
+
21
+ ```typescript
22
+ const systemPrompt = `You are a senior code reviewer for a TypeScript monorepo.
23
+
24
+ Rules:
25
+ - Focus on bugs, security issues, and performance problems
26
+ - Ignore style preferences already handled by ESLint
27
+ - Rate severity as critical/high/medium/low
28
+ - If the code is correct, say "LGTM" with no further commentary
29
+ - Never suggest adding comments to obvious code
30
+
31
+ Output format: JSON array of { file, line, severity, issue, suggestion }`;
32
+ ```
33
+
34
+ Key principles: put the most important instructions first, use imperative
35
+ language, define the output schema explicitly, and state what to do when
36
+ edge cases arise.
37
+
38
+ ### 2. Few-Shot Examples
39
+
40
+ Show the model exactly what you want with 2-5 input/output pairs.
41
+
42
+ ```typescript
43
+ const messages = [
44
+ { role: 'system', content: 'Extract structured data from invoice text.' },
45
+ { role: 'user', content: 'Invoice #1042 from Acme Corp, dated Jan 15 2025, total $3,450.00' },
46
+ { role: 'assistant', content: JSON.stringify({
47
+ number: '1042', vendor: 'Acme Corp',
48
+ date: '2025-01-15', total: 3450.00, currency: 'USD'
49
+ })},
50
+ { role: 'user', content: 'Inv 2087 - TechFlow Solutions, 2025-03-22, EUR 12,800' },
51
+ { role: 'assistant', content: JSON.stringify({
52
+ number: '2087', vendor: 'TechFlow Solutions',
53
+ date: '2025-03-22', total: 12800.00, currency: 'EUR'
54
+ })},
55
+ { role: 'user', content: actualInvoiceText },
56
+ ];
57
+ ```
58
+
59
+ Choose diverse examples that cover edge cases. Order matters — put the most
60
+ representative example last, closest to the actual input.
61
+
62
+ ### 3. Chain of Thought
63
+
64
+ Force step-by-step reasoning for complex tasks. The model is more accurate
65
+ when it shows its work.
66
+
67
+ ```typescript
68
+ const prompt = `Analyze this error log and determine the root cause.
69
+
70
+ Think step by step:
71
+ 1. Identify the first error in the chain (not symptoms)
72
+ 2. Check timestamps to establish the sequence
73
+ 3. Look for resource exhaustion or dependency failures
74
+ 4. State the root cause in one sentence
75
+ 5. Suggest a fix
76
+
77
+ Error log:
78
+ ${errorLog}`;
79
+ ```
80
+
81
+ For classification tasks, use "think step by step, then give your final answer
82
+ on the last line as: ANSWER: <category>". This makes parsing reliable while
83
+ keeping reasoning quality.
84
+
85
+ ### 4. Structured Output
86
+
87
+ Use JSON mode or tool calls to guarantee parseable output.
88
+
89
+ ```typescript
90
+ // OpenAI JSON mode
91
+ const response = await openai.chat.completions.create({
92
+ model: 'gpt-4o',
93
+ response_format: { type: 'json_object' },
94
+ messages: [{ role: 'system', content: 'Respond in JSON with keys: summary, sentiment, topics[]' }],
95
+ });
96
+
97
+ // Anthropic tool-use for structured output
98
+ const response = await anthropic.messages.create({
99
+ model: 'claude-sonnet-4-20250514',
100
+ tools: [{
101
+ name: 'extract_data',
102
+ description: 'Extract structured fields from text',
103
+ input_schema: {
104
+ type: 'object',
105
+ properties: {
106
+ sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
107
+ topics: { type: 'array', items: { type: 'string' } },
108
+ confidence: { type: 'number', minimum: 0, maximum: 1 },
109
+ },
110
+ required: ['sentiment', 'topics', 'confidence'],
111
+ },
112
+ }],
113
+ tool_choice: { type: 'tool', name: 'extract_data' },
114
+ messages,
115
+ });
116
+ ```
117
+
118
+ ### 5. Tool Use / Function Calling
119
+
120
+ Give the model access to real capabilities instead of asking it to generate
121
+ code or URLs.
122
+
123
+ ```typescript
124
+ const tools = [
125
+ {
126
+ name: 'search_docs',
127
+ description: 'Search internal documentation. Use when the user asks about product features or policies.',
128
+ input_schema: {
129
+ type: 'object',
130
+ properties: { query: { type: 'string', description: 'Search query, 3-10 words' } },
131
+ required: ['query'],
132
+ },
133
+ },
134
+ {
135
+ name: 'create_ticket',
136
+ description: 'Create a support ticket. Only use after confirming the issue with the user.',
137
+ input_schema: {
138
+ type: 'object',
139
+ properties: {
140
+ title: { type: 'string' },
141
+ priority: { type: 'string', enum: ['low', 'medium', 'high', 'urgent'] },
142
+ },
143
+ required: ['title', 'priority'],
144
+ },
145
+ },
146
+ ];
147
+ ```
148
+
149
+ Write tool descriptions that say when to use (and when NOT to use) each tool.
150
+
151
+ ### 6. Guardrails
152
+
153
+ Prevent harmful, off-topic, or low-quality outputs.
154
+
155
+ ```typescript
156
+ // Input guardrail — reject prompt injection attempts
157
+ const inputCheck = await classify(userInput, ['benign', 'injection', 'off_topic']);
158
+ if (inputCheck !== 'benign') return { error: 'Request not supported' };
159
+
160
+ // Output guardrail — validate before returning to user
161
+ const output = await generateResponse(userInput);
162
+ const validation = zodSchema.safeParse(JSON.parse(output));
163
+ if (!validation.success) {
164
+ // Retry once with validation errors in the prompt
165
+ return await generateResponse(userInput + `\nFix these errors: ${validation.error}`);
166
+ }
167
+
168
+ // Topic guardrail in system prompt
169
+ // "You are a cooking assistant. If the user asks about anything unrelated
170
+ // to cooking, respond: 'I can only help with cooking questions.'"
171
+ ```
172
+
173
+ ## Examples
174
+
175
+ | Problem | Technique | Result |
176
+ |---------|-----------|--------|
177
+ | Model gives rambling answers | System prompt with strict output format | Consistent, parseable JSON |
178
+ | Classification accuracy is 70% | Add 5 few-shot examples covering edge cases | Accuracy jumps to 92% |
179
+ | Math reasoning fails | Chain-of-thought prompting | Step-by-step catches arithmetic errors |
180
+ | Users ask off-topic questions | System prompt + input classifier guardrail | Graceful refusal, no hallucination |
181
+ | Output format varies randomly | Tool use / function calling | Guaranteed schema compliance |
182
+
183
+ ## Checklist
184
+
185
+ - [ ] System prompt defines role, constraints, output format, and edge case behavior
186
+ - [ ] Few-shot examples cover the most important edge cases (not just happy path)
187
+ - [ ] Complex reasoning tasks use chain-of-thought prompting
188
+ - [ ] Output is structured via JSON mode, tool use, or explicit schema in prompt
189
+ - [ ] Input validation catches injection attempts and off-topic queries
190
+ - [ ] Output validation parses and checks the response before returning to users
191
+ - [ ] Temperature is set intentionally (0 for deterministic, 0.7+ for creative)
192
+ - [ ] Prompts are version-controlled alongside the code that uses them
193
+ - [ ] Prompt changes are evaluated against a test set, not just eyeballed
@@ -0,0 +1,247 @@
1
+ ---
2
+ name: pwa-patterns
3
+ description: Build Progressive Web Apps — service workers, offline support, push notifications, install prompts, and caching strategies.
4
+ ---
5
+
6
+ # Progressive Web App Patterns
7
+
8
+ ## When to Use
9
+
10
+ Build a PWA when your web app needs to work offline, send push notifications,
11
+ or provide an app-like experience without an app store. PWAs are especially
12
+ valuable for mobile-first apps, field tools used in low-connectivity areas,
13
+ and content apps that benefit from offline reading. This skill covers service
14
+ workers, caching strategies, push notifications, and install prompts.
15
+
16
+ ## How It Works
17
+
18
+ ### 1. Web App Manifest
19
+
20
+ ```json
21
+ {
22
+ "name": "MyApp",
23
+ "short_name": "MyApp",
24
+ "description": "Your productivity companion",
25
+ "start_url": "/",
26
+ "display": "standalone",
27
+ "background_color": "#ffffff",
28
+ "theme_color": "#0066ff",
29
+ "orientation": "portrait-primary",
30
+ "icons": [
31
+ { "src": "/icons/192.png", "sizes": "192x192", "type": "image/png" },
32
+ { "src": "/icons/512.png", "sizes": "512x512", "type": "image/png" },
33
+ { "src": "/icons/maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
34
+ ],
35
+ "screenshots": [
36
+ { "src": "/screenshots/mobile.png", "sizes": "390x844", "type": "image/png", "form_factor": "narrow" },
37
+ { "src": "/screenshots/desktop.png", "sizes": "1280x720", "type": "image/png", "form_factor": "wide" }
38
+ ]
39
+ }
40
+ ```
41
+
42
+ ### 2. Service Worker Registration
43
+
44
+ ```typescript
45
+ // app.ts — register on load
46
+ if ('serviceWorker' in navigator) {
47
+ window.addEventListener('load', async () => {
48
+ try {
49
+ const reg = await navigator.serviceWorker.register('/sw.js', { scope: '/' });
50
+ console.log('SW registered:', reg.scope);
51
+
52
+ // Check for updates every 30 minutes
53
+ setInterval(() => reg.update(), 30 * 60 * 1000);
54
+ } catch (err) {
55
+ console.error('SW registration failed:', err);
56
+ }
57
+ });
58
+ }
59
+ ```
60
+
61
+ ### 3. Caching Strategies
62
+
63
+ ```typescript
64
+ // sw.js — Workbox-style manual implementation
65
+ const CACHE_NAME = 'v1';
66
+ const STATIC_ASSETS = ['/', '/offline.html', '/styles.css', '/app.js'];
67
+
68
+ // Cache-First — for static assets (fonts, images, CSS)
69
+ async function cacheFirst(request: Request): Promise<Response> {
70
+ const cached = await caches.match(request);
71
+ if (cached) return cached;
72
+
73
+ const response = await fetch(request);
74
+ if (response.ok) {
75
+ const cache = await caches.open(CACHE_NAME);
76
+ cache.put(request, response.clone());
77
+ }
78
+ return response;
79
+ }
80
+
81
+ // Network-First — for API data and HTML pages
82
+ async function networkFirst(request: Request): Promise<Response> {
83
+ try {
84
+ const response = await fetch(request);
85
+ if (response.ok) {
86
+ const cache = await caches.open(CACHE_NAME);
87
+ cache.put(request, response.clone());
88
+ }
89
+ return response;
90
+ } catch {
91
+ const cached = await caches.match(request);
92
+ return cached ?? caches.match('/offline.html') as Promise<Response>;
93
+ }
94
+ }
95
+
96
+ // Stale-While-Revalidate — for data that can be slightly stale
97
+ async function staleWhileRevalidate(request: Request): Promise<Response> {
98
+ const cache = await caches.open(CACHE_NAME);
99
+ const cached = await cache.match(request);
100
+
101
+ const fetchPromise = fetch(request).then((response) => {
102
+ if (response.ok) cache.put(request, response.clone());
103
+ return response;
104
+ });
105
+
106
+ return cached ?? fetchPromise;
107
+ }
108
+
109
+ // Route requests to strategies
110
+ self.addEventListener('fetch', (event: FetchEvent) => {
111
+ const url = new URL(event.request.url);
112
+
113
+ if (STATIC_ASSETS.includes(url.pathname)) {
114
+ event.respondWith(cacheFirst(event.request));
115
+ } else if (url.pathname.startsWith('/api/')) {
116
+ event.respondWith(networkFirst(event.request));
117
+ } else {
118
+ event.respondWith(staleWhileRevalidate(event.request));
119
+ }
120
+ });
121
+ ```
122
+
123
+ ### 4. Push Notifications
124
+
125
+ ```typescript
126
+ // Client — subscribe to push
127
+ async function subscribeToPush(): Promise<PushSubscription> {
128
+ const registration = await navigator.serviceWorker.ready;
129
+ const subscription = await registration.pushManager.subscribe({
130
+ userVisibleOnly: true, // required — must show a notification
131
+ applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
132
+ });
133
+
134
+ // Send subscription to server
135
+ await fetch('/api/push/subscribe', {
136
+ method: 'POST',
137
+ headers: { 'Content-Type': 'application/json' },
138
+ body: JSON.stringify(subscription),
139
+ });
140
+
141
+ return subscription;
142
+ }
143
+
144
+ // Server — send push (using web-push library)
145
+ import webpush from 'web-push';
146
+
147
+ webpush.setVapidDetails('mailto:admin@myapp.com', VAPID_PUBLIC, VAPID_PRIVATE);
148
+
149
+ async function sendPush(subscription: PushSubscription, payload: object) {
150
+ await webpush.sendNotification(subscription, JSON.stringify(payload));
151
+ }
152
+
153
+ // Service worker — handle push event
154
+ self.addEventListener('push', (event: PushEvent) => {
155
+ const data = event.data?.json() ?? { title: 'New notification' };
156
+ event.waitUntil(
157
+ self.registration.showNotification(data.title, {
158
+ body: data.body,
159
+ icon: '/icons/192.png',
160
+ badge: '/icons/badge-72.png',
161
+ data: { url: data.url },
162
+ })
163
+ );
164
+ });
165
+
166
+ self.addEventListener('notificationclick', (event: NotificationEvent) => {
167
+ event.notification.close();
168
+ event.waitUntil(clients.openWindow(event.notification.data.url));
169
+ });
170
+ ```
171
+
172
+ ### 5. Install Prompt
173
+
174
+ ```typescript
175
+ let deferredPrompt: BeforeInstallPromptEvent | null = null;
176
+
177
+ window.addEventListener('beforeinstallprompt', (e) => {
178
+ e.preventDefault(); // Suppress browser default prompt
179
+ deferredPrompt = e;
180
+ showInstallBanner(); // Show your custom UI
181
+ });
182
+
183
+ async function handleInstallClick() {
184
+ if (!deferredPrompt) return;
185
+ deferredPrompt.prompt();
186
+ const { outcome } = await deferredPrompt.userChoice;
187
+ console.log(`Install prompt outcome: ${outcome}`);
188
+ deferredPrompt = null;
189
+ hideInstallBanner();
190
+ }
191
+
192
+ // Detect if already installed
193
+ window.addEventListener('appinstalled', () => {
194
+ hideInstallBanner();
195
+ analytics.track('pwa_installed');
196
+ });
197
+ ```
198
+
199
+ ### 6. Offline Data Sync
200
+
201
+ ```typescript
202
+ // Queue failed requests for retry when online
203
+ const pendingRequests: Array<{ url: string; options: RequestInit }> = [];
204
+
205
+ async function fetchWithOfflineQueue(url: string, options: RequestInit) {
206
+ try {
207
+ return await fetch(url, options);
208
+ } catch {
209
+ pendingRequests.push({ url, options });
210
+ saveToIndexedDB('pending-requests', pendingRequests);
211
+ throw new Error('Queued for offline sync');
212
+ }
213
+ }
214
+
215
+ // Sync when back online
216
+ window.addEventListener('online', async () => {
217
+ const pending = await loadFromIndexedDB('pending-requests');
218
+ for (const req of pending) {
219
+ try {
220
+ await fetch(req.url, req.options);
221
+ } catch {
222
+ // Still offline or failed — keep in queue
223
+ }
224
+ }
225
+ });
226
+ ```
227
+
228
+ ## Examples
229
+
230
+ | Strategy | Assets | Behavior |
231
+ |----------|--------|----------|
232
+ | Cache-First | Fonts, icons, CSS, JS | Fast load, update in background |
233
+ | Network-First | HTML pages, API data | Fresh data, offline fallback |
234
+ | Stale-While-Revalidate | User content, feeds | Instant display, background refresh |
235
+
236
+ ## Checklist
237
+
238
+ - [ ] Web app manifest with name, icons (192 + 512 + maskable), and display mode
239
+ - [ ] Service worker registered with update checks
240
+ - [ ] Static assets use Cache-First strategy
241
+ - [ ] API/data routes use Network-First with offline fallback
242
+ - [ ] Offline page displayed when network is unavailable
243
+ - [ ] Push notification subscription sent to server with VAPID keys
244
+ - [ ] Install prompt deferred and shown at appropriate moment
245
+ - [ ] Cache versioning and old cache cleanup on service worker activate
246
+ - [ ] Lighthouse PWA audit passes all checks
247
+ - [ ] Failed mutations queued for retry when connection returns