@champpaba/claude-agent-kit 2.2.0 ā 2.2.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/CLAUDE.md +73 -7
- package/.claude/commands/csetup.md +530 -138
- package/README.md +20 -5
- package/package.json +1 -1
package/.claude/CLAUDE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# CLAUDE.md
|
|
2
2
|
|
|
3
3
|
> **Navigation Hub for AI Agents**
|
|
4
|
-
> **Template Version:** 2.
|
|
5
|
-
> **Latest:**
|
|
4
|
+
> **Template Version:** 2.3.0 - Zero-Maintenance Tech Detection
|
|
5
|
+
> **Latest:** Dynamic library detection via Context7 (any language, no hardcoded mappings) + Claude 4.5 optimized agents
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -131,18 +131,36 @@ Universal, framework-agnostic template for AI-assisted development.
|
|
|
131
131
|
|
|
132
132
|
---
|
|
133
133
|
|
|
134
|
-
## š Best Practices System (
|
|
134
|
+
## š Best Practices System (v2.3.0 - Zero Maintenance)
|
|
135
135
|
|
|
136
136
|
**Quick Summary:**
|
|
137
|
-
- `/csetup` **
|
|
138
|
-
- **
|
|
137
|
+
- `/csetup` **dynamically detects any library** from spec text + package files (no hardcoded mappings)
|
|
138
|
+
- **Works with any language:** JavaScript, Python, Rust, Go, PHP, Ruby - automatically
|
|
139
|
+
- **Context7 validates** each potential library name and resolves to official docs
|
|
139
140
|
- Files created in `.claude/contexts/domain/project/best-practices/`
|
|
140
141
|
- **Agents read** best practices before coding (validated by agent-executor)
|
|
141
|
-
|
|
142
|
+
|
|
143
|
+
**Key Change in v2.3.0:**
|
|
144
|
+
- ā Old: Hardcoded regex patterns + manual ID mappings (required maintenance)
|
|
145
|
+
- ā
New: NLP extraction + Context7 resolution (zero maintenance, any library)
|
|
146
|
+
|
|
147
|
+
**Detection Sources:**
|
|
148
|
+
| Source | Examples |
|
|
149
|
+
|--------|----------|
|
|
150
|
+
| Spec files | proposal.md, design.md, tasks.md |
|
|
151
|
+
| JS/TS | package.json, import statements |
|
|
152
|
+
| Python | requirements.txt, pyproject.toml, imports |
|
|
153
|
+
| Rust | Cargo.toml, use statements |
|
|
154
|
+
| Go | go.mod, import statements |
|
|
155
|
+
| PHP | composer.json |
|
|
156
|
+
| Ruby | Gemfile |
|
|
142
157
|
|
|
143
158
|
**Flow:**
|
|
144
159
|
```
|
|
145
|
-
/csetup ā
|
|
160
|
+
/csetup ā extract potential library names from ALL text sources
|
|
161
|
+
ā Context7 resolve-library-id (validates if real library)
|
|
162
|
+
ā Context7 get-library-docs (fetch best practices)
|
|
163
|
+
ā generate .md files for each verified library
|
|
146
164
|
/cdev ā inject paths into prompt ā agent reads ā validation checks
|
|
147
165
|
```
|
|
148
166
|
|
|
@@ -278,6 +296,54 @@ User: "Build login system"
|
|
|
278
296
|
|
|
279
297
|
---
|
|
280
298
|
|
|
299
|
+
## š v2.3.0: Zero-Maintenance Tech Stack Detection
|
|
300
|
+
|
|
301
|
+
**Problem Solved:** Previously, `/csetup` required hardcoded regex patterns and Context7 ID mappings for each library. Adding support for new libraries (like SQLAlchemy, Pydantic, Rust crates) required code changes.
|
|
302
|
+
|
|
303
|
+
**Solution:** Dynamic detection that works with any library in any language without maintenance.
|
|
304
|
+
|
|
305
|
+
### How It Works
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
1. Extract potential library names from ALL text sources:
|
|
309
|
+
- Spec files (proposal.md, design.md, tasks.md)
|
|
310
|
+
- Package files (package.json, requirements.txt, Cargo.toml, go.mod, etc.)
|
|
311
|
+
- Import statements in code snippets
|
|
312
|
+
- Prose mentions ("using FastAPI", "with Prisma")
|
|
313
|
+
|
|
314
|
+
2. Send each candidate to Context7 resolve-library-id:
|
|
315
|
+
- If Context7 recognizes it ā confirmed library ā
|
|
316
|
+
- If not recognized ā not a library, skip ā
|
|
317
|
+
|
|
318
|
+
3. For confirmed libraries, fetch best practices:
|
|
319
|
+
- Context7 get-library-docs with "best practices" topic
|
|
320
|
+
- Generate .md file with patterns, anti-patterns, checklist
|
|
321
|
+
|
|
322
|
+
4. Result: Best practices for ANY library, automatically!
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Benefits
|
|
326
|
+
|
|
327
|
+
| Aspect | Before (v1.8.0) | After (v2.3.0) |
|
|
328
|
+
|--------|-----------------|----------------|
|
|
329
|
+
| New library support | Manual code change | Automatic |
|
|
330
|
+
| Python stack | Partial (FastAPI, Django only) | Full (SQLAlchemy, Pydantic, Click, etc.) |
|
|
331
|
+
| Rust support | None | Automatic |
|
|
332
|
+
| Go support | None | Automatic |
|
|
333
|
+
| Maintenance | Required for each library | Zero |
|
|
334
|
+
|
|
335
|
+
### Files Changed
|
|
336
|
+
|
|
337
|
+
| File | Change |
|
|
338
|
+
|------|--------|
|
|
339
|
+
| `csetup.md` Step 2.7 | Complete rewrite with dynamic detection |
|
|
340
|
+
| `extractPotentialLibraryNames()` | New helper for NLP extraction |
|
|
341
|
+
| `parseContext7Response()` | New helper for Context7 response parsing |
|
|
342
|
+
| `generateBestPracticesFile()` | Updated signature, includes Context7 ID |
|
|
343
|
+
| `detectAdditionalTech()` | Deprecated, delegates to new system |
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
281
347
|
## š v2.0.0: Claude 4.5 Optimization + Design System v2.0
|
|
282
348
|
|
|
283
349
|
**Based on:** [Claude 4 Best Practices](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-4-best-practices)
|
|
@@ -476,173 +476,477 @@ const featureAnalysis = {
|
|
|
476
476
|
|
|
477
477
|
---
|
|
478
478
|
|
|
479
|
-
### Step 2.7: Auto-Setup Best Practices (
|
|
479
|
+
### Step 2.7: Auto-Setup Best Practices (v2.3.0 - Dynamic Detection)
|
|
480
480
|
|
|
481
|
-
> **
|
|
481
|
+
> **Zero-Maintenance Design:** Automatically detects any library/framework from spec text and resolves via Context7.
|
|
482
|
+
> **WHY:** Hardcoded mappings require constant maintenance and miss new libraries. Dynamic resolution works with any language (Python, Rust, Go, etc.) without code changes.
|
|
482
483
|
|
|
483
484
|
```typescript
|
|
484
|
-
//
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
)
|
|
495
|
-
|
|
485
|
+
// ============================================================
|
|
486
|
+
// STEP 2.7: Dynamic Tech Stack Detection & Best Practices
|
|
487
|
+
// ============================================================
|
|
488
|
+
|
|
489
|
+
output(`\nš Detecting Tech Stack (Dynamic Resolution)...`)
|
|
490
|
+
|
|
491
|
+
// 1. Gather text from ALL relevant sources
|
|
492
|
+
const textSources = {
|
|
493
|
+
proposal: Read(`openspec/changes/${changeId}/proposal.md`) || '',
|
|
494
|
+
tasks: Read(`openspec/changes/${changeId}/tasks.md`) || '',
|
|
495
|
+
design: fileExists(`openspec/changes/${changeId}/design.md`)
|
|
496
|
+
? Read(`openspec/changes/${changeId}/design.md`) : '',
|
|
497
|
+
packageJson: fileExists('package.json') ? Read('package.json') : '',
|
|
498
|
+
requirementsTxt: fileExists('requirements.txt') ? Read('requirements.txt') : '',
|
|
499
|
+
pyprojectToml: fileExists('pyproject.toml') ? Read('pyproject.toml') : '',
|
|
500
|
+
cargoToml: fileExists('Cargo.toml') ? Read('Cargo.toml') : '',
|
|
501
|
+
goMod: fileExists('go.mod') ? Read('go.mod') : '',
|
|
502
|
+
composerJson: fileExists('composer.json') ? Read('composer.json') : '',
|
|
503
|
+
gemfile: fileExists('Gemfile') ? Read('Gemfile') : ''
|
|
496
504
|
}
|
|
497
505
|
|
|
498
|
-
|
|
499
|
-
let designStack = []
|
|
500
|
-
const designPath = `openspec/changes/${changeId}/design.md`
|
|
501
|
-
if (fileExists(designPath)) {
|
|
502
|
-
const designContent = Read(designPath)
|
|
503
|
-
// Look for tech stack section
|
|
504
|
-
const techMatch = designContent.match(/tech.*stack|architecture|framework/gi)
|
|
505
|
-
if (techMatch) {
|
|
506
|
-
designStack = extractTechFromText(designContent)
|
|
507
|
-
output(` š From design.md: ${designStack.join(', ') || 'none'}`)
|
|
508
|
-
}
|
|
509
|
-
}
|
|
506
|
+
const allText = Object.values(textSources).join('\n')
|
|
510
507
|
|
|
511
|
-
//
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
prisma: /\b(prisma)\b/i,
|
|
524
|
-
drizzle: /\b(drizzle)\b/i,
|
|
525
|
-
postgres: /\b(postgres|postgresql)\b/i,
|
|
526
|
-
mongodb: /\b(mongodb|mongoose)\b/i,
|
|
527
|
-
tailwind: /\b(tailwind)\b/i,
|
|
528
|
-
typescript: /\b(typescript)\b/i,
|
|
529
|
-
vitest: /\b(vitest)\b/i,
|
|
530
|
-
jest: /\b(jest)\b/i,
|
|
531
|
-
playwright: /\b(playwright)\b/i
|
|
532
|
-
}
|
|
508
|
+
// 2. Extract library names using semantic analysis (TRUE zero-maintenance)
|
|
509
|
+
// WHY: Pattern-based extraction still requires maintenance.
|
|
510
|
+
// Instead, use Claude's understanding to identify libraries from context.
|
|
511
|
+
output(` š§ Analyzing text semantically...`)
|
|
512
|
+
|
|
513
|
+
const potentialLibraries = await extractLibrariesSemantically(allText)
|
|
514
|
+
|
|
515
|
+
output(` š Found ${potentialLibraries.length} potential libraries to verify`)
|
|
516
|
+
|
|
517
|
+
// 3. Resolve each potential library with Context7 (validate it's a real library)
|
|
518
|
+
const resolvedLibraries = []
|
|
519
|
+
const resolutionCache = new Map()
|
|
533
520
|
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
521
|
+
for (const candidate of potentialLibraries) {
|
|
522
|
+
if (resolutionCache.has(candidate.toLowerCase())) continue
|
|
523
|
+
|
|
524
|
+
try {
|
|
525
|
+
const result = await mcp__context7__resolve_library_id({
|
|
526
|
+
libraryName: candidate
|
|
527
|
+
})
|
|
528
|
+
|
|
529
|
+
const bestMatch = parseContext7Response(result, candidate)
|
|
530
|
+
|
|
531
|
+
if (bestMatch && bestMatch.score >= 60) {
|
|
532
|
+
resolvedLibraries.push({
|
|
533
|
+
name: candidate,
|
|
534
|
+
context7Id: bestMatch.id,
|
|
535
|
+
title: bestMatch.title,
|
|
536
|
+
snippets: bestMatch.snippets,
|
|
537
|
+
score: bestMatch.score
|
|
538
|
+
})
|
|
539
|
+
resolutionCache.set(candidate.toLowerCase(), bestMatch)
|
|
540
|
+
output(` ā
${candidate} ā ${bestMatch.id} (${bestMatch.snippets} snippets)`)
|
|
541
|
+
}
|
|
542
|
+
} catch (error) {
|
|
543
|
+
resolutionCache.set(candidate.toLowerCase(), null)
|
|
538
544
|
}
|
|
539
545
|
}
|
|
540
|
-
output(` š From proposal/tasks: ${proposalStack.join(', ') || 'none'}`)
|
|
541
546
|
|
|
542
|
-
|
|
543
|
-
|
|
547
|
+
output(`\nš Verified Libraries: ${resolvedLibraries.length}`)
|
|
548
|
+
resolvedLibraries.forEach(lib => {
|
|
549
|
+
output(` - ${lib.title} (${lib.context7Id})`)
|
|
550
|
+
})
|
|
544
551
|
|
|
545
|
-
//
|
|
546
|
-
if (
|
|
547
|
-
output(`\nā ļø
|
|
552
|
+
// 4. If no libraries detected, ask user for guidance
|
|
553
|
+
if (resolvedLibraries.length === 0) {
|
|
554
|
+
output(`\nā ļø No libraries auto-detected from spec files`)
|
|
548
555
|
|
|
549
556
|
const answer = await askUserQuestion({
|
|
550
557
|
questions: [{
|
|
551
|
-
question: '
|
|
552
|
-
header: '
|
|
558
|
+
question: 'Enter the main libraries/frameworks for this project (comma-separated):',
|
|
559
|
+
header: 'Libraries',
|
|
553
560
|
options: [
|
|
554
|
-
{ label: '
|
|
555
|
-
{ label: '
|
|
556
|
-
{ label: '
|
|
557
|
-
{ label: '
|
|
561
|
+
{ label: 'Skip', description: 'Continue without library-specific best practices' },
|
|
562
|
+
{ label: 'React, Next.js', description: 'Common frontend stack' },
|
|
563
|
+
{ label: 'FastAPI, SQLAlchemy, Pydantic', description: 'Python API stack' },
|
|
564
|
+
{ label: 'Express, Prisma', description: 'Node.js backend stack' }
|
|
558
565
|
],
|
|
559
|
-
multiSelect:
|
|
566
|
+
multiSelect: false
|
|
560
567
|
}]
|
|
561
568
|
})
|
|
562
569
|
|
|
563
|
-
|
|
564
|
-
|
|
570
|
+
if (!answer.includes('Skip')) {
|
|
571
|
+
// Parse user input and resolve each
|
|
572
|
+
const userLibraries = answer.split(',').map(s => s.trim()).filter(Boolean)
|
|
573
|
+
for (const lib of userLibraries) {
|
|
574
|
+
const result = await mcp__context7__resolve_library_id({ libraryName: lib })
|
|
575
|
+
const bestMatch = parseContext7Response(result, lib)
|
|
576
|
+
if (bestMatch) {
|
|
577
|
+
resolvedLibraries.push({
|
|
578
|
+
name: lib,
|
|
579
|
+
context7Id: bestMatch.id,
|
|
580
|
+
title: bestMatch.title,
|
|
581
|
+
snippets: bestMatch.snippets,
|
|
582
|
+
score: bestMatch.score
|
|
583
|
+
})
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
565
587
|
}
|
|
566
588
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
// 3. Check if best-practices already exist
|
|
589
|
+
// 5. Generate best-practices files for resolved libraries
|
|
570
590
|
const bpDir = '.claude/contexts/domain/project/best-practices/'
|
|
571
591
|
const existingBp = fileExists(bpDir) ? listFiles(bpDir) : []
|
|
572
592
|
|
|
573
|
-
|
|
574
|
-
|
|
593
|
+
// Filter to libraries that don't have best-practices yet
|
|
594
|
+
const newLibraries = resolvedLibraries.filter(lib => {
|
|
595
|
+
const safeName = lib.name.toLowerCase().replace(/[^a-z0-9]/g, '-')
|
|
596
|
+
return !existingBp.some(f => f.toLowerCase().includes(safeName))
|
|
575
597
|
})
|
|
576
598
|
|
|
577
|
-
|
|
578
|
-
if (missingBp.length > 0) {
|
|
599
|
+
if (newLibraries.length > 0) {
|
|
579
600
|
output(`\nš Generating Best Practices from Context7...`)
|
|
580
601
|
|
|
581
|
-
// Create directory
|
|
582
|
-
if (!fileExists(
|
|
583
|
-
mkdir(
|
|
602
|
+
// Create directory if needed
|
|
603
|
+
if (!fileExists(bpDir)) {
|
|
604
|
+
mkdir(bpDir)
|
|
584
605
|
}
|
|
585
606
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
react: '/facebook/react',
|
|
589
|
-
nextjs: '/vercel/next.js',
|
|
590
|
-
vue: '/vuejs/vue',
|
|
591
|
-
express: '/expressjs/express',
|
|
592
|
-
fastapi: '/fastapi/fastapi',
|
|
593
|
-
prisma: '/prisma/prisma',
|
|
594
|
-
drizzle: '/drizzle-team/drizzle-orm',
|
|
595
|
-
vitest: '/vitest-dev/vitest',
|
|
596
|
-
jest: '/jestjs/jest',
|
|
597
|
-
playwright: '/microsoft/playwright',
|
|
598
|
-
tailwind: '/tailwindlabs/tailwindcss'
|
|
599
|
-
}
|
|
607
|
+
for (const lib of newLibraries) {
|
|
608
|
+
output(` š Fetching ${lib.title} best practices...`)
|
|
600
609
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
output(` š Fetching ${tech} best practices...`)
|
|
606
|
-
|
|
607
|
-
// Query Context7 for best practices
|
|
608
|
-
const docs = await mcp__context7__get-library-docs({
|
|
609
|
-
context7CompatibleLibraryID: libraryId,
|
|
610
|
-
topic: 'best practices, common mistakes, anti-patterns, patterns',
|
|
610
|
+
try {
|
|
611
|
+
const docs = await mcp__context7__get_library_docs({
|
|
612
|
+
context7CompatibleLibraryID: lib.context7Id,
|
|
613
|
+
topic: 'best practices, patterns, anti-patterns, common mistakes',
|
|
611
614
|
mode: 'code'
|
|
612
615
|
})
|
|
613
616
|
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
Write(
|
|
617
|
+
const bpContent = generateBestPracticesFile(lib.title, docs, lib.context7Id)
|
|
618
|
+
const safeName = lib.name.toLowerCase().replace(/[^a-z0-9]/g, '-')
|
|
619
|
+
Write(`${bpDir}${safeName}.md`, bpContent)
|
|
617
620
|
|
|
618
|
-
output(` ā
${
|
|
619
|
-
}
|
|
620
|
-
output(` ā ļø ${
|
|
621
|
+
output(` ā
${safeName}.md generated`)
|
|
622
|
+
} catch (error) {
|
|
623
|
+
output(` ā ļø ${lib.title} - failed to fetch docs, skipping`)
|
|
621
624
|
}
|
|
622
625
|
}
|
|
623
626
|
|
|
624
|
-
// Generate index.md
|
|
625
|
-
generateBestPracticesIndex(
|
|
626
|
-
output(` ā
index.md
|
|
627
|
-
|
|
628
|
-
// Generate domain/index.md if not exists
|
|
629
|
-
if (!fileExists('.claude/contexts/domain/index.md')) {
|
|
630
|
-
generateDomainIndex('project', detectedStack)
|
|
631
|
-
output(` ā
domain/index.md generated`)
|
|
632
|
-
}
|
|
627
|
+
// Generate/update index.md
|
|
628
|
+
generateBestPracticesIndex(resolvedLibraries, changeId)
|
|
629
|
+
output(` ā
index.md updated`)
|
|
633
630
|
|
|
634
631
|
output(`\nā
Best Practices Setup Complete!`)
|
|
635
|
-
output(`
|
|
636
|
-
output(` Location:
|
|
632
|
+
output(` New files: ${newLibraries.length}`)
|
|
633
|
+
output(` Location: ${bpDir}`)
|
|
634
|
+
} else if (resolvedLibraries.length > 0) {
|
|
635
|
+
output(`\nā
Best Practices: Already configured for detected libraries`)
|
|
637
636
|
} else {
|
|
638
|
-
output(`\nā
Best Practices:
|
|
637
|
+
output(`\nā
Best Practices: Skipped (no libraries to configure)`)
|
|
639
638
|
}
|
|
640
639
|
|
|
641
|
-
//
|
|
640
|
+
// 6. Store resolved stack for context.md generation
|
|
642
641
|
const stackForContext = {
|
|
643
|
-
detected:
|
|
644
|
-
|
|
645
|
-
|
|
642
|
+
detected: resolvedLibraries.map(l => l.name),
|
|
643
|
+
resolved: resolvedLibraries,
|
|
644
|
+
bestPracticesPath: bpDir,
|
|
645
|
+
files: existingBp.concat(newLibraries.map(l => `${l.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}.md`))
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
### Helper: extractLibrariesSemantically() - PRIMARY
|
|
652
|
+
|
|
653
|
+
> **TRUE Zero-Maintenance:** Uses Claude's semantic understanding to extract library names from any text.
|
|
654
|
+
> **WHY:** Pattern-based regex always has edge cases. Claude understands context and meaning.
|
|
655
|
+
|
|
656
|
+
```typescript
|
|
657
|
+
async function extractLibrariesSemantically(text: string): Promise<string[]> {
|
|
658
|
+
// Truncate text if too long (keep first 8000 chars for efficiency)
|
|
659
|
+
const truncatedText = text.length > 8000 ? text.slice(0, 8000) + '\n...[truncated]' : text
|
|
660
|
+
|
|
661
|
+
// Use Claude to semantically extract library/framework names
|
|
662
|
+
// This is the MAIN Claude running /csetup, so we just analyze inline
|
|
663
|
+
const prompt = `
|
|
664
|
+
Analyze this text and extract ALL programming libraries, frameworks, and tools mentioned.
|
|
665
|
+
|
|
666
|
+
TEXT:
|
|
667
|
+
${truncatedText}
|
|
668
|
+
|
|
669
|
+
INSTRUCTIONS:
|
|
670
|
+
1. Look for any technology names (libraries, frameworks, ORMs, databases, tools)
|
|
671
|
+
2. Include both explicit mentions ("using Mastra") and implicit ones ("PostgreSQL database")
|
|
672
|
+
3. Return ONLY the library/framework names, one per line
|
|
673
|
+
4. Do NOT include generic terms like "API", "REST", "database" (unless it's a specific product name)
|
|
674
|
+
5. Do NOT include programming languages (Python, JavaScript, etc.) unless they're library names
|
|
675
|
+
6. Include package names from any language (npm, pip, cargo, go, etc.)
|
|
676
|
+
|
|
677
|
+
OUTPUT FORMAT (one name per line, nothing else):
|
|
678
|
+
`
|
|
679
|
+
|
|
680
|
+
// Since this runs within Main Claude context, we analyze inline
|
|
681
|
+
// The output parsing happens in the calling code
|
|
682
|
+
// For now, return a simple extraction that Main Claude will process
|
|
683
|
+
|
|
684
|
+
// Fallback: Use pattern-based extraction as baseline
|
|
685
|
+
// Main Claude will enhance this with semantic understanding
|
|
686
|
+
const patternBased = extractPotentialLibraryNames(text)
|
|
687
|
+
|
|
688
|
+
// Main Claude: When you execute this, also semantically analyze the text
|
|
689
|
+
// and add any libraries you identify from context that patterns missed.
|
|
690
|
+
// Example: "The system uses Mastra for orchestration" ā add "Mastra"
|
|
691
|
+
|
|
692
|
+
return patternBased
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**Execution Note for Main Claude:**
|
|
697
|
+
|
|
698
|
+
When running `/csetup`, after calling `extractLibrariesSemantically()`:
|
|
699
|
+
1. Review the spec text yourself (proposal.md, design.md, tasks.md)
|
|
700
|
+
2. Identify any library/framework names the patterns might have missed
|
|
701
|
+
3. Add them to the `potentialLibraries` array before Context7 validation
|
|
702
|
+
|
|
703
|
+
This hybrid approach ensures:
|
|
704
|
+
- Pattern extraction catches obvious cases quickly
|
|
705
|
+
- Claude's semantic understanding catches edge cases
|
|
706
|
+
- Context7 validates everything (filters out false positives)
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
### Helper: extractPotentialLibraryNames() - FALLBACK
|
|
711
|
+
|
|
712
|
+
> **Pattern-based fallback:** Provides baseline extraction. Main Claude enhances with semantic analysis.
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
function extractPotentialLibraryNames(text: string): string[] {
|
|
716
|
+
const candidates = new Set<string>()
|
|
717
|
+
|
|
718
|
+
// === Pattern 1: Package file dependencies ===
|
|
719
|
+
// package.json: "react": "^18.0.0" ā react
|
|
720
|
+
const npmDeps = text.match(/"([a-z@][a-z0-9._/-]*)"\s*:\s*"[\^~]?\d/gi) || []
|
|
721
|
+
npmDeps.forEach(m => {
|
|
722
|
+
const match = m.match(/"([^"]+)"/)
|
|
723
|
+
if (match) candidates.add(match[1].replace(/^@[^/]+\//, '')) // Strip scope
|
|
724
|
+
})
|
|
725
|
+
|
|
726
|
+
// requirements.txt: sqlalchemy==2.0.0 ā sqlalchemy
|
|
727
|
+
const pyDeps = text.match(/^([a-zA-Z][a-zA-Z0-9_-]*)\s*[=<>~!]/gm) || []
|
|
728
|
+
pyDeps.forEach(m => {
|
|
729
|
+
const match = m.match(/^([a-zA-Z][a-zA-Z0-9_-]*)/)
|
|
730
|
+
if (match) candidates.add(match[1])
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
// Cargo.toml: tokio = "1.0" ā tokio
|
|
734
|
+
const rustDeps = text.match(/^([a-z][a-z0-9_-]*)\s*=/gm) || []
|
|
735
|
+
rustDeps.forEach(m => {
|
|
736
|
+
const match = m.match(/^([a-z][a-z0-9_-]*)/)
|
|
737
|
+
if (match) candidates.add(match[1])
|
|
738
|
+
})
|
|
739
|
+
|
|
740
|
+
// go.mod: require github.com/gin-gonic/gin ā gin
|
|
741
|
+
const goDeps = text.match(/(?:require\s+)?github\.com\/[^/\s]+\/([a-z][a-z0-9_-]*)/gi) || []
|
|
742
|
+
goDeps.forEach(m => {
|
|
743
|
+
const match = m.match(/\/([a-z][a-z0-9_-]*)$/i)
|
|
744
|
+
if (match) candidates.add(match[1])
|
|
745
|
+
})
|
|
746
|
+
|
|
747
|
+
// === Pattern 2: Import statements ===
|
|
748
|
+
// Python: from sqlalchemy import, import pydantic
|
|
749
|
+
const pyImports = text.match(/(?:from|import)\s+([a-zA-Z][a-zA-Z0-9_]*)/g) || []
|
|
750
|
+
pyImports.forEach(m => {
|
|
751
|
+
const match = m.match(/(?:from|import)\s+([a-zA-Z][a-zA-Z0-9_]*)/)
|
|
752
|
+
if (match) candidates.add(match[1])
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
// JS/TS: import X from 'Y', require('Y')
|
|
756
|
+
const jsImports = text.match(/(?:from|require\s*\(\s*)['"]([a-zA-Z@][a-zA-Z0-9._/-]*)['"]/g) || []
|
|
757
|
+
jsImports.forEach(m => {
|
|
758
|
+
const match = m.match(/['"]([^'"]+)['"]/)
|
|
759
|
+
if (match) {
|
|
760
|
+
const pkg = match[1].replace(/^@[^/]+\//, '').split('/')[0]
|
|
761
|
+
candidates.add(pkg)
|
|
762
|
+
}
|
|
763
|
+
})
|
|
764
|
+
|
|
765
|
+
// Rust: use tokio::, extern crate serde
|
|
766
|
+
const rustImports = text.match(/(?:use|extern\s+crate)\s+([a-z][a-z0-9_]*)/g) || []
|
|
767
|
+
rustImports.forEach(m => {
|
|
768
|
+
const match = m.match(/(?:use|extern\s+crate)\s+([a-z][a-z0-9_]*)/)
|
|
769
|
+
if (match) candidates.add(match[1])
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
// === Pattern 3: Tech mentions in prose ===
|
|
773
|
+
// "using FastAPI", "with Prisma", "powered by Mastra"
|
|
774
|
+
const techMentions = text.match(/(?:using|with|via|built with|powered by)\s+([A-Z][a-zA-Z0-9.]*)/gi) || []
|
|
775
|
+
techMentions.forEach(m => {
|
|
776
|
+
const match = m.match(/\s([A-Z][a-zA-Z0-9.]*)$/i)
|
|
777
|
+
if (match) candidates.add(match[1])
|
|
778
|
+
})
|
|
779
|
+
|
|
780
|
+
// CamelCase words (FastAPI, SQLAlchemy, NextAuth)
|
|
781
|
+
const camelCase = text.match(/\b([A-Z][a-z]+(?:[A-Z][a-z]+)+)\b/g) || []
|
|
782
|
+
camelCase.forEach(w => candidates.add(w))
|
|
783
|
+
|
|
784
|
+
// === Pattern 3.5: PascalCase single words after tech keywords ===
|
|
785
|
+
// "Framework: Mastra", "ORM: Prisma", "Database: PostgreSQL"
|
|
786
|
+
// WHY: Many library names are single PascalCase words (Mastra, Prisma, Django, Flask)
|
|
787
|
+
const techKeywordPatterns = [
|
|
788
|
+
/(?:framework|library|orm|database|db|backend|frontend|ui|css|styling)[:\s]+([A-Z][a-z]+\w*)/gi,
|
|
789
|
+
/(?:built\s+with|powered\s+by|using|via|with)\s+([A-Z][a-z]+\w*)/gi,
|
|
790
|
+
/\*\*(?:framework|library|orm|database|backend|frontend)\*\*[:\s]+([A-Z][a-z]+\w*)/gi
|
|
791
|
+
]
|
|
792
|
+
techKeywordPatterns.forEach(pattern => {
|
|
793
|
+
const matches = text.matchAll(pattern)
|
|
794
|
+
for (const match of matches) {
|
|
795
|
+
if (match[1]) candidates.add(match[1])
|
|
796
|
+
}
|
|
797
|
+
})
|
|
798
|
+
|
|
799
|
+
// === Pattern 3.6: Standalone PascalCase in markdown lists ===
|
|
800
|
+
// "- Mastra for AI agents", "- PostgreSQL database", "* React frontend"
|
|
801
|
+
const mdListItems = text.match(/^[\s]*[-*]\s+([A-Z][a-z]+\w*)(?:\s|$)/gm) || []
|
|
802
|
+
mdListItems.forEach(m => {
|
|
803
|
+
const match = m.match(/[-*]\s+([A-Z][a-z]+\w*)/)
|
|
804
|
+
if (match) candidates.add(match[1])
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
// === Pattern 3.7: Words after "for" in tech context ===
|
|
808
|
+
// "Mastra for AI orchestration", "Drizzle for database"
|
|
809
|
+
const forPattern = text.match(/([A-Z][a-z]+\w*)\s+for\s+(?:ai|database|backend|frontend|api|web|mobile|server|client)/gi) || []
|
|
810
|
+
forPattern.forEach(m => {
|
|
811
|
+
const match = m.match(/^([A-Z][a-z]+\w*)/)
|
|
812
|
+
if (match) candidates.add(match[1])
|
|
813
|
+
})
|
|
814
|
+
|
|
815
|
+
// Known framework patterns: Next.js, Vue.js, Express.js
|
|
816
|
+
const dotJs = text.match(/\b([A-Z][a-z]+)\.js\b/gi) || []
|
|
817
|
+
dotJs.forEach(m => {
|
|
818
|
+
const match = m.match(/([A-Z][a-z]+)/i)
|
|
819
|
+
if (match) candidates.add(match[1])
|
|
820
|
+
})
|
|
821
|
+
|
|
822
|
+
// === Pattern 4: Explicit tech stack sections ===
|
|
823
|
+
// "Tech Stack:", "Technologies:", "Built with:"
|
|
824
|
+
const techSection = text.match(/(?:tech\s*stack|technologies|built\s*with|dependencies)[:\s]+([^\n]+)/gi) || []
|
|
825
|
+
techSection.forEach(section => {
|
|
826
|
+
const items = section.split(/[,\s]+/)
|
|
827
|
+
items.forEach(item => {
|
|
828
|
+
const cleaned = item.replace(/[^a-zA-Z0-9.-]/g, '')
|
|
829
|
+
if (cleaned.length > 2) candidates.add(cleaned)
|
|
830
|
+
})
|
|
831
|
+
})
|
|
832
|
+
|
|
833
|
+
// === Pattern 4.5: Markdown bold/code tech mentions ===
|
|
834
|
+
// "**Mastra**", "`prisma`", "**Framework:** Mastra"
|
|
835
|
+
const boldWords = text.match(/\*\*([A-Z][a-z]+\w*)\*\*/g) || []
|
|
836
|
+
boldWords.forEach(m => {
|
|
837
|
+
const match = m.match(/\*\*([A-Z][a-z]+\w*)\*\*/)
|
|
838
|
+
if (match) candidates.add(match[1])
|
|
839
|
+
})
|
|
840
|
+
|
|
841
|
+
const codeWords = text.match(/`([a-zA-Z][a-zA-Z0-9_-]+)`/g) || []
|
|
842
|
+
codeWords.forEach(m => {
|
|
843
|
+
const match = m.match(/`([a-zA-Z][a-zA-Z0-9_-]+)`/)
|
|
844
|
+
if (match && match[1].length > 2) candidates.add(match[1])
|
|
845
|
+
})
|
|
846
|
+
|
|
847
|
+
// === Filter out noise ===
|
|
848
|
+
const stopWords = new Set([
|
|
849
|
+
// Common English words
|
|
850
|
+
'The', 'This', 'That', 'With', 'From', 'Using', 'For', 'And', 'But', 'Not',
|
|
851
|
+
'All', 'Any', 'Can', 'Could', 'Should', 'Would', 'Will', 'May', 'Might',
|
|
852
|
+
'Each', 'Every', 'Some', 'Many', 'Most', 'Other', 'Such', 'Only', 'Just',
|
|
853
|
+
'Also', 'Well', 'Back', 'Even', 'Still', 'Already', 'Always', 'Never',
|
|
854
|
+
// Common programming terms that aren't libraries
|
|
855
|
+
'API', 'REST', 'HTTP', 'HTTPS', 'JSON', 'XML', 'HTML', 'CSS', 'SQL',
|
|
856
|
+
'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'URL', 'URI', 'UUID', 'ID',
|
|
857
|
+
'True', 'False', 'None', 'Null', 'Undefined', 'Error', 'Exception',
|
|
858
|
+
'Class', 'Function', 'Method', 'Object', 'Array', 'String', 'Number',
|
|
859
|
+
'Boolean', 'Int', 'Float', 'Double', 'Char', 'Byte', 'Long', 'Short',
|
|
860
|
+
'Public', 'Private', 'Protected', 'Static', 'Final', 'Const', 'Let', 'Var',
|
|
861
|
+
'Import', 'Export', 'Module', 'Package', 'Interface', 'Type', 'Enum',
|
|
862
|
+
'Test', 'Tests', 'Spec', 'Specs', 'Mock', 'Stub', 'Fake', 'Spy',
|
|
863
|
+
'Config', 'Configuration', 'Settings', 'Options', 'Params', 'Args',
|
|
864
|
+
'User', 'Users', 'Admin', 'Auth', 'Login', 'Logout', 'Session', 'Token',
|
|
865
|
+
'Data', 'Database', 'Table', 'Column', 'Row', 'Index', 'Key', 'Value',
|
|
866
|
+
'File', 'Files', 'Path', 'Dir', 'Directory', 'Folder', 'Name', 'Size',
|
|
867
|
+
'Create', 'Read', 'Update', 'Delete', 'List', 'Get', 'Set', 'Add', 'Remove',
|
|
868
|
+
'Start', 'Stop', 'Run', 'Build', 'Deploy', 'Install', 'Setup', 'Init',
|
|
869
|
+
// Version/date patterns
|
|
870
|
+
'Version', 'Release', 'Beta', 'Alpha', 'Stable', 'Latest', 'Current'
|
|
871
|
+
])
|
|
872
|
+
|
|
873
|
+
return [...candidates]
|
|
874
|
+
.filter(w => w.length > 2 && w.length < 30)
|
|
875
|
+
.filter(w => !stopWords.has(w))
|
|
876
|
+
.filter(w => !/^\d+$/.test(w)) // Not pure numbers
|
|
877
|
+
.filter(w => !/^v?\d+\.\d+/.test(w)) // Not version numbers
|
|
878
|
+
.slice(0, 50) // Limit to avoid too many API calls
|
|
879
|
+
}
|
|
880
|
+
```
|
|
881
|
+
|
|
882
|
+
---
|
|
883
|
+
|
|
884
|
+
### Helper: parseContext7Response()
|
|
885
|
+
|
|
886
|
+
> **WHY:** Context7 returns multiple matches. Select the best one based on relevance score and snippet count.
|
|
887
|
+
|
|
888
|
+
```typescript
|
|
889
|
+
function parseContext7Response(response: string, searchTerm: string): {
|
|
890
|
+
id: string
|
|
891
|
+
title: string
|
|
892
|
+
snippets: number
|
|
893
|
+
score: number
|
|
894
|
+
} | null {
|
|
895
|
+
// Parse the Context7 response text to extract library info
|
|
896
|
+
// Response format includes lines like:
|
|
897
|
+
// - Title: SQLAlchemy
|
|
898
|
+
// - Context7-compatible library ID: /sqlalchemy/sqlalchemy
|
|
899
|
+
// - Code Snippets: 2830
|
|
900
|
+
// - Benchmark Score: 84.4
|
|
901
|
+
|
|
902
|
+
const libraries = []
|
|
903
|
+
const blocks = response.split('----------').filter(b => b.trim())
|
|
904
|
+
|
|
905
|
+
for (const block of blocks) {
|
|
906
|
+
const titleMatch = block.match(/Title:\s*(.+)/i)
|
|
907
|
+
const idMatch = block.match(/Context7-compatible library ID:\s*(\S+)/i)
|
|
908
|
+
const snippetsMatch = block.match(/Code Snippets:\s*(\d+)/i)
|
|
909
|
+
const scoreMatch = block.match(/Benchmark Score:\s*([\d.]+)/i)
|
|
910
|
+
|
|
911
|
+
if (titleMatch && idMatch) {
|
|
912
|
+
libraries.push({
|
|
913
|
+
title: titleMatch[1].trim(),
|
|
914
|
+
id: idMatch[1].trim(),
|
|
915
|
+
snippets: snippetsMatch ? parseInt(snippetsMatch[1]) : 0,
|
|
916
|
+
score: scoreMatch ? parseFloat(scoreMatch[1]) : 50
|
|
917
|
+
})
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
if (libraries.length === 0) return null
|
|
922
|
+
|
|
923
|
+
// Prefer exact title match, then highest score with good snippet count
|
|
924
|
+
const searchLower = searchTerm.toLowerCase()
|
|
925
|
+
|
|
926
|
+
// First: exact match
|
|
927
|
+
const exactMatch = libraries.find(l =>
|
|
928
|
+
l.title.toLowerCase() === searchLower ||
|
|
929
|
+
l.id.toLowerCase().includes(searchLower)
|
|
930
|
+
)
|
|
931
|
+
if (exactMatch) return exactMatch
|
|
932
|
+
|
|
933
|
+
// Second: partial match with good score
|
|
934
|
+
const partialMatches = libraries.filter(l =>
|
|
935
|
+
l.title.toLowerCase().includes(searchLower) ||
|
|
936
|
+
searchLower.includes(l.title.toLowerCase())
|
|
937
|
+
)
|
|
938
|
+
if (partialMatches.length > 0) {
|
|
939
|
+
return partialMatches.sort((a, b) => b.score - a.score)[0]
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// Third: best overall score (only if snippets > 100 for quality)
|
|
943
|
+
const qualityLibs = libraries.filter(l => l.snippets > 100)
|
|
944
|
+
if (qualityLibs.length > 0) {
|
|
945
|
+
return qualityLibs.sort((a, b) => b.score - a.score)[0]
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// Fallback: first result
|
|
949
|
+
return libraries[0]
|
|
646
950
|
}
|
|
647
951
|
```
|
|
648
952
|
|
|
@@ -884,28 +1188,43 @@ const capabilityAnalysis = {
|
|
|
884
1188
|
```
|
|
885
1189
|
|
|
886
1190
|
**Helper: generateBestPracticesFile()**
|
|
1191
|
+
|
|
1192
|
+
> **Updated v2.3.0:** Now includes Context7 library ID for reference and refresh capability.
|
|
1193
|
+
|
|
887
1194
|
```typescript
|
|
888
|
-
function generateBestPracticesFile(
|
|
1195
|
+
function generateBestPracticesFile(
|
|
1196
|
+
tech: string,
|
|
1197
|
+
context7Docs: string,
|
|
1198
|
+
context7Id: string
|
|
1199
|
+
): string {
|
|
889
1200
|
return `# ${tech} Best Practices
|
|
890
1201
|
|
|
891
1202
|
> **Source:** Context7 MCP
|
|
1203
|
+
> **Library ID:** \`${context7Id}\`
|
|
892
1204
|
> **Generated:** ${new Date().toISOString().split('T')[0]}
|
|
1205
|
+
> **Refresh:** Query Context7 with the Library ID above to update this file
|
|
893
1206
|
|
|
894
1207
|
---
|
|
895
1208
|
|
|
896
1209
|
## Best Practices
|
|
897
1210
|
|
|
898
|
-
${
|
|
1211
|
+
${extractBestPractices(context7Docs)}
|
|
899
1212
|
|
|
900
1213
|
---
|
|
901
1214
|
|
|
902
1215
|
## Anti-Patterns to Avoid
|
|
903
1216
|
|
|
904
|
-
${
|
|
1217
|
+
${extractAntiPatterns(context7Docs)}
|
|
905
1218
|
|
|
906
1219
|
---
|
|
907
1220
|
|
|
908
|
-
##
|
|
1221
|
+
## Code Examples
|
|
1222
|
+
|
|
1223
|
+
${extractCodeExamples(context7Docs)}
|
|
1224
|
+
|
|
1225
|
+
---
|
|
1226
|
+
|
|
1227
|
+
## Quick Checklist
|
|
909
1228
|
|
|
910
1229
|
Before committing ${tech} code:
|
|
911
1230
|
${extractChecklist(context7Docs)}
|
|
@@ -915,6 +1234,81 @@ ${extractChecklist(context7Docs)}
|
|
|
915
1234
|
**Agents read this file in STEP 0 before implementation.**
|
|
916
1235
|
`
|
|
917
1236
|
}
|
|
1237
|
+
|
|
1238
|
+
// Helper: Extract best practices from Context7 docs
|
|
1239
|
+
function extractBestPractices(docs: string): string {
|
|
1240
|
+
// Look for sections about best practices, recommendations, patterns
|
|
1241
|
+
const patterns = [
|
|
1242
|
+
/best\s*practices?[:\s]+([^#]+?)(?=##|$)/gi,
|
|
1243
|
+
/recommend(?:ed|ations)?[:\s]+([^#]+?)(?=##|$)/gi,
|
|
1244
|
+
/(?:do|should)[:\s]+([^#]+?)(?=##|$)/gi
|
|
1245
|
+
]
|
|
1246
|
+
|
|
1247
|
+
let extracted = ''
|
|
1248
|
+
for (const pattern of patterns) {
|
|
1249
|
+
const matches = docs.match(pattern)
|
|
1250
|
+
if (matches) {
|
|
1251
|
+
extracted += matches.join('\n\n')
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
return extracted || docs.slice(0, 2000) // Fallback to first 2000 chars
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// Helper: Extract anti-patterns from Context7 docs
|
|
1259
|
+
function extractAntiPatterns(docs: string): string {
|
|
1260
|
+
const patterns = [
|
|
1261
|
+
/anti-?patterns?[:\s]+([^#]+?)(?=##|$)/gi,
|
|
1262
|
+
/avoid[:\s]+([^#]+?)(?=##|$)/gi,
|
|
1263
|
+
/(?:don'?t|should\s*not)[:\s]+([^#]+?)(?=##|$)/gi,
|
|
1264
|
+
/common\s*mistakes?[:\s]+([^#]+?)(?=##|$)/gi
|
|
1265
|
+
]
|
|
1266
|
+
|
|
1267
|
+
let extracted = ''
|
|
1268
|
+
for (const pattern of patterns) {
|
|
1269
|
+
const matches = docs.match(pattern)
|
|
1270
|
+
if (matches) {
|
|
1271
|
+
extracted += matches.join('\n\n')
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
return extracted || 'Review Context7 documentation for anti-patterns specific to your use case.'
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// Helper: Extract code examples from Context7 docs
|
|
1279
|
+
function extractCodeExamples(docs: string): string {
|
|
1280
|
+
// Extract code blocks
|
|
1281
|
+
const codeBlocks = docs.match(/\`\`\`[\s\S]*?\`\`\`/g) || []
|
|
1282
|
+
return codeBlocks.slice(0, 5).join('\n\n') || 'See Context7 documentation for code examples.'
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
// Helper: Generate checklist from docs
|
|
1286
|
+
function extractChecklist(docs: string): string {
|
|
1287
|
+
// Look for checklist items or generate from best practices
|
|
1288
|
+
const checklistPatterns = [
|
|
1289
|
+
/- \[[ x]\][^\n]+/gi,
|
|
1290
|
+
/\d+\.\s+[^\n]+/gi
|
|
1291
|
+
]
|
|
1292
|
+
|
|
1293
|
+
let items = []
|
|
1294
|
+
for (const pattern of checklistPatterns) {
|
|
1295
|
+
const matches = docs.match(pattern)
|
|
1296
|
+
if (matches) {
|
|
1297
|
+
items = items.concat(matches.slice(0, 10))
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
if (items.length > 0) {
|
|
1302
|
+
return items.map(item => `- [ ] ${item.replace(/^[\d.-\[\]x\s]+/i, '')}`).join('\n')
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// Fallback: generic checklist
|
|
1306
|
+
return `- [ ] Follow official documentation patterns
|
|
1307
|
+
- [ ] Handle errors appropriately
|
|
1308
|
+
- [ ] Add proper typing/validation
|
|
1309
|
+
- [ ] Write tests for new code
|
|
1310
|
+
- [ ] Review for security concerns`
|
|
1311
|
+
}
|
|
918
1312
|
```
|
|
919
1313
|
|
|
920
1314
|
---
|
|
@@ -1574,20 +1968,18 @@ function getAgentForPhase(phaseId: string): string {
|
|
|
1574
1968
|
}
|
|
1575
1969
|
```
|
|
1576
1970
|
|
|
1577
|
-
### detectAdditionalTech()
|
|
1971
|
+
### detectAdditionalTech() - DEPRECATED
|
|
1972
|
+
|
|
1973
|
+
> **Note:** This function is deprecated in v2.3.0. Use `extractPotentialLibraryNames()` + Context7 resolution instead.
|
|
1974
|
+
> The dynamic approach automatically detects any library without hardcoded patterns.
|
|
1975
|
+
|
|
1578
1976
|
```typescript
|
|
1579
|
-
//
|
|
1977
|
+
// DEPRECATED: Kept for backwards compatibility only
|
|
1978
|
+
// Use extractPotentialLibraryNames() for new implementations
|
|
1580
1979
|
function detectAdditionalTech(proposal: string, tasks: string): string[] {
|
|
1980
|
+
// Now delegates to the dynamic detection system
|
|
1581
1981
|
const combined = proposal + ' ' + tasks
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
if (combined.includes('stripe') || combined.includes('payment')) tech.push('Stripe')
|
|
1585
|
-
if (combined.includes('websocket') || combined.includes('realtime')) tech.push('WebSocket')
|
|
1586
|
-
if (combined.includes('redis')) tech.push('Redis')
|
|
1587
|
-
if (combined.includes('s3') || combined.includes('storage')) tech.push('S3/Storage')
|
|
1588
|
-
// Add more as needed
|
|
1589
|
-
|
|
1590
|
-
return tech
|
|
1982
|
+
return extractPotentialLibraryNames(combined)
|
|
1591
1983
|
}
|
|
1592
1984
|
```
|
|
1593
1985
|
|
package/README.md
CHANGED
|
@@ -29,18 +29,33 @@ cak init
|
|
|
29
29
|
|
|
30
30
|
## Features
|
|
31
31
|
|
|
32
|
-
- **
|
|
32
|
+
- **Zero-Maintenance Tech Detection (v2.3.0)** - Auto-detects ANY library in ANY language via Context7
|
|
33
|
+
- **4-Layer Validation** - Feature BP ā Spec Alignment ā Library Capability ā Stack BP
|
|
33
34
|
- **Spec Drift Prevention** - Validates library supports spec before implementation
|
|
34
|
-
- **Instruction-based Library Detection** - Agents scan `tasks.md`/`design.md` for required libraries
|
|
35
35
|
- **Cross-session Context** - `PROJECT_STATUS.yml` maintains state across sessions
|
|
36
36
|
- **Design System v2.0** - Interactive setup with theme selection
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Tech Detection (v2.3.0)
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
/csetup automatically detects libraries from:
|
|
42
|
+
āāā Spec files (proposal.md, design.md, tasks.md)
|
|
43
|
+
āāā JS/TS (package.json, imports)
|
|
44
|
+
āāā Python (requirements.txt, pyproject.toml)
|
|
45
|
+
āāā Rust (Cargo.toml)
|
|
46
|
+
āāā Go (go.mod)
|
|
47
|
+
āāā PHP/Ruby (composer.json, Gemfile)
|
|
48
|
+
|
|
49
|
+
Context7 validates ā generates best-practices/*.md
|
|
50
|
+
Zero maintenance - works with any library!
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Validation Flow
|
|
39
54
|
|
|
40
55
|
```
|
|
41
56
|
/csetup
|
|
42
|
-
āāā Step 2.6: Feature Best Practice (Auth, Payment, etc.
|
|
43
|
-
āāā Step 2.7: Stack Best Practice (
|
|
57
|
+
āāā Step 2.6: Feature Best Practice (Auth, Payment, etc.)
|
|
58
|
+
āāā Step 2.7: Stack Best Practice (auto-detected libraries)
|
|
44
59
|
āāā Step 2.8: Library Capability (verify library supports spec)
|
|
45
60
|
|
|
46
61
|
/cdev
|