@raftlabs/raftstack 1.6.3 → 1.7.1
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/.claude/skills/asana/SKILL.md +378 -0
- package/.claude/skills/backend/SKILL.md +132 -1
- package/.claude/skills/code-quality/SKILL.md +53 -38
- package/.claude/skills/database/SKILL.md +55 -6
- package/.claude/skills/react/SKILL.md +112 -2
- package/.claude/skills/seo/SKILL.md +117 -5
- package/dist/cli.js +15 -6
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: database
|
|
3
|
-
description: Use when
|
|
3
|
+
description: Use when writing Drizzle ORM schemas, creating database tables, adding indexes, writing SQL migrations, defining relations, or optimizing PostgreSQL queries. Use for foreign key indexes, JSONB columns, composite indexes, or prepared statements.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Database Development
|
|
@@ -125,20 +125,66 @@ export const users = pgTable("users", {
|
|
|
125
125
|
| Strategy | Use When | Pros | Cons |
|
|
126
126
|
|----------|----------|------|------|
|
|
127
127
|
| **UUID** | Distributed systems, public IDs | No collisions, hide growth | Larger, slower indexes |
|
|
128
|
-
| **
|
|
128
|
+
| **Identity** | Single DB, internal IDs (2025+) | Compact, fast, SQL standard | Exposes growth |
|
|
129
129
|
| **ULID/NanoID** | Need sortability + randomness | Sortable, compact | Custom generation |
|
|
130
130
|
|
|
131
131
|
```typescript
|
|
132
|
-
// UUID (default
|
|
132
|
+
// UUID (default for distributed/public IDs)
|
|
133
133
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
134
134
|
|
|
135
|
-
//
|
|
136
|
-
id:
|
|
135
|
+
// ✅ GOOD: Identity columns (PostgreSQL 10+, preferred over serial)
|
|
136
|
+
id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
|
|
137
|
+
|
|
138
|
+
// For bigint IDs
|
|
139
|
+
id: bigint("id", { mode: "number" }).primaryKey().generatedAlwaysAsIdentity(),
|
|
140
|
+
|
|
141
|
+
// ❌ AVOID: Serial (legacy, has sequence ownership issues)
|
|
142
|
+
id: serial("id").primaryKey(), // Use identity instead
|
|
137
143
|
|
|
138
144
|
// ULID for sortable + random
|
|
139
145
|
id: varchar("id", { length: 26 }).primaryKey().$default(() => ulid()),
|
|
140
146
|
```
|
|
141
147
|
|
|
148
|
+
**Why Identity over Serial:**
|
|
149
|
+
- SQL standard (serial is PostgreSQL-specific)
|
|
150
|
+
- No sequence ownership issues during migrations
|
|
151
|
+
- Better `COPY` and `pg_dump` behavior
|
|
152
|
+
- Cannot be overwritten accidentally (`GENERATED ALWAYS`)
|
|
153
|
+
|
|
154
|
+
### 7. Full-Text Search with GIN
|
|
155
|
+
|
|
156
|
+
For searchable text columns, use PostgreSQL's built-in full-text search:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { sql } from "drizzle-orm";
|
|
160
|
+
import { index, pgTable, text, tsvector } from "drizzle-orm/pg-core";
|
|
161
|
+
|
|
162
|
+
export const products = pgTable("products", {
|
|
163
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
164
|
+
name: varchar("name", { length: 255 }).notNull(),
|
|
165
|
+
description: text("description"),
|
|
166
|
+
// Generated tsvector column for search
|
|
167
|
+
searchVector: tsvector("search_vector").generatedAlwaysAs(
|
|
168
|
+
sql`to_tsvector('english', coalesce(${products.name}, '') || ' ' || coalesce(${products.description}, ''))`
|
|
169
|
+
),
|
|
170
|
+
}, (table) => ({
|
|
171
|
+
// GIN index for fast full-text search
|
|
172
|
+
searchIdx: index("products_search_idx").using("gin", table.searchVector),
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
// Query with full-text search
|
|
176
|
+
const results = await db
|
|
177
|
+
.select()
|
|
178
|
+
.from(products)
|
|
179
|
+
.where(sql`${products.searchVector} @@ plainto_tsquery('english', ${query})`);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Key patterns:**
|
|
183
|
+
- Use `tsvector` for searchable content
|
|
184
|
+
- `generatedAlwaysAs` auto-updates on row changes
|
|
185
|
+
- GIN index makes searches fast (vs sequential scan)
|
|
186
|
+
- `plainto_tsquery` for simple user queries
|
|
187
|
+
|
|
142
188
|
## Migration Strategy
|
|
143
189
|
|
|
144
190
|
### Generate vs Push
|
|
@@ -429,11 +475,14 @@ if (duration > 100) {
|
|
|
429
475
|
|
|
430
476
|
- [Drizzle ORM Documentation](https://orm.drizzle.team/docs/overview) - Relations, prepared statements, migrations
|
|
431
477
|
- [PostgreSQL Indexes](https://www.postgresql.org/docs/current/indexes.html) - Index types and usage
|
|
478
|
+
- [PostgreSQL Full-Text Search](https://www.postgresql.org/docs/current/textsearch.html) - tsvector, GIN indexes
|
|
432
479
|
- [Drizzle Kit](https://orm.drizzle.team/docs/kit-overview) - Migration management
|
|
433
480
|
|
|
434
481
|
**Version Notes:**
|
|
435
|
-
- Drizzle ORM 0.30+: Improved relations API
|
|
482
|
+
- Drizzle ORM 0.30+: Improved relations API, identity column support
|
|
436
483
|
- Drizzle Kit 0.20+: Enhanced migration generation
|
|
484
|
+
- PostgreSQL 10+: Identity columns (prefer over serial)
|
|
485
|
+
- PostgreSQL 15+: Improved JSON and full-text performance
|
|
437
486
|
|
|
438
487
|
## Red Flags - STOP and Add Indexes
|
|
439
488
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: react
|
|
3
|
-
description: Use when writing React components,
|
|
3
|
+
description: Use when writing React components, creating hooks, using useState/useEffect/useReducer, building Next.js pages, implementing Server Components or Client Components, working with Remix loaders, Astro islands, or Vite SPA. Use for React performance issues, re-render problems, or component refactoring.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# React Development
|
|
@@ -229,6 +229,60 @@ export function SignupForm() {
|
|
|
229
229
|
|
|
230
230
|
## React 19 Features
|
|
231
231
|
|
|
232
|
+
### React Compiler (Auto-Memoization)
|
|
233
|
+
|
|
234
|
+
React Compiler automatically adds memoization - no more manual `useMemo`, `useCallback`, or `React.memo`.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// ❌ OLD: Manual memoization everywhere
|
|
238
|
+
const MemoizedComponent = React.memo(({ user }) => {
|
|
239
|
+
const formattedName = useMemo(() => formatName(user), [user]);
|
|
240
|
+
const handleClick = useCallback(() => saveUser(user), [user]);
|
|
241
|
+
return <button onClick={handleClick}>{formattedName}</button>;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// ✅ NEW: React Compiler handles it automatically
|
|
245
|
+
function UserButton({ user }) {
|
|
246
|
+
const formattedName = formatName(user);
|
|
247
|
+
const handleClick = () => saveUser(user);
|
|
248
|
+
return <button onClick={handleClick}>{formattedName}</button>;
|
|
249
|
+
}
|
|
250
|
+
// Compiler adds memoization during build - 25-40% fewer re-renders
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Key benefits:**
|
|
254
|
+
- No manual memoization needed
|
|
255
|
+
- Reduces bundle size (no memo wrappers)
|
|
256
|
+
- 25-40% fewer re-renders in typical apps
|
|
257
|
+
- Works with existing React 19 codebases
|
|
258
|
+
|
|
259
|
+
**Enable in Next.js 15+:**
|
|
260
|
+
```javascript
|
|
261
|
+
// next.config.js
|
|
262
|
+
module.exports = {
|
|
263
|
+
experimental: {
|
|
264
|
+
reactCompiler: true,
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Server/Client Component Decision Tree
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
Need this in the component?
|
|
273
|
+
├── Event handlers (onClick, onChange) → 'use client'
|
|
274
|
+
├── useState, useEffect, useReducer → 'use client'
|
|
275
|
+
├── Browser APIs (localStorage, window) → 'use client'
|
|
276
|
+
├── Third-party client libs (charts, maps) → 'use client'
|
|
277
|
+
│
|
|
278
|
+
├── Data fetching from DB/API → Server Component ✅
|
|
279
|
+
├── Access backend resources → Server Component ✅
|
|
280
|
+
├── Keep sensitive data (tokens, keys) → Server Component ✅
|
|
281
|
+
└── Reduce client bundle → Server Component ✅
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Rule of thumb:** Start with Server Components. Only add 'use client' when you hit a boundary that requires it.
|
|
285
|
+
|
|
232
286
|
### use() Hook - Read Promises/Context
|
|
233
287
|
|
|
234
288
|
```typescript
|
|
@@ -279,6 +333,59 @@ function TodoList({ todos }: { todos: Todo[] }) {
|
|
|
279
333
|
|
|
280
334
|
**Auto-reverts on error** - no manual rollback needed.
|
|
281
335
|
|
|
336
|
+
### Partial Pre-rendering (PPR) - Next.js 15+
|
|
337
|
+
|
|
338
|
+
Combine static shell with dynamic content in a single request:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
// app/products/[id]/page.tsx
|
|
342
|
+
import { Suspense } from 'react';
|
|
343
|
+
|
|
344
|
+
// Static shell - pre-rendered at build time
|
|
345
|
+
export default async function ProductPage({ params }) {
|
|
346
|
+
const product = await getProduct(params.id); // Cached/static
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
<div>
|
|
350
|
+
{/* Static content - instant load */}
|
|
351
|
+
<h1>{product.name}</h1>
|
|
352
|
+
<p>{product.description}</p>
|
|
353
|
+
|
|
354
|
+
{/* Dynamic content - streams in */}
|
|
355
|
+
<Suspense fallback={<PriceSkeleton />}>
|
|
356
|
+
<DynamicPrice productId={params.id} />
|
|
357
|
+
</Suspense>
|
|
358
|
+
|
|
359
|
+
<Suspense fallback={<InventorySkeleton />}>
|
|
360
|
+
<InventoryStatus productId={params.id} />
|
|
361
|
+
</Suspense>
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Dynamic component - fetches real-time data
|
|
367
|
+
async function DynamicPrice({ productId }) {
|
|
368
|
+
const price = await getCurrentPrice(productId); // Always fresh
|
|
369
|
+
return <span className="price">${price}</span>;
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**Enable PPR:**
|
|
374
|
+
```javascript
|
|
375
|
+
// next.config.js
|
|
376
|
+
module.exports = {
|
|
377
|
+
experimental: {
|
|
378
|
+
ppr: true,
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
**Benefits:**
|
|
384
|
+
- Static shell loads instantly (like SSG)
|
|
385
|
+
- Dynamic parts stream in (like SSR)
|
|
386
|
+
- Best of both worlds in one request
|
|
387
|
+
- No full-page waterfall
|
|
388
|
+
|
|
282
389
|
## Streaming & Suspense
|
|
283
390
|
|
|
284
391
|
### Full Page Streaming with loading.tsx
|
|
@@ -384,12 +491,15 @@ test('shows fallback then content', async () => {
|
|
|
384
491
|
|
|
385
492
|
- [Next.js Documentation](https://nextjs.org/docs) - App Router, Server Components, Server Actions
|
|
386
493
|
- [React 19 Release](https://react.dev/blog/2024/12/05/react-19) - use(), useActionState, useOptimistic
|
|
494
|
+
- [React Compiler](https://react.dev/learn/react-compiler) - Auto-memoization setup and usage
|
|
387
495
|
- [React Server Components](https://react.dev/reference/rsc/server-components) - Official RSC guide
|
|
388
496
|
|
|
389
497
|
**Version Notes:**
|
|
390
498
|
- Next.js 14+: App Router stable, Server Actions stable
|
|
391
|
-
- Next.js 15:
|
|
499
|
+
- Next.js 15: Turbopack stable, React Compiler support, PPR experimental
|
|
392
500
|
- React 19: use(), Actions, useOptimistic, useActionState
|
|
501
|
+
- React 19.2+: Partial Pre-rendering (PPR), enhanced Suspense
|
|
502
|
+
- React Compiler: Auto-memoization, 25-40% fewer re-renders
|
|
393
503
|
|
|
394
504
|
## Red Flags - STOP and Restructure
|
|
395
505
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: seo
|
|
3
|
-
description: Use when
|
|
3
|
+
description: Use when adding page metadata, implementing OpenGraph tags, creating JSON-LD structured data, generating sitemaps, optimizing LCP/INP/CLS, or configuring robots.txt. Use for Next.js Metadata API, Article/Product/FAQ schemas, or image optimization with next/image.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SEO Optimization
|
|
@@ -128,6 +128,35 @@ input.addEventListener('input', (e) => {
|
|
|
128
128
|
|
|
129
129
|
### 2. Use Next.js Metadata API Correctly
|
|
130
130
|
|
|
131
|
+
#### Root Layout: metadataBase + title.template
|
|
132
|
+
|
|
133
|
+
**Critical:** Set `metadataBase` in your root layout. Without it, relative OG image URLs break.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
// app/layout.tsx
|
|
137
|
+
import { Metadata } from 'next';
|
|
138
|
+
|
|
139
|
+
export const metadata: Metadata = {
|
|
140
|
+
metadataBase: new URL('https://site.com'), // REQUIRED for OG images
|
|
141
|
+
title: {
|
|
142
|
+
default: 'Brand Name',
|
|
143
|
+
template: '%s | Brand Name', // Auto-appends to all pages
|
|
144
|
+
},
|
|
145
|
+
description: 'Default site description',
|
|
146
|
+
openGraph: {
|
|
147
|
+
siteName: 'Brand Name',
|
|
148
|
+
locale: 'en_US',
|
|
149
|
+
type: 'website',
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Why metadataBase matters:**
|
|
155
|
+
- Without it: `images: ['/og.png']` → broken URL
|
|
156
|
+
- With it: `images: ['/og.png']` → `https://site.com/og.png`
|
|
157
|
+
|
|
158
|
+
#### Page-Level Metadata
|
|
159
|
+
|
|
131
160
|
```typescript
|
|
132
161
|
// app/products/[slug]/page.tsx
|
|
133
162
|
import { Metadata } from 'next';
|
|
@@ -136,11 +165,11 @@ export async function generateMetadata({ params }): Promise<Metadata> {
|
|
|
136
165
|
const product = await getProduct(params.slug);
|
|
137
166
|
|
|
138
167
|
return {
|
|
139
|
-
title:
|
|
168
|
+
title: product.name, // Becomes "Product Name | Brand Name" via template
|
|
140
169
|
description: truncate(product.description, 155), // Max 155 chars
|
|
141
170
|
|
|
142
171
|
alternates: {
|
|
143
|
-
canonical:
|
|
172
|
+
canonical: `/products/${product.slug}`, // Relative OK with metadataBase
|
|
144
173
|
},
|
|
145
174
|
|
|
146
175
|
// OpenGraph - use 'product' for e-commerce
|
|
@@ -278,6 +307,87 @@ const faqJsonLd = {
|
|
|
278
307
|
};
|
|
279
308
|
```
|
|
280
309
|
|
|
310
|
+
#### BreadcrumbList Schema
|
|
311
|
+
|
|
312
|
+
Shows breadcrumb trail in search results:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
const breadcrumbJsonLd = {
|
|
316
|
+
'@context': 'https://schema.org',
|
|
317
|
+
'@type': 'BreadcrumbList',
|
|
318
|
+
itemListElement: [
|
|
319
|
+
{
|
|
320
|
+
'@type': 'ListItem',
|
|
321
|
+
position: 1,
|
|
322
|
+
name: 'Home',
|
|
323
|
+
item: 'https://site.com',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
'@type': 'ListItem',
|
|
327
|
+
position: 2,
|
|
328
|
+
name: 'Products',
|
|
329
|
+
item: 'https://site.com/products',
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
'@type': 'ListItem',
|
|
333
|
+
position: 3,
|
|
334
|
+
name: product.name,
|
|
335
|
+
item: `https://site.com/products/${product.slug}`,
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
};
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### 6. Answer Engine Optimization (AEO)
|
|
342
|
+
|
|
343
|
+
AI search engines (ChatGPT, Perplexity, Claude) are becoming traffic sources. Optimize for them:
|
|
344
|
+
|
|
345
|
+
**Key AEO strategies:**
|
|
346
|
+
|
|
347
|
+
| Strategy | Implementation |
|
|
348
|
+
|----------|----------------|
|
|
349
|
+
| **FAQ sections** | Add FAQPage schema - AI pulls from structured Q&A |
|
|
350
|
+
| **Direct answers** | Start content with clear, factual statements |
|
|
351
|
+
| **Structured data** | Schema.org markup helps AI understand content |
|
|
352
|
+
| **Topic authority** | Comprehensive coverage on topic clusters |
|
|
353
|
+
| **Citation-friendly** | Include stats, dates, sources that AI can cite |
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
// ✅ GOOD: Content structure for AI search
|
|
357
|
+
function ProductPage({ product }) {
|
|
358
|
+
return (
|
|
359
|
+
<>
|
|
360
|
+
{/* Direct answer for AI to extract */}
|
|
361
|
+
<p className="lead">
|
|
362
|
+
The {product.name} is a {product.category} that {product.keyBenefit}.
|
|
363
|
+
Priced at ${product.price}, it's ideal for {product.targetAudience}.
|
|
364
|
+
</p>
|
|
365
|
+
|
|
366
|
+
{/* FAQ section with schema */}
|
|
367
|
+
<section>
|
|
368
|
+
<h2>Frequently Asked Questions</h2>
|
|
369
|
+
{product.faqs.map(faq => (
|
|
370
|
+
<details key={faq.id}>
|
|
371
|
+
<summary>{faq.question}</summary>
|
|
372
|
+
<p>{faq.answer}</p>
|
|
373
|
+
</details>
|
|
374
|
+
))}
|
|
375
|
+
</section>
|
|
376
|
+
|
|
377
|
+
{/* Structured data for both Google and AI */}
|
|
378
|
+
<JsonLd data={productJsonLd} />
|
|
379
|
+
<JsonLd data={faqJsonLd} />
|
|
380
|
+
</>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**Why AEO matters:**
|
|
386
|
+
- 40% of Gen Z uses TikTok/AI for search over Google
|
|
387
|
+
- AI search engines cite well-structured content
|
|
388
|
+
- FAQ sections appear in AI answers
|
|
389
|
+
- Structured data = machine-readable = AI-friendly
|
|
390
|
+
|
|
281
391
|
## Sitemap & Robots
|
|
282
392
|
|
|
283
393
|
### Dynamic Sitemap Generation
|
|
@@ -408,13 +518,15 @@ new PerformanceObserver((list) => {
|
|
|
408
518
|
- [Core Web Vitals INP Guide](https://web.dev/articles/inp) - Official INP optimization patterns
|
|
409
519
|
- [Optimize INP](https://web.dev/articles/optimize-inp) - Three-phase optimization approach
|
|
410
520
|
- [Next.js Image Component](https://nextjs.org/docs/app/api-reference/components/image) - priority, placeholder, sizes
|
|
411
|
-
- [Next.js Metadata API](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) - generateMetadata
|
|
521
|
+
- [Next.js Metadata API](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) - generateMetadata, metadataBase
|
|
412
522
|
- [Schema.org](https://schema.org/) - Structured data vocabulary
|
|
523
|
+
- [Rich Results Test](https://search.google.com/test/rich-results) - Validate structured data
|
|
413
524
|
|
|
414
525
|
**Version Notes:**
|
|
415
526
|
- INP replaced FID as Core Web Vital (March 2024)
|
|
416
|
-
- Next.js 15: Enhanced Image component
|
|
527
|
+
- Next.js 15: Enhanced Image component, metadataBase required for OG
|
|
417
528
|
- Good INP: < 200ms (improving from 500ms → 200ms = 22% engagement boost)
|
|
529
|
+
- AEO emerging: AI search engines (ChatGPT, Perplexity) use structured data
|
|
418
530
|
|
|
419
531
|
## Red Flags - STOP and Fix
|
|
420
532
|
|
package/dist/cli.js
CHANGED
|
@@ -1598,15 +1598,18 @@ function getPackageSkillsDir() {
|
|
|
1598
1598
|
const packageRoot = join15(dirname2(currentFilePath), "..");
|
|
1599
1599
|
return join15(packageRoot, ".claude", "skills");
|
|
1600
1600
|
}
|
|
1601
|
-
async function copyDirectory(srcDir, destDir, result, baseDir) {
|
|
1601
|
+
async function copyDirectory(srcDir, destDir, result, baseDir, skipDirs) {
|
|
1602
1602
|
await ensureDir(destDir);
|
|
1603
1603
|
const entries = await readdir(srcDir, { withFileTypes: true });
|
|
1604
1604
|
for (const entry of entries) {
|
|
1605
|
+
if (skipDirs && entry.isDirectory() && skipDirs.includes(entry.name)) {
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1605
1608
|
const srcPath = join15(srcDir, entry.name);
|
|
1606
1609
|
const destPath = join15(destDir, entry.name);
|
|
1607
1610
|
const relativePath = destPath.replace(baseDir + "/", "");
|
|
1608
1611
|
if (entry.isDirectory()) {
|
|
1609
|
-
await copyDirectory(srcPath, destPath, result, baseDir);
|
|
1612
|
+
await copyDirectory(srcPath, destPath, result, baseDir, skipDirs);
|
|
1610
1613
|
} else {
|
|
1611
1614
|
if (existsSync5(destPath)) {
|
|
1612
1615
|
const backupPath = await backupFile(destPath);
|
|
@@ -1619,7 +1622,7 @@ async function copyDirectory(srcDir, destDir, result, baseDir) {
|
|
|
1619
1622
|
}
|
|
1620
1623
|
}
|
|
1621
1624
|
}
|
|
1622
|
-
async function generateClaudeSkills(targetDir) {
|
|
1625
|
+
async function generateClaudeSkills(targetDir, options) {
|
|
1623
1626
|
const result = {
|
|
1624
1627
|
created: [],
|
|
1625
1628
|
modified: [],
|
|
@@ -1635,7 +1638,11 @@ async function generateClaudeSkills(targetDir) {
|
|
|
1635
1638
|
return result;
|
|
1636
1639
|
}
|
|
1637
1640
|
await ensureDir(join15(targetDir, ".claude"));
|
|
1638
|
-
|
|
1641
|
+
const skipDirs = [];
|
|
1642
|
+
if (!options?.includeAsana) {
|
|
1643
|
+
skipDirs.push("asana");
|
|
1644
|
+
}
|
|
1645
|
+
await copyDirectory(packageSkillsDir, targetSkillsDir, result, targetDir, skipDirs);
|
|
1639
1646
|
return result;
|
|
1640
1647
|
}
|
|
1641
1648
|
|
|
@@ -2201,7 +2208,9 @@ async function runInit(targetDir = process.cwd()) {
|
|
|
2201
2208
|
await generateContributing(targetDir, !!config.asanaBaseUrl, config.packageManager)
|
|
2202
2209
|
);
|
|
2203
2210
|
results.push(await generateQuickReference(targetDir, config.packageManager));
|
|
2204
|
-
results.push(await generateClaudeSkills(targetDir
|
|
2211
|
+
results.push(await generateClaudeSkills(targetDir, {
|
|
2212
|
+
includeAsana: !!config.asanaBaseUrl
|
|
2213
|
+
}));
|
|
2205
2214
|
results.push(await updateProjectPackageJson(targetDir, config));
|
|
2206
2215
|
spinner4.stop("Configuration files generated!");
|
|
2207
2216
|
} catch (error) {
|
|
@@ -2662,7 +2671,7 @@ ${pc4.bold("Branches")}
|
|
|
2662
2671
|
// package.json
|
|
2663
2672
|
var package_default = {
|
|
2664
2673
|
name: "@raftlabs/raftstack",
|
|
2665
|
-
version: "1.
|
|
2674
|
+
version: "1.7.1",
|
|
2666
2675
|
description: "CLI tool for setting up Git hooks, commit conventions, and GitHub integration",
|
|
2667
2676
|
type: "module",
|
|
2668
2677
|
main: "./dist/index.js",
|