@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.
- package/README.md +106 -41
- package/dist/bin.cjs +963 -84
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +963 -84
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +73 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +73 -56
- package/dist/index.js.map +1 -1
- package/package.json +23 -24
- package/templates/CLAUDE.md.template +60 -5
- package/templates/agents/README.md +58 -39
- package/templates/agents/_registry.json +43 -202
- package/templates/agents/engineering/{hono-engineer.md → api-engineer.md} +61 -70
- package/templates/agents/engineering/database-engineer.md +253 -0
- package/templates/agents/engineering/frontend-engineer.md +302 -0
- package/templates/hooks/on-notification.sh +0 -0
- package/templates/scripts/add-changelogs.sh +0 -0
- package/templates/scripts/generate-code-registry.ts +0 -0
- package/templates/scripts/health-check.sh +0 -0
- package/templates/scripts/sync-registry.sh +0 -0
- package/templates/scripts/telemetry-report.ts +0 -0
- package/templates/scripts/validate-docs.sh +0 -0
- package/templates/scripts/validate-registry.sh +0 -0
- package/templates/scripts/validate-structure.sh +0 -0
- package/templates/scripts/worktree-cleanup.sh +0 -0
- package/templates/scripts/worktree-create.sh +0 -0
- package/templates/skills/README.md +99 -90
- package/templates/skills/_registry.json +323 -16
- package/templates/skills/api-frameworks/express-patterns.md +411 -0
- package/templates/skills/api-frameworks/fastify-patterns.md +419 -0
- package/templates/skills/api-frameworks/hono-patterns.md +388 -0
- package/templates/skills/api-frameworks/nestjs-patterns.md +497 -0
- package/templates/skills/database/drizzle-patterns.md +449 -0
- package/templates/skills/database/mongoose-patterns.md +503 -0
- package/templates/skills/database/prisma-patterns.md +487 -0
- package/templates/skills/frontend-frameworks/astro-patterns.md +415 -0
- package/templates/skills/frontend-frameworks/nextjs-patterns.md +470 -0
- package/templates/skills/frontend-frameworks/react-patterns.md +516 -0
- package/templates/skills/frontend-frameworks/tanstack-start-patterns.md +469 -0
- package/templates/skills/patterns/atdd-methodology.md +364 -0
- package/templates/skills/patterns/bdd-methodology.md +281 -0
- package/templates/skills/patterns/clean-architecture.md +444 -0
- package/templates/skills/patterns/hexagonal-architecture.md +567 -0
- package/templates/skills/patterns/vertical-slice-architecture.md +502 -0
- package/templates/agents/engineering/astro-engineer.md +0 -293
- package/templates/agents/engineering/db-drizzle-engineer.md +0 -360
- package/templates/agents/engineering/express-engineer.md +0 -316
- package/templates/agents/engineering/fastify-engineer.md +0 -399
- package/templates/agents/engineering/mongoose-engineer.md +0 -473
- package/templates/agents/engineering/nestjs-engineer.md +0 -429
- package/templates/agents/engineering/nextjs-engineer.md +0 -451
- package/templates/agents/engineering/prisma-engineer.md +0 -432
- package/templates/agents/engineering/react-senior-dev.md +0 -394
- package/templates/agents/engineering/tanstack-start-engineer.md +0 -447
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# Astro Patterns
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Astro is a web framework for content-focused websites with islands architecture. This skill provides patterns for building Astro applications.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Page Components
|
|
10
|
+
|
|
11
|
+
### Basic Page
|
|
12
|
+
|
|
13
|
+
```astro
|
|
14
|
+
---
|
|
15
|
+
// src/pages/index.astro
|
|
16
|
+
import BaseLayout from '@/layouts/BaseLayout.astro';
|
|
17
|
+
import { getItems } from '@/lib/api';
|
|
18
|
+
|
|
19
|
+
const items = await getItems();
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
<BaseLayout title="Home">
|
|
23
|
+
<main>
|
|
24
|
+
<h1>Welcome</h1>
|
|
25
|
+
<ul>
|
|
26
|
+
{items.map((item) => (
|
|
27
|
+
<li>
|
|
28
|
+
<a href={`/items/${item.id}`}>{item.title}</a>
|
|
29
|
+
</li>
|
|
30
|
+
))}
|
|
31
|
+
</ul>
|
|
32
|
+
</main>
|
|
33
|
+
</BaseLayout>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Dynamic Route
|
|
37
|
+
|
|
38
|
+
```astro
|
|
39
|
+
---
|
|
40
|
+
// src/pages/items/[id].astro
|
|
41
|
+
import BaseLayout from '@/layouts/BaseLayout.astro';
|
|
42
|
+
import { getItem, getItems } from '@/lib/api';
|
|
43
|
+
|
|
44
|
+
export async function getStaticPaths() {
|
|
45
|
+
const items = await getItems();
|
|
46
|
+
|
|
47
|
+
return items.map((item) => ({
|
|
48
|
+
params: { id: item.id },
|
|
49
|
+
props: { item },
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface Props {
|
|
54
|
+
item: Item;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const { item } = Astro.props;
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
<BaseLayout title={item.title}>
|
|
61
|
+
<article>
|
|
62
|
+
<h1>{item.title}</h1>
|
|
63
|
+
<p>{item.description}</p>
|
|
64
|
+
<p>Price: ${item.price}</p>
|
|
65
|
+
</article>
|
|
66
|
+
</BaseLayout>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Layouts
|
|
72
|
+
|
|
73
|
+
### Base Layout
|
|
74
|
+
|
|
75
|
+
```astro
|
|
76
|
+
---
|
|
77
|
+
// src/layouts/BaseLayout.astro
|
|
78
|
+
import Header from '@/components/Header.astro';
|
|
79
|
+
import Footer from '@/components/Footer.astro';
|
|
80
|
+
import '@/styles/global.css';
|
|
81
|
+
|
|
82
|
+
interface Props {
|
|
83
|
+
title: string;
|
|
84
|
+
description?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const { title, description = 'Default description' } = Astro.props;
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
<!doctype html>
|
|
91
|
+
<html lang="en">
|
|
92
|
+
<head>
|
|
93
|
+
<meta charset="UTF-8" />
|
|
94
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
95
|
+
<meta name="description" content={description} />
|
|
96
|
+
<title>{title}</title>
|
|
97
|
+
</head>
|
|
98
|
+
<body>
|
|
99
|
+
<Header />
|
|
100
|
+
<slot />
|
|
101
|
+
<Footer />
|
|
102
|
+
</body>
|
|
103
|
+
</html>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Nested Layouts
|
|
107
|
+
|
|
108
|
+
```astro
|
|
109
|
+
---
|
|
110
|
+
// src/layouts/DocsLayout.astro
|
|
111
|
+
import BaseLayout from './BaseLayout.astro';
|
|
112
|
+
import Sidebar from '@/components/Sidebar.astro';
|
|
113
|
+
|
|
114
|
+
interface Props {
|
|
115
|
+
title: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const { title } = Astro.props;
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
<BaseLayout title={title}>
|
|
122
|
+
<div class="flex">
|
|
123
|
+
<Sidebar />
|
|
124
|
+
<main class="flex-1">
|
|
125
|
+
<slot />
|
|
126
|
+
</main>
|
|
127
|
+
</div>
|
|
128
|
+
</BaseLayout>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Islands Architecture
|
|
134
|
+
|
|
135
|
+
### React Island
|
|
136
|
+
|
|
137
|
+
```astro
|
|
138
|
+
---
|
|
139
|
+
// src/pages/interactive.astro
|
|
140
|
+
import BaseLayout from '@/layouts/BaseLayout.astro';
|
|
141
|
+
import Counter from '@/components/Counter'; // React component
|
|
142
|
+
import ItemForm from '@/components/ItemForm'; // React component
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
<BaseLayout title="Interactive Page">
|
|
146
|
+
<h1>Static Content</h1>
|
|
147
|
+
|
|
148
|
+
<!-- Hydrates on page load -->
|
|
149
|
+
<Counter client:load initialCount={0} />
|
|
150
|
+
|
|
151
|
+
<!-- Hydrates when visible -->
|
|
152
|
+
<ItemForm client:visible />
|
|
153
|
+
|
|
154
|
+
<!-- Hydrates on idle -->
|
|
155
|
+
<HeavyComponent client:idle />
|
|
156
|
+
|
|
157
|
+
<!-- Only client-side (no SSR) -->
|
|
158
|
+
<ClientOnlyWidget client:only="react" />
|
|
159
|
+
</BaseLayout>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Interactive React Component
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
// src/components/Counter.tsx
|
|
166
|
+
import { useState } from 'react';
|
|
167
|
+
|
|
168
|
+
interface CounterProps {
|
|
169
|
+
initialCount: number;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export default function Counter({ initialCount }: CounterProps) {
|
|
173
|
+
const [count, setCount] = useState(initialCount);
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<div className="p-4 border rounded">
|
|
177
|
+
<p>Count: {count}</p>
|
|
178
|
+
<button onClick={() => setCount(count + 1)}>Increment</button>
|
|
179
|
+
<button onClick={() => setCount(count - 1)}>Decrement</button>
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Content Collections
|
|
188
|
+
|
|
189
|
+
### Collection Schema
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
// src/content/config.ts
|
|
193
|
+
import { defineCollection, z } from 'astro:content';
|
|
194
|
+
|
|
195
|
+
const blog = defineCollection({
|
|
196
|
+
type: 'content',
|
|
197
|
+
schema: z.object({
|
|
198
|
+
title: z.string(),
|
|
199
|
+
description: z.string(),
|
|
200
|
+
pubDate: z.coerce.date(),
|
|
201
|
+
updatedDate: z.coerce.date().optional(),
|
|
202
|
+
heroImage: z.string().optional(),
|
|
203
|
+
tags: z.array(z.string()).default([]),
|
|
204
|
+
draft: z.boolean().default(false),
|
|
205
|
+
}),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const authors = defineCollection({
|
|
209
|
+
type: 'data',
|
|
210
|
+
schema: z.object({
|
|
211
|
+
name: z.string(),
|
|
212
|
+
bio: z.string(),
|
|
213
|
+
avatar: z.string(),
|
|
214
|
+
}),
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
export const collections = { blog, authors };
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Using Collections
|
|
221
|
+
|
|
222
|
+
```astro
|
|
223
|
+
---
|
|
224
|
+
// src/pages/blog/index.astro
|
|
225
|
+
import { getCollection } from 'astro:content';
|
|
226
|
+
import BaseLayout from '@/layouts/BaseLayout.astro';
|
|
227
|
+
import BlogCard from '@/components/BlogCard.astro';
|
|
228
|
+
|
|
229
|
+
const posts = await getCollection('blog', ({ data }) => {
|
|
230
|
+
return !data.draft;
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const sortedPosts = posts.sort(
|
|
234
|
+
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
|
|
235
|
+
);
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
<BaseLayout title="Blog">
|
|
239
|
+
<h1>Blog Posts</h1>
|
|
240
|
+
<div class="grid gap-4">
|
|
241
|
+
{sortedPosts.map((post) => (
|
|
242
|
+
<BlogCard post={post} />
|
|
243
|
+
))}
|
|
244
|
+
</div>
|
|
245
|
+
</BaseLayout>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Blog Post Page
|
|
249
|
+
|
|
250
|
+
```astro
|
|
251
|
+
---
|
|
252
|
+
// src/pages/blog/[...slug].astro
|
|
253
|
+
import { getCollection } from 'astro:content';
|
|
254
|
+
import BlogLayout from '@/layouts/BlogLayout.astro';
|
|
255
|
+
|
|
256
|
+
export async function getStaticPaths() {
|
|
257
|
+
const posts = await getCollection('blog');
|
|
258
|
+
|
|
259
|
+
return posts.map((post) => ({
|
|
260
|
+
params: { slug: post.slug },
|
|
261
|
+
props: { post },
|
|
262
|
+
}));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const { post } = Astro.props;
|
|
266
|
+
const { Content } = await post.render();
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
<BlogLayout title={post.data.title}>
|
|
270
|
+
<article>
|
|
271
|
+
<h1>{post.data.title}</h1>
|
|
272
|
+
<time datetime={post.data.pubDate.toISOString()}>
|
|
273
|
+
{post.data.pubDate.toLocaleDateString()}
|
|
274
|
+
</time>
|
|
275
|
+
<Content />
|
|
276
|
+
</article>
|
|
277
|
+
</BlogLayout>
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## API Routes
|
|
283
|
+
|
|
284
|
+
### Basic Endpoint
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// src/pages/api/items.ts
|
|
288
|
+
import type { APIRoute } from 'astro';
|
|
289
|
+
import { getItems } from '@/lib/api';
|
|
290
|
+
|
|
291
|
+
export const GET: APIRoute = async () => {
|
|
292
|
+
const items = await getItems();
|
|
293
|
+
|
|
294
|
+
return new Response(JSON.stringify({ data: items }), {
|
|
295
|
+
status: 200,
|
|
296
|
+
headers: {
|
|
297
|
+
'Content-Type': 'application/json',
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
export const POST: APIRoute = async ({ request }) => {
|
|
303
|
+
const body = await request.json();
|
|
304
|
+
|
|
305
|
+
// Create item...
|
|
306
|
+
|
|
307
|
+
return new Response(JSON.stringify({ data: newItem }), {
|
|
308
|
+
status: 201,
|
|
309
|
+
headers: {
|
|
310
|
+
'Content-Type': 'application/json',
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
};
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Dynamic Endpoint
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// src/pages/api/items/[id].ts
|
|
320
|
+
import type { APIRoute } from 'astro';
|
|
321
|
+
|
|
322
|
+
export const GET: APIRoute = async ({ params }) => {
|
|
323
|
+
const { id } = params;
|
|
324
|
+
const item = await getItem(id);
|
|
325
|
+
|
|
326
|
+
if (!item) {
|
|
327
|
+
return new Response(JSON.stringify({ error: 'Not found' }), {
|
|
328
|
+
status: 404,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return new Response(JSON.stringify({ data: item }), {
|
|
333
|
+
status: 200,
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Integrations
|
|
341
|
+
|
|
342
|
+
### Config with Integrations
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// astro.config.mjs
|
|
346
|
+
import { defineConfig } from 'astro/config';
|
|
347
|
+
import react from '@astrojs/react';
|
|
348
|
+
import tailwind from '@astrojs/tailwind';
|
|
349
|
+
import sitemap from '@astrojs/sitemap';
|
|
350
|
+
import vercel from '@astrojs/vercel/serverless';
|
|
351
|
+
|
|
352
|
+
export default defineConfig({
|
|
353
|
+
site: 'https://example.com',
|
|
354
|
+
integrations: [
|
|
355
|
+
react(),
|
|
356
|
+
tailwind(),
|
|
357
|
+
sitemap(),
|
|
358
|
+
],
|
|
359
|
+
output: 'hybrid', // Static by default, opt-in to server rendering
|
|
360
|
+
adapter: vercel(),
|
|
361
|
+
});
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## Project Structure
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
src/
|
|
370
|
+
├── pages/
|
|
371
|
+
│ ├── index.astro
|
|
372
|
+
│ ├── about.astro
|
|
373
|
+
│ ├── blog/
|
|
374
|
+
│ │ ├── index.astro
|
|
375
|
+
│ │ └── [...slug].astro
|
|
376
|
+
│ └── api/
|
|
377
|
+
│ └── items.ts
|
|
378
|
+
├── layouts/
|
|
379
|
+
│ ├── BaseLayout.astro
|
|
380
|
+
│ └── BlogLayout.astro
|
|
381
|
+
├── components/
|
|
382
|
+
│ ├── Header.astro # Static component
|
|
383
|
+
│ ├── Footer.astro
|
|
384
|
+
│ ├── BlogCard.astro
|
|
385
|
+
│ └── Counter.tsx # React island
|
|
386
|
+
├── content/
|
|
387
|
+
│ ├── config.ts
|
|
388
|
+
│ └── blog/
|
|
389
|
+
│ ├── post-1.md
|
|
390
|
+
│ └── post-2.md
|
|
391
|
+
├── lib/
|
|
392
|
+
│ └── api.ts
|
|
393
|
+
└── styles/
|
|
394
|
+
└── global.css
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Best Practices
|
|
400
|
+
|
|
401
|
+
### Good
|
|
402
|
+
|
|
403
|
+
- Use Astro components for static content
|
|
404
|
+
- Use islands (`client:*`) only for interactive parts
|
|
405
|
+
- Use content collections for structured content
|
|
406
|
+
- Use `client:visible` for below-the-fold interactivity
|
|
407
|
+
- Generate static pages when possible
|
|
408
|
+
|
|
409
|
+
### Bad
|
|
410
|
+
|
|
411
|
+
- Adding `client:load` to everything (defeats the purpose)
|
|
412
|
+
- Using React/Vue for static content
|
|
413
|
+
- Not using content collections for blog/docs
|
|
414
|
+
- Ignoring image optimization
|
|
415
|
+
- Server rendering when static works
|