@champpaba/claude-agent-kit 2.2.0 → 2.3.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/.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.0.0 - Claude 4.5 Optimized + Design System v2.0
5
- > **Latest:** Full template refactored with Claude 4.5 best practices (agents ~65% smaller) + Interactive design setup with theme selection
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 (v1.8.0)
134
+ ## šŸ“š Best Practices System (v2.3.0 - Zero Maintenance)
135
135
 
136
136
  **Quick Summary:**
137
- - `/csetup` **auto-detects tech stack** from: package.json → design.md → proposal/tasks (3 sources)
138
- - **Auto-generates best practices** from Context7 MCP (React, Next.js, Prisma, etc.)
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
- - `/cdev` **injects** relevant best-practices paths into agent prompts
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 → detect stack → query Context7 → generate best-practices
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 (v1.8.0)
479
+ ### Step 2.7: Auto-Setup Best Practices (v2.3.0 - Dynamic Detection)
480
480
 
481
- > **NEW:** Auto-detect tech stack and generate best-practices (replaces /psetup and /agentsetup)
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
- // 1. Detect tech stack from multiple sources
485
- output(`\nšŸ” Detecting Tech Stack...`)
486
-
487
- // Source 1: package.json / requirements.txt (if exists)
488
- let packageStack = []
489
- if (fileExists('package.json')) {
490
- const pkg = JSON.parse(Read('package.json'))
491
- const deps = { ...pkg.dependencies, ...pkg.devDependencies }
492
- packageStack = Object.keys(deps).filter(d =>
493
- ['next', 'react', 'vue', 'express', 'fastapi', 'prisma', 'drizzle', 'vitest', 'jest'].some(k => d.includes(k))
494
- )
495
- output(` šŸ“¦ From package.json: ${packageStack.join(', ') || 'none'}`)
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
- // Source 2: design.md (architecture section)
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
- // Source 3: proposal.md + tasks.md (keywords)
512
- const proposalContent = Read(`openspec/changes/${changeId}/proposal.md`)
513
- const tasksContent = Read(`openspec/changes/${changeId}/tasks.md`)
514
- const combined = (proposalContent + ' ' + tasksContent).toLowerCase()
515
-
516
- const techDetection = {
517
- react: /\b(react|jsx|tsx|use[A-Z]\w+|usestate|useeffect)\b/i,
518
- nextjs: /\b(next\.?js|next js|app router|pages router)\b/i,
519
- vue: /\b(vue|vuex|pinia|nuxt)\b/i,
520
- express: /\b(express\.js|express js|expressjs)\b/i,
521
- fastapi: /\b(fastapi|fast api)\b/i,
522
- django: /\b(django)\b/i,
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 proposalStack = []
535
- for (const [tech, pattern] of Object.entries(techDetection)) {
536
- if (pattern.test(combined)) {
537
- proposalStack.push(tech)
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
- // Merge all sources (remove duplicates)
543
- const detectedStack = [...new Set([...packageStack, ...designStack, ...proposalStack])]
547
+ output(`\nšŸ“Š Verified Libraries: ${resolvedLibraries.length}`)
548
+ resolvedLibraries.forEach(lib => {
549
+ output(` - ${lib.title} (${lib.context7Id})`)
550
+ })
544
551
 
545
- // 2. If no stack detected, ask user
546
- if (detectedStack.length === 0) {
547
- output(`\nāš ļø Could not auto-detect tech stack`)
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: 'What tech stack will you use?',
552
- header: 'Stack',
558
+ question: 'Enter the main libraries/frameworks for this project (comma-separated):',
559
+ header: 'Libraries',
553
560
  options: [
554
- { label: 'Next.js + React', description: 'Full-stack React framework' },
555
- { label: 'FastAPI + Python', description: 'Python async API' },
556
- { label: 'Express + Node', description: 'Node.js backend' },
557
- { label: 'Vue + Nuxt', description: 'Vue.js framework' }
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: true
566
+ multiSelect: false
560
567
  }]
561
568
  })
562
569
 
563
- // Parse user selection into detectedStack
564
- detectedStack.push(...parseUserStackSelection(answer))
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
- output(`\nāœ… Final Tech Stack: ${detectedStack.join(', ')}`)
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
- const missingBp = detectedStack.filter(tech => {
574
- return !existingBp.some(f => f.toLowerCase().includes(tech.toLowerCase()))
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
- // 4. Generate missing best-practices from Context7
578
- if (missingBp.length > 0) {
599
+ if (newLibraries.length > 0) {
579
600
  output(`\nšŸ“š Generating Best Practices from Context7...`)
580
601
 
581
- // Create directory structure if needed
582
- if (!fileExists('.claude/contexts/domain/')) {
583
- mkdir('.claude/contexts/domain/project/best-practices/')
602
+ // Create directory if needed
603
+ if (!fileExists(bpDir)) {
604
+ mkdir(bpDir)
584
605
  }
585
606
 
586
- // Context7 library ID mapping
587
- const context7Ids = {
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
- for (const tech of missingBp) {
602
- const libraryId = context7Ids[tech.toLowerCase()]
603
-
604
- if (libraryId) {
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
- // Generate best-practices file
615
- const bpContent = generateBestPracticesFile(tech, docs)
616
- Write(`.claude/contexts/domain/project/best-practices/${tech}.md`, bpContent)
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(` āœ… ${tech}.md generated`)
619
- } else {
620
- output(` āš ļø ${tech} - no Context7 mapping, using universal patterns`)
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(detectedStack, changeId)
626
- output(` āœ… index.md generated`)
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(` Files: ${missingBp.length + 1} generated`)
636
- output(` Location: .claude/contexts/domain/project/best-practices/`)
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: Already configured (${existingBp.length} files)`)
637
+ output(`\nāœ… Best Practices: Skipped (no libraries to configure)`)
639
638
  }
640
639
 
641
- // 5. Store detected stack in context.md (for agents to reference)
640
+ // 6. Store resolved stack for context.md generation
642
641
  const stackForContext = {
643
- detected: detectedStack,
644
- bestPracticesPath: '.claude/contexts/domain/project/best-practices/',
645
- files: [...existingBp, ...missingBp.map(t => `${t}.md`)]
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(tech: string, context7Docs: string): string {
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
- ${extractDos(context7Docs)}
1211
+ ${extractBestPractices(context7Docs)}
899
1212
 
900
1213
  ---
901
1214
 
902
1215
  ## Anti-Patterns to Avoid
903
1216
 
904
- ${extractDonts(context7Docs)}
1217
+ ${extractAntiPatterns(context7Docs)}
905
1218
 
906
1219
  ---
907
1220
 
908
- ## šŸŽÆ Quick Checklist
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
- // Detect change-specific tech (Stripe, WebSocket, etc.)
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
- const tech = []
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
- - **4-Layer Validation (v2.2.0)** - Feature BP → Spec Alignment → Library Capability → Stack BP
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
- ## Validation Flow (v2.2.0)
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. vs industry standard)
43
- ā”œā”€ā”€ Step 2.7: Stack Best Practice (React, Next.js, etc.)
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@champpaba/claude-agent-kit",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Universal multi-agent template for Claude Code - AI-assisted development with specialized agents",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {