@codihaus/claude-skills 1.0.0 → 1.2.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.
- package/knowledge/domains/_index.md +105 -0
- package/knowledge/domains/ecommerce/_index.md +499 -0
- package/knowledge/domains/saas/_index.md +371 -0
- package/knowledge/stacks/_index.md +101 -0
- package/knowledge/stacks/directus/_index.md +349 -0
- package/knowledge/stacks/nextjs/_index.md +654 -0
- package/knowledge/stacks/nuxt/_index.md +469 -0
- package/package.json +3 -1
- package/project-scripts/graph.py +330 -0
- package/skills/_registry.md +61 -0
- package/skills/dev-coding/SKILL.md +16 -5
- package/skills/dev-coding-backend/SKILL.md +116 -251
- package/skills/dev-coding-frontend/SKILL.md +134 -388
- package/skills/dev-review/SKILL.md +13 -2
- package/skills/dev-scout/SKILL.md +180 -2
- package/skills/dev-scout/references/stack-patterns.md +371 -0
- package/skills/dev-specs/SKILL.md +74 -2
- package/src/commands/init.js +89 -12
- package/src/utils/project-setup.js +444 -0
- package/src/utils/skills.js +87 -1
- /package/{skills/dev-coding-frontend/references/nextjs.md → knowledge/stacks/nextjs/references/performance.md} +0 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# Nuxt.js
|
|
2
|
+
|
|
3
|
+
> Vue.js meta-framework with SSR, file-based routing, and auto-imports
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
**What it is:**
|
|
8
|
+
- Full-stack Vue.js framework
|
|
9
|
+
- Server-side rendering (SSR) by default
|
|
10
|
+
- File-based routing (pages/)
|
|
11
|
+
- Auto-imported composables and components
|
|
12
|
+
- Nitro server engine for API routes
|
|
13
|
+
|
|
14
|
+
**When to use:**
|
|
15
|
+
- Vue.js applications needing SEO (SSR)
|
|
16
|
+
- Full-stack apps (frontend + API)
|
|
17
|
+
- Projects wanting convention over configuration
|
|
18
|
+
- Teams familiar with Vue ecosystem
|
|
19
|
+
|
|
20
|
+
**Key concepts:**
|
|
21
|
+
- **Pages** = File-based routes in `pages/`
|
|
22
|
+
- **Composables** = Reusable logic in `composables/`
|
|
23
|
+
- **Components** = Auto-imported from `components/`
|
|
24
|
+
- **Server Routes** = API endpoints in `server/api/`
|
|
25
|
+
- **Middleware** = Route guards in `middleware/`
|
|
26
|
+
- **Plugins** = App initialization in `plugins/`
|
|
27
|
+
|
|
28
|
+
## Best Practices
|
|
29
|
+
|
|
30
|
+
### Project Structure
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
nuxt-app/
|
|
34
|
+
├── assets/ # Uncompiled assets (SCSS, images)
|
|
35
|
+
├── components/ # Auto-imported Vue components
|
|
36
|
+
│ ├── common/ # Shared components
|
|
37
|
+
│ ├── forms/ # Form components
|
|
38
|
+
│ └── ui/ # UI primitives
|
|
39
|
+
├── composables/ # Auto-imported composables
|
|
40
|
+
│ ├── useAuth.ts # Auth logic
|
|
41
|
+
│ ├── useApi.ts # API client
|
|
42
|
+
│ └── use{Feature}.ts # Feature-specific
|
|
43
|
+
├── layouts/ # Page layouts
|
|
44
|
+
├── middleware/ # Route middleware
|
|
45
|
+
├── pages/ # File-based routes
|
|
46
|
+
├── plugins/ # Nuxt plugins
|
|
47
|
+
├── server/ # Nitro server
|
|
48
|
+
│ ├── api/ # API routes
|
|
49
|
+
│ ├── middleware/ # Server middleware
|
|
50
|
+
│ └── utils/ # Server utilities
|
|
51
|
+
├── stores/ # Pinia stores (if using)
|
|
52
|
+
├── types/ # TypeScript types
|
|
53
|
+
└── utils/ # Shared utilities
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Data Fetching
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
DO:
|
|
60
|
+
✓ Use useFetch/useAsyncData for SSR-compatible fetching
|
|
61
|
+
✓ Use $fetch for client-only or event handlers
|
|
62
|
+
✓ Set key for caching: useFetch('/api/users', { key: 'users' })
|
|
63
|
+
✓ Use lazy: true for non-critical data
|
|
64
|
+
|
|
65
|
+
DON'T:
|
|
66
|
+
✗ Use raw fetch() in components (breaks SSR hydration)
|
|
67
|
+
✗ Fetch in onMounted for initial data
|
|
68
|
+
✗ Forget error handling
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Composables
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
DO:
|
|
75
|
+
✓ Name with use* prefix (useAuth, useProducts)
|
|
76
|
+
✓ Return reactive refs and functions
|
|
77
|
+
✓ Handle SSR context (check process.client if needed)
|
|
78
|
+
✓ Keep composables focused (single responsibility)
|
|
79
|
+
|
|
80
|
+
DON'T:
|
|
81
|
+
✗ Use component-specific logic
|
|
82
|
+
✗ Directly mutate external state
|
|
83
|
+
✗ Forget to handle loading/error states
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Server Routes (Nitro)
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
DO:
|
|
90
|
+
✓ Use server/api/ for API endpoints
|
|
91
|
+
✓ Define event handlers with defineEventHandler
|
|
92
|
+
✓ Use readBody for POST data
|
|
93
|
+
✓ Return objects (auto-serialized)
|
|
94
|
+
✓ Use server-only secrets (process.env)
|
|
95
|
+
|
|
96
|
+
DON'T:
|
|
97
|
+
✗ Expose secrets to client (use runtimeConfig.secret)
|
|
98
|
+
✗ Skip input validation
|
|
99
|
+
✗ Forget error handling
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### State Management
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
DO:
|
|
106
|
+
✓ Use useState for simple shared state
|
|
107
|
+
✓ Use Pinia for complex state
|
|
108
|
+
✓ Keep state minimal (derive where possible)
|
|
109
|
+
✓ Use localStorage only via plugins (SSR-safe)
|
|
110
|
+
|
|
111
|
+
DON'T:
|
|
112
|
+
✗ Use Vue reactive() at module level (SSR leak)
|
|
113
|
+
✗ Store derived data (compute it)
|
|
114
|
+
✗ Forget SSR hydration
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Anti-Patterns
|
|
118
|
+
|
|
119
|
+
| Anti-Pattern | Problem | Better Approach |
|
|
120
|
+
|--------------|---------|-----------------|
|
|
121
|
+
| `fetch()` in setup | SSR hydration mismatch | Use `useFetch()` |
|
|
122
|
+
| Direct localStorage | Fails on server | Use `useState` + plugin |
|
|
123
|
+
| Reactive at module level | State leak between requests | Use `useState()` |
|
|
124
|
+
| Heavy sync operations | Blocks rendering | Use `lazy: true` or defer |
|
|
125
|
+
| Ignoring loading state | Poor UX | Handle pending/error |
|
|
126
|
+
| API secrets in client | Security risk | Use `runtimeConfig.secret` |
|
|
127
|
+
|
|
128
|
+
## For /dev-specs
|
|
129
|
+
|
|
130
|
+
### Page Specification
|
|
131
|
+
|
|
132
|
+
```markdown
|
|
133
|
+
## Page: /products/[id]
|
|
134
|
+
|
|
135
|
+
### Route
|
|
136
|
+
- Path: `/products/:id`
|
|
137
|
+
- File: `pages/products/[id].vue`
|
|
138
|
+
|
|
139
|
+
### Data Requirements
|
|
140
|
+
- Product details (useFetch from API/Directus)
|
|
141
|
+
- Related products (lazy load)
|
|
142
|
+
|
|
143
|
+
### Components
|
|
144
|
+
| Component | Purpose |
|
|
145
|
+
|-----------|---------|
|
|
146
|
+
| ProductGallery | Image carousel |
|
|
147
|
+
| ProductInfo | Name, price, description |
|
|
148
|
+
| AddToCart | Cart interaction |
|
|
149
|
+
|
|
150
|
+
### SEO
|
|
151
|
+
- Title: `{product.name} | Store`
|
|
152
|
+
- Meta description: `{product.description}`
|
|
153
|
+
- OG image: `{product.image}`
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Composable Specification
|
|
157
|
+
|
|
158
|
+
```markdown
|
|
159
|
+
## Composable: useProducts
|
|
160
|
+
|
|
161
|
+
### Purpose
|
|
162
|
+
Manage product data fetching and caching
|
|
163
|
+
|
|
164
|
+
### Interface
|
|
165
|
+
```typescript
|
|
166
|
+
function useProducts() {
|
|
167
|
+
return {
|
|
168
|
+
products: Ref<Product[]>,
|
|
169
|
+
pending: Ref<boolean>,
|
|
170
|
+
error: Ref<Error | null>,
|
|
171
|
+
refresh: () => Promise<void>,
|
|
172
|
+
getProduct: (id: string) => Product | undefined,
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Usage
|
|
178
|
+
```vue
|
|
179
|
+
const { products, pending } = useProducts()
|
|
180
|
+
```
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Server Route Specification
|
|
184
|
+
|
|
185
|
+
```markdown
|
|
186
|
+
## API: POST /api/orders
|
|
187
|
+
|
|
188
|
+
### Purpose
|
|
189
|
+
Create new order
|
|
190
|
+
|
|
191
|
+
### Request
|
|
192
|
+
```typescript
|
|
193
|
+
{
|
|
194
|
+
items: { productId: string, quantity: number }[],
|
|
195
|
+
shippingAddress: Address,
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Response
|
|
200
|
+
```typescript
|
|
201
|
+
{ orderId: string, total: number, status: 'pending' }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Errors
|
|
205
|
+
| Code | Reason |
|
|
206
|
+
|------|--------|
|
|
207
|
+
| 400 | Invalid items or address |
|
|
208
|
+
| 401 | Not authenticated |
|
|
209
|
+
| 422 | Insufficient stock |
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## For /dev-coding
|
|
213
|
+
|
|
214
|
+
### Composable Pattern
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// composables/useProducts.ts
|
|
218
|
+
export const useProducts = () => {
|
|
219
|
+
const products = useState<Product[]>('products', () => []);
|
|
220
|
+
const pending = ref(false);
|
|
221
|
+
const error = ref<Error | null>(null);
|
|
222
|
+
|
|
223
|
+
const fetch = async () => {
|
|
224
|
+
pending.value = true;
|
|
225
|
+
error.value = null;
|
|
226
|
+
try {
|
|
227
|
+
const data = await $fetch<Product[]>('/api/products');
|
|
228
|
+
products.value = data;
|
|
229
|
+
} catch (e) {
|
|
230
|
+
error.value = e as Error;
|
|
231
|
+
} finally {
|
|
232
|
+
pending.value = false;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const getProduct = (id: string) =>
|
|
237
|
+
products.value.find(p => p.id === id);
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
products: readonly(products),
|
|
241
|
+
pending: readonly(pending),
|
|
242
|
+
error: readonly(error),
|
|
243
|
+
fetch,
|
|
244
|
+
getProduct,
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Page with Data Fetching
|
|
250
|
+
|
|
251
|
+
```vue
|
|
252
|
+
<!-- pages/products/[id].vue -->
|
|
253
|
+
<script setup lang="ts">
|
|
254
|
+
const route = useRoute();
|
|
255
|
+
const { data: product, pending, error } = await useFetch(
|
|
256
|
+
`/api/products/${route.params.id}`,
|
|
257
|
+
{ key: `product-${route.params.id}` }
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// SEO
|
|
261
|
+
useSeoMeta({
|
|
262
|
+
title: () => product.value?.name ?? 'Product',
|
|
263
|
+
description: () => product.value?.description,
|
|
264
|
+
});
|
|
265
|
+
</script>
|
|
266
|
+
|
|
267
|
+
<template>
|
|
268
|
+
<div v-if="pending">Loading...</div>
|
|
269
|
+
<div v-else-if="error">Error: {{ error.message }}</div>
|
|
270
|
+
<div v-else-if="product">
|
|
271
|
+
<h1>{{ product.name }}</h1>
|
|
272
|
+
<p>{{ product.price }}</p>
|
|
273
|
+
</div>
|
|
274
|
+
</template>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Server Route
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
// server/api/products/[id].get.ts
|
|
281
|
+
export default defineEventHandler(async (event) => {
|
|
282
|
+
const id = getRouterParam(event, 'id');
|
|
283
|
+
|
|
284
|
+
if (!id) {
|
|
285
|
+
throw createError({
|
|
286
|
+
statusCode: 400,
|
|
287
|
+
message: 'Product ID required',
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Fetch from Directus or database
|
|
292
|
+
const product = await getProduct(id);
|
|
293
|
+
|
|
294
|
+
if (!product) {
|
|
295
|
+
throw createError({
|
|
296
|
+
statusCode: 404,
|
|
297
|
+
message: 'Product not found',
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return product;
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Server Route with Body
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
// server/api/orders.post.ts
|
|
309
|
+
export default defineEventHandler(async (event) => {
|
|
310
|
+
const body = await readBody(event);
|
|
311
|
+
|
|
312
|
+
// Validate
|
|
313
|
+
const parsed = orderSchema.safeParse(body);
|
|
314
|
+
if (!parsed.success) {
|
|
315
|
+
throw createError({
|
|
316
|
+
statusCode: 400,
|
|
317
|
+
message: 'Invalid order data',
|
|
318
|
+
data: parsed.error.flatten(),
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Create order
|
|
323
|
+
const order = await createOrder(parsed.data);
|
|
324
|
+
|
|
325
|
+
return { orderId: order.id, status: order.status };
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Middleware (Auth)
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// middleware/auth.ts
|
|
333
|
+
export default defineNuxtRouteMiddleware((to, from) => {
|
|
334
|
+
const { user } = useAuth();
|
|
335
|
+
|
|
336
|
+
if (!user.value && to.meta.requiresAuth) {
|
|
337
|
+
return navigateTo('/login');
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Usage in page
|
|
342
|
+
definePageMeta({
|
|
343
|
+
middleware: 'auth',
|
|
344
|
+
requiresAuth: true,
|
|
345
|
+
});
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Plugin (Client-only)
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
// plugins/localStorage.client.ts
|
|
352
|
+
export default defineNuxtPlugin(() => {
|
|
353
|
+
return {
|
|
354
|
+
provide: {
|
|
355
|
+
localStorage: {
|
|
356
|
+
get: (key: string) => localStorage.getItem(key),
|
|
357
|
+
set: (key: string, value: string) => localStorage.setItem(key, value),
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Usage: const { $localStorage } = useNuxtApp()
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## For /dev-review
|
|
367
|
+
|
|
368
|
+
### SSR Compatibility Checklist
|
|
369
|
+
|
|
370
|
+
- [ ] Using `useFetch`/`useAsyncData` for data
|
|
371
|
+
- [ ] No `window`/`document` access without `process.client` check
|
|
372
|
+
- [ ] Using `useState` instead of module-level reactive
|
|
373
|
+
- [ ] Plugins marked `.client.ts` if browser-only
|
|
374
|
+
- [ ] No localStorage without client check
|
|
375
|
+
|
|
376
|
+
### Performance Checklist
|
|
377
|
+
|
|
378
|
+
- [ ] Using `lazy: true` for below-fold data
|
|
379
|
+
- [ ] Images optimized with `<NuxtImg>`
|
|
380
|
+
- [ ] Components lazy-loaded where appropriate
|
|
381
|
+
- [ ] Using `key` parameter for cache control
|
|
382
|
+
- [ ] No blocking operations in setup
|
|
383
|
+
|
|
384
|
+
### Security Checklist
|
|
385
|
+
|
|
386
|
+
- [ ] Secrets in `runtimeConfig` (not `public`)
|
|
387
|
+
- [ ] Server routes validate input
|
|
388
|
+
- [ ] Auth middleware on protected routes
|
|
389
|
+
- [ ] CSRF protection on mutations
|
|
390
|
+
- [ ] No sensitive data in client bundle
|
|
391
|
+
|
|
392
|
+
### Common Mistakes
|
|
393
|
+
|
|
394
|
+
| Mistake | Impact | Fix |
|
|
395
|
+
|---------|--------|-----|
|
|
396
|
+
| `fetch()` instead of `useFetch` | Hydration mismatch | Use `useFetch` |
|
|
397
|
+
| Missing `key` in useFetch | Cache conflicts | Add unique key |
|
|
398
|
+
| Ignoring `pending` state | Flash of empty | Show loading |
|
|
399
|
+
| Direct localStorage | SSR crash | Use plugin or client check |
|
|
400
|
+
| Secrets in `public` config | Leaked to client | Use `runtimeConfig.secret` |
|
|
401
|
+
|
|
402
|
+
## Integration
|
|
403
|
+
|
|
404
|
+
### With Directus
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
// composables/useDirectus.ts
|
|
408
|
+
import { createDirectus, rest, authentication } from '@directus/sdk';
|
|
409
|
+
|
|
410
|
+
export const useDirectus = () => {
|
|
411
|
+
const config = useRuntimeConfig();
|
|
412
|
+
|
|
413
|
+
const client = createDirectus<Schema>(config.public.directusUrl)
|
|
414
|
+
.with(rest())
|
|
415
|
+
.with(authentication('cookie', { credentials: 'include' }));
|
|
416
|
+
|
|
417
|
+
return client;
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// composables/useProducts.ts
|
|
421
|
+
export const useProducts = () => {
|
|
422
|
+
const directus = useDirectus();
|
|
423
|
+
|
|
424
|
+
return useAsyncData('products', () =>
|
|
425
|
+
directus.request(readItems('products', {
|
|
426
|
+
filter: { status: { _eq: 'published' } },
|
|
427
|
+
}))
|
|
428
|
+
);
|
|
429
|
+
};
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### With Pinia
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
// stores/cart.ts
|
|
436
|
+
export const useCartStore = defineStore('cart', () => {
|
|
437
|
+
const items = ref<CartItem[]>([]);
|
|
438
|
+
|
|
439
|
+
const total = computed(() =>
|
|
440
|
+
items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
const addItem = (product: Product, quantity = 1) => {
|
|
444
|
+
const existing = items.value.find(i => i.productId === product.id);
|
|
445
|
+
if (existing) {
|
|
446
|
+
existing.quantity += quantity;
|
|
447
|
+
} else {
|
|
448
|
+
items.value.push({ productId: product.id, ...product, quantity });
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
return { items, total, addItem };
|
|
453
|
+
});
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## Resources
|
|
457
|
+
|
|
458
|
+
### Official Docs
|
|
459
|
+
- Docs: https://nuxt.com/docs
|
|
460
|
+
- Modules: https://nuxt.com/modules
|
|
461
|
+
|
|
462
|
+
### Context7 Queries
|
|
463
|
+
|
|
464
|
+
```
|
|
465
|
+
Query: "Nuxt 3 useFetch best practices"
|
|
466
|
+
Query: "Nuxt 3 server routes authentication"
|
|
467
|
+
Query: "Nuxt 3 middleware patterns"
|
|
468
|
+
Query: "Nuxt 3 Pinia setup"
|
|
469
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codihaus/claude-skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Claude Code skills for software development workflow",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
"bin",
|
|
41
41
|
"src",
|
|
42
42
|
"skills",
|
|
43
|
+
"knowledge",
|
|
44
|
+
"project-scripts",
|
|
43
45
|
"templates",
|
|
44
46
|
"README.md"
|
|
45
47
|
]
|