UNPKG

@vibe-dev-kit/cli

Version:

Advanced Command-line toolkit that analyzes your codebase and deploys project-aware rules, memories, commands and agents to any AI coding assistant - VDK is the world's first Vibe Development Kit

1,536 lines (1,329 loc) 102 kB
import os from 'node:os' import path from 'node:path' import chalk from 'chalk' import fs from 'fs-extra' import { validateCommand } from '../../utils/schema-validator.js' import { RuleAdapter } from './RuleAdapter.js' /** * Enhanced Claude Code Adapter * * Leverages Claude Code's advanced features including: * - Hierarchical memory management with import system * - Sophisticated slash command generation * - MCP server integration patterns * - Team memory synchronization * - Native Claude Code file structure */ export class ClaudeCodeAdapter extends RuleAdapter { constructor(options = {}) { super(options) this.claudeConfigPath = path.join(this.projectPath, '.claude') this.claudeUserPath = path.join(os.homedir(), '.claude') this.ruleGenerator = options.ruleGenerator // Reference to parent RuleGenerator for centralized fetching } /** * Enhanced Claude Code adaptation with memory hierarchy */ async adaptForClaude(rules, projectContext, categoryFilter = null) { console.log('🧠 Generating enhanced Claude Code memory hierarchy...') const adapted = { files: [], directories: [this.claudeConfigPath], memoryHierarchy: await this.generateMemoryHierarchy(rules, projectContext, categoryFilter), slashCommands: await this.generateSlashCommands(rules, projectContext, categoryFilter), mcpIntegrations: await this.generateMcpIntegrations(rules, projectContext), settings: await this.generateClaudeSettings(rules, projectContext), } // Ensure .claude directory exists await fs.ensureDir(this.claudeConfigPath) await fs.ensureDir(path.join(this.claudeConfigPath, 'commands')) // Generate memory files for (const memory of adapted.memoryHierarchy) { await fs.writeFile(memory.path, memory.content) adapted.files.push(memory.path) } // Generate slash commands for (const command of adapted.slashCommands) { const commandPath = path.join(this.claudeConfigPath, 'commands', `${command.name}.md`) await fs.writeFile(commandPath, command.content) adapted.files.push(commandPath) } // Generate settings if (adapted.settings) { const settingsPath = path.join(this.claudeConfigPath, 'settings.json') await fs.writeFile(settingsPath, JSON.stringify(adapted.settings, null, 2)) adapted.files.push(settingsPath) } console.log(`✅ Generated ${adapted.files.length} Claude Code files`) return adapted } /** * Generate hierarchical memory structure using CORRECT Claude Code format */ async generateMemoryHierarchy(rules, projectContext, categoryFilter = null) { const memories = [] const _projectName = projectContext.name || path.basename(this.projectPath) // Fetch technology-specific rules from remote repository let technologyRules = [] if (this.ruleGenerator?.fetchFromRepository) { try { console.log('🔍 Fetching technology-specific rules for Claude Code...') // Use the full projectContext as analysisData since it contains technologyData technologyRules = await this.ruleGenerator.fetchFromRepository( projectContext, 'rules', null, // No specific platform filter for rules categoryFilter ) console.log(`📚 Fetched ${technologyRules.length} technology-specific rules`) if (technologyRules.length > 0) { console.log(`📋 Rule names: ${technologyRules.map((r) => r.name).join(', ')}`) } } catch (error) { console.warn(`⚠️ Failed to fetch technology rules: ${error.message}`) } } // Main project memory with CORRECT Claude Code structure and technology rules const mainMemory = await this.generateCorrectClaudeMainMemory( rules, projectContext, technologyRules ) memories.push({ path: path.join(this.projectPath, 'CLAUDE.md'), content: mainMemory, type: 'project', priority: 'high', }) return memories } /** * Generate CORRECT Claude Code memory structure as per report findings */ async generateCorrectClaudeMainMemory(_rules, projectContext, technologyRules = []) { const projectName = projectContext.name || path.basename(this.projectPath) // Handle both techStack and technologyData structure const techData = projectContext.techStack || projectContext.technologyData || {} const frameworks = techData.frameworks || [] const languages = techData.primaryLanguages || [] const libraries = techData.libraries || [] const packageManager = await this.detectPackageManager(projectContext) const buildTool = await this.detectBuildTool(projectContext) const testFramework = techData.testFramework || 'jest' // Extract technology-specific guidelines from remote rules const technologyGuidelines = await this.extractTechnologyGuidelines( technologyRules, projectContext ) return `# ${projectName} - Claude Code Memory ## Project Overview This is a **${this.determineProjectType(frameworks, languages)}** project. ### Key Information - **Project Type**: ${this.determineProjectType(frameworks, languages)} - **Primary Language**: ${languages.join(', ') || 'Not detected'} - **Frameworks**: ${frameworks.join(', ') || 'Not detected'} - **Libraries**: ${libraries.slice(0, 3).join(', ') || 'Standard libraries'} ## Coding Preferences ### Code Style - Use ${this.detectIndentation(projectContext)} indentation - Prefer ${languages.includes('TypeScript') ? 'TypeScript strict mode' : 'modern JavaScript'} - ${this.generateStyleGuidelines(projectContext)} ### Project Structure - Follow ${this.detectProjectStructure(projectContext)} structure - ${this.generateStructureGuidelines(projectContext)} ### Testing - Use ${testFramework} for testing - ${this.generateTestingGuidelines(projectContext)} ## Development Environment ### Tools & Setup - Package manager: ${packageManager} - Build tool: ${buildTool} - Primary IDE: ${await this.detectPrimaryIDE(projectContext)} - AI Assistant: Claude Code ### Development Commands - \`${await this.detectDevCommand(projectContext)}\` - Start development server - \`${await this.detectTestCommand(projectContext)}\` - Run tests - \`${await this.detectBuildCommand(projectContext)}\` - Build for production ## Technology-Specific Guidelines ${technologyGuidelines} ## Workflow Preferences ### Development Workflow - Start with failing tests when appropriate - Run tests before committing - Use feature flags for incomplete features - Plan for rollback strategies ### Communication - Over-communicate in remote environments - Document decisions and reasoning - Share knowledge through code comments and docs - Ask for clarification when requirements are unclear --- *Generated by VDK CLI - Enhanced for Claude Code* *Technology rules: ${technologyRules.length} rules integrated*` } /** * Determine project type based on frameworks and languages */ determineProjectType(frameworks, languages) { // Prioritize Astro detection first since it's more specific if (frameworks.includes('Astro') && frameworks.includes('Starlight')) { return 'Astro Starlight Documentation Site' } if (frameworks.includes('Astro')) { return 'Astro Application' } if (frameworks.includes('Next.js') && frameworks.includes('Supabase')) { return 'Next.js + Supabase Full-Stack Application' } if (frameworks.includes('Next.js')) { return 'Next.js Application' } if (frameworks.includes('React')) { return 'React Application' } if (frameworks.includes('Vue.js')) { return 'Vue.js Application' } if (languages.includes('typescript')) { return 'TypeScript Application' } if (languages.includes('javascript')) { return 'JavaScript Application' } return 'Web Application' } /** * Extract technology-specific guidelines from remote rules */ async extractTechnologyGuidelines(technologyRules, projectContext) { if (!technologyRules || technologyRules.length === 0) { return `### General Guidelines - Follow established patterns in the codebase - Maintain consistency with existing code - Use project-specific conventions` } console.log( `🔍 Processing ${technologyRules.length} technology rules for guidelines extraction` ) if (this.verbose) { technologyRules.forEach((rule) => { console.log(` 📄 Rule: ${rule.name}, Content length: ${rule.content?.length || 0}`) }) } const guidelines = [] // Handle both techStack and technologyData structure const techData = projectContext.techStack || projectContext.technologyData || {} const frameworks = techData.frameworks || [] const languages = techData.primaryLanguages || [] // Group rules by category for better organization const _rulesByCategory = this.groupRulesByCategory(technologyRules) // Extract framework-specific guidelines for (const framework of frameworks) { // Enhanced framework matching with aliases const frameworkLower = framework.toLowerCase() const frameworkAliases = { 'tailwind css': ['tailwind', 'tailwindcss'], 'next.js': ['nextjs'], supabase: ['supabase'], react: ['react'], typescript: ['typescript', 'ts'], 'shadcn/ui': ['shadcn', 'shadcnui'], } const searchTerms = [frameworkLower] if (frameworkAliases[frameworkLower]) { searchTerms.push(...frameworkAliases[frameworkLower]) } const frameworkRules = technologyRules.filter((rule) => searchTerms.some( (term) => rule.name.toLowerCase().includes(term) || rule.path?.toLowerCase().includes(term) ) ) if (frameworkRules.length > 0) { guidelines.push(`### ${framework} Guidelines`) for (const rule of frameworkRules.slice(0, 3)) { // Limit to 3 most relevant const extractedContent = this.extractRuleContent(rule.content, projectContext) if (extractedContent) { guidelines.push(extractedContent) } } guidelines.push('') // Add spacing } } // Extract language-specific guidelines for (const language of languages) { const languageRules = technologyRules.filter( (rule) => rule.name.toLowerCase().includes(language.toLowerCase()) || rule.path?.toLowerCase().includes(language.toLowerCase()) ) if (languageRules.length > 0) { guidelines.push(`### ${language} Guidelines`) for (const rule of languageRules.slice(0, 2)) { // Limit to 2 most relevant const extractedContent = this.extractRuleContent(rule.content, projectContext) if (extractedContent) { guidelines.push(extractedContent) } } guidelines.push('') // Add spacing } } // Extract library-specific guidelines (important for shadcn/ui, etc.) const libraries = techData.libraries || [] for (const library of libraries) { const libraryLower = library.toLowerCase() const libraryAliases = { 'shadcn/ui': ['shadcn', 'shadcnui'], 'tailwind css': ['tailwind', 'tailwindcss'], 'radix ui': ['radix'], 'framer motion': ['framer'], } const searchTerms = [libraryLower] if (libraryAliases[libraryLower]) { searchTerms.push(...libraryAliases[libraryLower]) } const libraryRules = technologyRules.filter((rule) => searchTerms.some( (term) => rule.name.toLowerCase().includes(term) || rule.path?.toLowerCase().includes(term) ) ) if (libraryRules.length > 0) { guidelines.push(`### ${library} Guidelines`) for (const rule of libraryRules.slice(0, 2)) { // Limit to 2 most relevant const extractedContent = this.extractRuleContent(rule.content, projectContext) if (extractedContent) { guidelines.push(extractedContent) } } guidelines.push('') // Add spacing } } // Add core/general rules const coreRules = technologyRules.filter( (rule) => rule.path?.includes('core/') || rule.name.includes('core') || rule.name.includes('general') ) if (coreRules.length > 0) { guidelines.push('### Core Development Practices') for (const rule of coreRules.slice(0, 2)) { const extractedContent = this.extractRuleContent(rule.content) if (extractedContent) { guidelines.push(extractedContent) } } } return guidelines.length > 0 ? guidelines.join('\n') : `### Project-Specific Guidelines - Follow patterns established in this ${frameworks.join('/')} codebase - Maintain consistency with existing ${languages.join('/')} code - Reference remote rules: ${technologyRules.length} rules available` } /** * Extract relevant content from a rule's markdown content */ extractRuleContent(content, projectContext = null) { if (!content) { return null } try { // Remove frontmatter (YAML between ---) const withoutFrontmatter = this.stripFrontmatter(content) // Split into lines and process const lines = withoutFrontmatter.split('\n') const relevantLines = [] let _currentSection = null let captureContent = false for (let i = 0; i < lines.length; i++) { const line = lines[i] const trimmed = line.trim() if (!trimmed) { continue } // Check for section headers that indicate useful content if ( trimmed.match( /^#+\s*(core principles|best practices|guidelines|rules|conventions|patterns|key concepts|important|essential|recommendations|development practices|coding standards)/i ) ) { _currentSection = trimmed.replace(/^#+\s*/, '') captureContent = true continue } // Check for technology-specific sections if ( trimmed.match( /^#+\s*(typescript|nextjs|supabase|react|development|code)\s+(guidelines|best practices|rules|patterns|conventions)/i ) ) { _currentSection = trimmed.replace(/^#+\s*/, '') captureContent = true continue } // Stop capturing if we hit unrelated sections or tech stacks (which are just lists) if ( trimmed.match( /^#+\s+(meta|compatibility|examples|implementation|setup|overview|tech stack|technology stack|recommended|stack)/i ) ) { captureContent = false continue } // Capture content if we're in a relevant section if (captureContent) { // CRITICAL: Filter out mobile/native patterns for web projects const isMobilePattern = this.isMobilePattern(trimmed) const isWebProject = this.isWebProject(projectContext) if (isWebProject && isMobilePattern) { continue // Skip mobile patterns for web projects } // Skip technology lists that start with **Technology** (like **Next.js 15+**) if (trimmed.match(/^-\s*\*\*[A-Z][^*]+\*\*\s*(with|for|v?\d)/i)) { continue // Skip technology stack items } // Capture bullet points that are actual guidelines (contain action words) if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) { if (this.isActionableGuideline(trimmed)) { relevantLines.push(trimmed) } } // Capture numbered lists that are guidelines else if (trimmed.match(/^\d+\.\s/)) { const content = trimmed.replace(/^\d+\.\s/, '') if (this.isActionableGuideline(content)) { relevantLines.push(`- ${content}`) } } // Capture important standalone statements else if ( trimmed.length > 25 && !trimmed.startsWith('#') && (trimmed.includes('should') || trimmed.includes('must') || trimmed.includes('prefer') || trimmed.includes('avoid') || trimmed.includes('use') || trimmed.includes('always') || trimmed.includes('never') || trimmed.includes('implement')) ) { relevantLines.push(`- ${trimmed}`) } } // Limit content extraction if (relevantLines.length >= 10) { break } } // If no structured content found, extract first few bullet points from anywhere if (relevantLines.length === 0) { const bullets = lines .filter((line) => { const trimmed = line.trim() return (trimmed.startsWith('- ') || trimmed.startsWith('* ')) && trimmed.length > 15 }) .slice(0, 5) return bullets.length > 0 ? bullets.join('\n') : null } return relevantLines.slice(0, 8).join('\n') // Limit to 8 most relevant points } catch (error) { console.warn(`Failed to extract content from rule: ${error.message}`) return null } } /** * Strip YAML frontmatter from content (reusing RuleAdapter pattern) */ stripFrontmatter(content) { if (content.startsWith('---')) { const parts = content.split('---') return parts.slice(2).join('---').trim() } return content } /** * Check if a line contains mobile/native patterns that shouldn't be in web projects */ isMobilePattern(line) { const mobilePatternsLower = line.toLowerCase() return ( mobilePatternsLower.includes('expo-font') || mobilePatternsLower.includes('react-native') || (mobilePatternsLower.includes('usecolorscheme') && mobilePatternsLower.includes('react-native')) || mobilePatternsLower.includes('expo router') || mobilePatternsLower.includes('@expo/') || (mobilePatternsLower.includes('error boundaries') && mobilePatternsLower.includes('navigation tree')) || (mobilePatternsLower.includes('ios') && mobilePatternsLower.includes('android')) || (mobilePatternsLower.includes('mobile') && (mobilePatternsLower.includes('app') || mobilePatternsLower.includes('device'))) || (mobilePatternsLower.includes('native') && !mobilePatternsLower.includes('native web')) || mobilePatternsLower.includes('capacitor') || mobilePatternsLower.includes('cordova') || (mobilePatternsLower.includes('ionic') && !mobilePatternsLower.includes('ionic')) // Keep general ionic references ) } /** * Check if the project is a web project (Next.js, React web, etc.) */ isWebProject(projectContext) { const techData = projectContext.techStack || projectContext.technologyData || {} const frameworks = techData.frameworks || [] return frameworks.some((framework) => { const fw = framework.toLowerCase() return ( fw.includes('next.js') || fw.includes('nextjs') || (fw.includes('react') && !fw.includes('react native')) || fw.includes('vue.js') || fw.includes('angular') || fw.includes('nuxt') || fw.includes('remix') || fw.includes('gatsby') || (fw.includes('supabase') && !fw.includes('mobile')) ) }) } /** * Check if a line contains actionable guidelines rather than just technology lists */ isActionableGuideline(line) { const lineLower = line.toLowerCase() // Skip technology stack items that just list versions/tools const techStackPatterns = [ /\*\*[^*]+\*\*\s*(with|for|v?\d+)/i, // **Next.js 15+** with App Router /\*\*[^*]+\*\*\s*for\s+/i, // **Supabase** for backend services /\*\*[^*]+\d+\.\d+\*\*/i, // **TypeScript 5.4+** /^-?\s*\*\*[A-Z]/, // **React 19+** ] if (techStackPatterns.some((pattern) => pattern.test(line))) { return false } // Look for actionable language that indicates actual guidelines const actionableWords = [ 'use', 'avoid', 'prefer', 'should', 'must', 'always', 'never', 'implement', 'ensure', 'configure', 'setup', 'create', 'define', 'follow', 'apply', 'enable', 'disable', 'keep', 'make', 'write', 'structure', 'organize', 'split', 'extract', 'handle', 'manage', 'validate', 'sanitize', 'optimize', 'test', 'document', 'review', ] return actionableWords.some((word) => lineLower.includes(word)) && line.length > 20 } /** * Group rules by category for better organization */ groupRulesByCategory(rules) { const categories = { frameworks: [], languages: [], stacks: [], technologies: [], core: [], other: [], } for (const rule of rules) { const path = rule.path?.toLowerCase() || '' const name = rule.name?.toLowerCase() || '' if (path.includes('frameworks/') || name.includes('framework')) { categories.frameworks.push(rule) } else if (path.includes('languages/') || name.includes('language')) { categories.languages.push(rule) } else if (path.includes('stacks/') || name.includes('stack')) { categories.stacks.push(rule) } else if (path.includes('technologies/') || name.includes('tech')) { categories.technologies.push(rule) } else if (path.includes('core/') || name.includes('core')) { categories.core.push(rule) } else { categories.other.push(rule) } } return categories } /** * Generate CORRECT Claude Code settings.json with permissions system */ async generateClaudeSettings(_rules, projectContext) { const packageManager = projectContext.techStack?.packageManager || 'npm' const frameworks = projectContext.techStack?.frameworks || [] const settings = { allowedTools: [ 'Read(src/**)', 'Edit(src/**)', `Bash(${packageManager} run:*)`, 'Bash(git status)', 'Bash(git add:*)', 'Bash(git commit:*)', 'Bash(git diff:*)', 'Bash(git log:*)', 'Glob(**/*.js)', 'Glob(**/*.ts)', 'Glob(**/*.tsx)', 'Grep(*)', ], deniedTools: [ 'Bash(rm -rf:*)', 'Bash(sudo:*)', 'Edit(.env*)', 'Write(node_modules/**/*)', 'Write(.git/**/*)', ], env: { BASH_DEFAULT_TIMEOUT_MS: this.getOptimalTimeout(projectContext), PROJECT_NAME: projectContext.name || path.basename(this.projectPath), }, } // Add framework-specific permissions for (const framework of frameworks) { const frameworkPermissions = this.getFrameworkPermissions(framework) settings.allowedTools.push(...frameworkPermissions) } // Add project-specific environment variables if (projectContext.techStack?.buildTools?.includes('vite')) { settings.env.VITE_DEV_MODE = 'true' } if (projectContext.techStack?.testingFrameworks?.includes('jest')) { settings.allowedTools.push('Bash(npm run test:*)', 'Bash(jest:*)') } return settings } // Helper methods for Claude Code structure detectIndentation(projectContext) { if (projectContext.techStack?.languages?.includes('Python')) { return '4-space' } if (projectContext.techStack?.languages?.includes('Go')) { return '4-space' } return '2-space' } generateStyleGuidelines(projectContext) { const guidelines = [] if (projectContext.techStack?.frameworks?.includes('React')) { guidelines.push('Use functional components with hooks') } if (projectContext.techStack?.languages?.includes('TypeScript')) { guidelines.push('Always use TypeScript strict mode') } return guidelines.join('\n- ') || 'Follow project conventions' } detectProjectStructure(projectContext) { if (projectContext.techStack?.frameworks?.includes('Next.js')) { return 'Next.js App Router' } if (projectContext.techStack?.frameworks?.includes('React')) { return 'React component-based' } return 'standard module' } generateStructureGuidelines(projectContext) { if (projectContext.techStack?.frameworks?.includes('React')) { return 'Keep components in src/components/, hooks in src/hooks/' } return 'Follow conventional directory structures' } generateTestingGuidelines(_projectContext) { return 'Write tests for all business logic, use descriptive test names' } async detectPrimaryIDE(projectContext) { try { const projectPath = projectContext.projectPath || this.projectPath // Check for IDE-specific files and folders const ideIndicators = { 'VS Code': ['.vscode/', '.vscode/settings.json', '.vscode/launch.json'], Cursor: ['.cursor/', 'cursor.json', '.cursorrules'], Windsurf: ['.windsurf/', '.windsurfrules.md', '.codeium/'], JetBrains: ['.idea/', '*.iml'], Zed: ['.zed/', 'zed.json'], } for (const [ide, indicators] of Object.entries(ideIndicators)) { for (const indicator of indicators) { if (await this.fileExists(path.join(projectPath, indicator))) { return ide } } } // Additional detection methods for VS Code without .vscode folder: // 1. Check for VS Code specific extensions in package.json const packageJsonPath = path.join(projectPath, 'package.json') if (await this.fileExists(packageJsonPath)) { const packageContent = await fs.readFile(packageJsonPath, 'utf8') const packageData = JSON.parse(packageContent) // Check for VS Code extension development if ( packageData.contributes || packageData.engines?.vscode || packageData.categories?.includes('Extension Packs') || packageData.main?.includes('extension.js') ) { return 'VS Code' } // Check for common VS Code specific dev dependencies const vscodeDevDeps = ['@types/vscode', 'vscode-test', '@vscode/test-electron'] const allDeps = { ...packageData.dependencies, ...packageData.devDependencies } if (vscodeDevDeps.some((dep) => allDeps[dep])) { return 'VS Code' } } // 2. Check for common workspace files that suggest VS Code const vscodeWorkspaceFiles = ['*.code-workspace', 'workspace.json'] for (const pattern of vscodeWorkspaceFiles) { // This is a simplified check - in practice you'd use glob matching if (await this.fileExists(path.join(projectPath, pattern.replace('*', 'project')))) { return 'VS Code' } } return 'Not specified' } catch { return 'Not specified' } } /** * Detects package manager by checking lock files */ async detectPackageManager(projectContext) { try { const projectPath = projectContext.projectPath || this.projectPath // Check for lock files in order of preference if (await this.fileExists(path.join(projectPath, 'pnpm-lock.yaml'))) { return 'pnpm' } if (await this.fileExists(path.join(projectPath, 'yarn.lock'))) { return 'yarn' } if (await this.fileExists(path.join(projectPath, 'bun.lockb'))) { return 'bun' } if (await this.fileExists(path.join(projectPath, 'package-lock.json'))) { return 'npm' } // Default fallback return 'npm' } catch { return 'npm' } } /** * Helper to check if file exists */ async fileExists(filePath) { try { await fs.access(filePath) return true } catch { return false } } /** * Extract actual scripts from package.json */ async extractPackageScripts(projectContext) { try { const projectPath = projectContext.projectPath || this.projectPath const packageJsonPath = path.join(projectPath, 'package.json') if (await this.fileExists(packageJsonPath)) { const packageJsonContent = await fs.readFile(packageJsonPath, 'utf8') const packageData = JSON.parse(packageJsonContent) return packageData.scripts || {} } } catch { // Silently fail and return empty object } return {} } async detectDevCommand(projectContext) { const pm = await this.detectPackageManager(projectContext) const scripts = await this.extractPackageScripts(projectContext) // Check for common dev script variations if (scripts.dev) { return `${pm} run dev` } if (scripts.start) { return `${pm} run start` } if (scripts.serve) { return `${pm} run serve` } return `${pm} run dev` } async detectTestCommand(projectContext) { const pm = await this.detectPackageManager(projectContext) const scripts = await this.extractPackageScripts(projectContext) if (scripts.test) { return `${pm} run test` } if (scripts['test:unit']) { return `${pm} run test:unit` } return `${pm} run test` } async detectBuildCommand(projectContext) { const pm = await this.detectPackageManager(projectContext) const scripts = await this.extractPackageScripts(projectContext) if (scripts.build) { return `${pm} run build` } if (scripts['build:prod']) { return `${pm} run build:prod` } return `${pm} run build` } async detectBuildTool(projectContext) { const techData = projectContext.techStack || projectContext.technologyData || {} const frameworks = techData.frameworks || [] // Check for turbopack in Next.js projects if ( frameworks.some( (fw) => fw.toLowerCase().includes('next.js') || fw.toLowerCase().includes('nextjs') ) ) { const scripts = await this.extractPackageScripts(projectContext) // Check if dev script uses --turbo flag if (scripts.dev?.includes('--turbo')) { return 'Turbopack (Next.js)' } return 'Next.js' } if (frameworks.some((fw) => fw.toLowerCase().includes('nuxt'))) { return 'Nuxt.js' } if (frameworks.some((fw) => fw.toLowerCase().includes('gatsby'))) { return 'Gatsby' } if (frameworks.some((fw) => fw.toLowerCase().includes('astro'))) { return 'Astro' } // Check for build tools in libraries/buildTools const buildTools = techData.buildTools || [] if (buildTools.includes('Vite')) { return 'Vite' } if (buildTools.includes('Webpack')) { return 'Webpack' } if (buildTools.includes('esbuild')) { return 'esbuild' } if (buildTools.includes('Rollup')) { return 'Rollup' } if (buildTools.includes('Parcel')) { return 'Parcel' } // Default fallback return 'npm scripts' } generateWorkflowPreferences(_rules, projectContext) { return `### Development Workflow - Start with failing tests when appropriate - Run tests before committing - Use feature flags for incomplete features - Plan for rollback strategies ### Framework-Specific Guidelines ${this.generateFrameworkSpecificGuidelines(projectContext)}` } generateFrameworkSpecificGuidelines(projectContext) { const frameworks = projectContext.techStack?.frameworks || [] const guidelines = [] if (frameworks.includes('React')) { guidelines.push('- Use React hooks over class components') guidelines.push('- Implement proper state management patterns') } if (frameworks.includes('Next.js')) { guidelines.push('- Use App Router for new routes') guidelines.push('- Leverage server components when possible') } return guidelines.join('\n') || '- Follow established patterns in the codebase' } getOptimalTimeout(projectContext) { // Longer timeout for complex builds if (projectContext.techStack?.frameworks?.includes('Next.js')) { return '180000' } return '120000' } /** * Generate main CLAUDE.md with import-based structure */ async generateMainMemory(rules, projectContext) { const projectName = path.basename(this.projectPath) const primaryLanguages = projectContext.techStack?.primaryLanguages || ['JavaScript'] const frameworks = projectContext.techStack?.frameworks || [] return `# ${projectName} - Claude Code Memory ## Project Overview ${projectContext.description || `${projectName} is a ${frameworks.join(', ') || primaryLanguages.join(', ')} project.`} ## Import Hierarchy @CLAUDE-patterns.md @CLAUDE-integrations.md ## Quick Commands ${await this.generateQuickCommandsSection(rules, projectContext)} ## Development Context ### Technology Stack - **Languages**: ${primaryLanguages.join(', ')} ${frameworks.length > 0 ? `- **Frameworks**: ${frameworks.join(', ')}` : ''} ${projectContext.techStack?.libraries?.length > 0 ? `- **Libraries**: ${projectContext.techStack.libraries.slice(0, 5).join(', ')}` : ''} ### Project Structure \`\`\` ${await this.generateProjectStructureSummary(projectContext)} \`\`\` ## Development Commands ${await this.generateDevelopmentCommands(projectContext)} ## Coding Standards ${await this.generateCodingStandards(rules, projectContext)} ## Team Collaboration - **Memory Management**: Use import-based structure for personal preferences - **Slash Commands**: Available in \`.claude/commands/\` directory - **Settings**: Configured in \`.claude/settings.json\` ## Personal Preferences Import @~/.claude/my-project-preferences.md --- *Generated by VDK CLI - Enhanced Claude Code Integration* *Last updated: ${new Date().toISOString().split('T')[0]}*` } /** * Generate CLAUDE-patterns.md for code patterns and conventions */ async generatePatternsMemory(rules, projectContext) { const patterns = projectContext.patterns || {} const namingConventions = patterns.namingConventions || {} return `# Code Patterns and Conventions ## Naming Conventions ${await this.generateNamingConventions(namingConventions)} ## Architectural Patterns ${await this.generateArchitecturalPatterns(patterns.architecturalPatterns || [])} ## Code Organization ${await this.generateCodeOrganization(projectContext)} ## Framework-Specific Patterns ${await this.generateFrameworkPatterns(rules, projectContext)} ## Testing Patterns ${await this.generateTestingPatterns(rules, projectContext)} ## Error Handling Patterns ${await this.generateErrorHandlingPatterns(rules, projectContext)} ## Performance Patterns ${await this.generatePerformancePatterns(rules, projectContext)} --- *This file contains project-specific patterns discovered through codebase analysis*` } /** * Generate CLAUDE-integrations.md for MCP and tool integrations */ async generateIntegrationsMemory(rules, projectContext) { return `# Claude Code Integrations ## MCP Server Configuration ${await this.generateMcpServerConfig(rules, projectContext)} ## Available Tools ${await this.generateAvailableTools(rules, projectContext)} ## Permission Configuration ${await this.generatePermissionConfig(rules, projectContext)} ## Hook System Integration ${await this.generateHookIntegration(rules, projectContext)} ## IDE Integration - **Primary IDE**: ${this.detectPrimaryIDE(projectContext)} - **Extensions**: Auto-detection enabled - **Diff Viewer**: Configured for IDE integration ## External Services ${await this.generateExternalServices(rules, projectContext)} --- *This file manages Claude Code's integration with external tools and services*` } /** * Generate sophisticated slash commands by fetching from remote repository */ async generateSlashCommands(rules, projectContext, categoryFilter = null) { try { // Use the centralized repository fetch method from RuleGenerator if (this.ruleGenerator?.fetchFromRepository) { const remoteCommands = await this.ruleGenerator.fetchFromRepository( projectContext, 'commands', 'claude-code', categoryFilter ) if (remoteCommands.length > 0) { console.log(`✅ Fetched ${remoteCommands.length} commands from repository`) // Transform the templates into command format return this.transformTemplatesIntoCommands(remoteCommands, projectContext) } } console.log('⚠️ No remote commands found, generating fallback commands') console.log(chalk.yellow('💡 To get more commands when available:')) console.log(chalk.gray(' • Check your GitHub token: VDK_GITHUB_TOKEN in .env.local')) console.log(chalk.gray(' • Get a token from: https://github.com/settings/tokens')) // Generate fallback commands based on detected technologies return this.generateFallbackCommands(rules, projectContext, categoryFilter) } catch (error) { console.log(`⚠️ Failed to fetch remote commands: ${error.message}`) if (error.message.includes('401')) { console.log(chalk.yellow('💡 GitHub authentication failed. To enable command fetching:')) console.log(chalk.gray(' • Add VDK_GITHUB_TOKEN=your_token to .env.local')) console.log(chalk.gray(' • Get a token from: https://github.com/settings/tokens')) console.log(chalk.gray(' • Token needs "public_repo" access for public repositories')) } // Generate fallback commands even when remote fetch fails return this.generateFallbackCommands(rules, projectContext, categoryFilter) } } /** * Generate fallback commands when remote fetch fails * @param {Array} rules - Available rules * @param {Object} projectContext - Project context * @param {Object} categoryFilter - Category filter * @returns {Array} Fallback commands */ generateFallbackCommands(_rules, projectContext, categoryFilter = null) { const commands = [] const frameworks = projectContext.techStack?.frameworks || [] const languages = projectContext.techStack?.primaryLanguages || [] const isAstroProject = frameworks.some((fw) => fw.toLowerCase().includes('astro')) const isNextJSProject = frameworks.some((fw) => fw.toLowerCase().includes('next')) const isContentProject = frameworks.some( (fw) => fw.toLowerCase().includes('starlight') || fw.toLowerCase().includes('content') ) // Core development commands (always included) commands.push({ name: 'review-code', description: 'Comprehensive code review and analysis', content: `Perform a thorough code review focusing on: - Code quality and best practices - Performance optimizations - Security considerations - Architecture and design patterns - Testing coverage Provide specific, actionable feedback with examples.`, }) commands.push({ name: 'debug-issue', description: 'Debug and troubleshoot issues', content: `Help debug the current issue by: - Analyzing error messages and stack traces - Identifying potential root causes - Suggesting debugging strategies - Providing step-by-step troubleshooting Ask for relevant code, logs, or error details if needed.`, }) commands.push({ name: 'optimize-performance', description: 'Analyze and optimize performance', content: `Analyze performance bottlenecks and provide optimization recommendations: - Code performance analysis - Bundle size optimization - Runtime performance improvements - Best practices for ${frameworks[0] || 'the current stack'} Focus on measurable improvements with implementation examples.`, }) // Astro-specific commands if (isAstroProject) { commands.push({ name: 'create-astro-component', description: 'Create new Astro component following project patterns', content: `Create a new Astro component with: - Proper TypeScript typing - Scoped CSS styling - Props validation - SEO optimization if needed - Component documentation Follow established project patterns for consistency. ## Arguments - Component name (required): $ARGUMENTS - Component type: page|layout|ui (default: ui)`, }) if (isContentProject) { commands.push({ name: 'create-content-page', description: 'Create new content page with proper frontmatter', content: `Create a new content page with: - Proper frontmatter structure - SEO metadata - Navigation integration - Content structure following site patterns ## Arguments - Page title (required): $ARGUMENTS - Content category: guide|reference|tutorial (default: guide)`, }) commands.push({ name: 'update-navigation', description: 'Update site navigation and content structure', content: `Update the site navigation to include new content: - Add to sidebar configuration - Update content collections - Ensure proper ordering - Maintain consistent navigation patterns Review current navigation structure and suggest improvements.`, }) } } // Next.js specific commands if (isNextJSProject) { commands.push({ name: 'create-nextjs-page', description: 'Create new Next.js page with App Router', content: `Create a new Next.js page using App Router: - Server Component by default - Proper TypeScript typing - SEO metadata - Error boundaries - Loading states ## Arguments - Page path (required): $ARGUMENTS - Page type: static|dynamic|api (default: static)`, }) commands.push({ name: 'create-api-route', description: 'Create new API route with validation', content: `Create a new API route with: - Request/response typing - Input validation - Error handling - Rate limiting considerations - Proper HTTP status codes ## Arguments - Route path (required): $ARGUMENTS - HTTP method: GET|POST|PUT|DELETE (default: GET)`, }) } // TypeScript specific commands if (languages.includes('typescript') || languages.includes('TypeScript')) { commands.push({ name: 'improve-types', description: 'Improve TypeScript types and interfaces', content: `Analyze and improve TypeScript usage: - Add missing type annotations - Improve type safety - Reduce any usage - Create utility types where beneficial - Improve interface design Focus on making types more precise and maintainable.`, }) } // Filter by categories if specified if (categoryFilter?.categories) { const allowedCategories = categoryFilter.categories return commands.filter((cmd) => { // Map commands to categories (simplified) const commandCategories = { 'review-code': ['development', 'quality'], 'debug-issue': ['development'], 'optimize-performance': ['development', 'quality'], 'create-astro-component': ['development'], 'create-content-page': ['development', 'workflow'], 'update-navigation': ['workflow'], 'create-nextjs-page': ['development'], 'create-api-route': ['development'], 'improve-types': ['development', 'quality'], } const cmdCategories = commandCategories[cmd.name] || ['development'] return cmdCategories.some((cat) => allowedCategories.includes(cat)) }) } return commands } /** * Transform repository templates into Claude Code command format * @param {Array} templates - Templates from repository * @param {Object} projectContext - Project context * @returns {Array} Commands in Claude Code format */ transformTemplatesIntoCommands(templates, _projectContext) { const commands = [] for (const template of templates) { try { // Parse the template content as a command const parsedCommand = this.parseClaudeCodeCommand(template.content) if (parsedCommand) { commands.push({ name: template.name.replace('.md', ''), content: template.content, metadata: parsedCommand, category: this.getCategoryFromPath(template.path), source: 'repository', relevanceScore: template.relevanceScore || 0.5, }) } } catch (error) { console.log(`Warning: Could not parse command ${template.name}: ${error.message}`) } } return commands.sort((a, b) => (b.relevanceScore || 0) - (a.relevanceScore || 0)) } /** * Extract category from file path * @param {string} path - File path * @returns {string} Category name */ getCategoryFromPath(path) { const pathParts = path.split('/') // Path structure: .ai/commands/claude-code/category/command.md if (pathParts.length >= 4) { return pathParts[3] // Get the category directory name } return 'general' } /** * Fetch commands from VDK-Blueprints repository */ async fetchRemoteCommands(platform, projectContext) { const VDK_RULES_REPO_API_URL = 'https://api.github.com/repos/entro314-labs/VDK-Blueprints' const commands = [] try { // Fetch command categories for the platform const response = await fetch(`${VDK_RULES_REPO_API_URL}/contents/.ai/commands/${platform}`) if (!response.ok) { return [] } const categories = await response.json() // Fetch commands from each category for (const category of categories.filter((item) => item.type === 'dir')) { try { const categoryResponse = await fetch(category.url) if (categoryResponse.ok) { const commandFiles = await categoryResponse.json() for (const file of commandFiles.filter((f) => f.name.endsWith('.md'))) { const commandContent = await this.downloadFile(file.download_url) if (commandContent) { // Parse and validate command content const parsedCommand = this.parseClaudeCodeCommand(commandContent) if (parsedCommand && (await this.validateClaudeCodeCommand(parsedCommand))) { commands.push({ name: file.name.replace('.md', ''), content: commandContent, metadata: parsedCommand, category: category.name, source: 'repository', relevanceScore: this.calculateCommandRelevance(parsedCommand, projectContext), }) } } } } } catch (error) { console.log(`Could not fetch commands from ${category.name}: ${error.message}`) } } } catch (error) { console.log(`Failed to fetch command categories: ${error.message}`) } // Sort by relevance score return commands.sort((a, b) => (b.relevanceScore || 0) - (a.relevanceScore || 0)) } /** * Parse Claude Code command frontmatter */ parseClaudeCodeCommand(content) { try { const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/) if (!frontmatterMatch) { return null } // Simple YAML parsing for the schema fields const yamlContent = frontmatterMatch[1] const parsed = {} const lines = yamlContent.split('\n') let _currentKey = null let currentObject = null for (const line of lines) { const trimmed = line.trim() if (!trimmed || trimmed.startsWith('#')) { continue } if (line.match(/^[a-zA-Z]/)) { // Top-level key const [key, value] = trimmed.split(':').map((s) => s.trim()) if (value && value !== '') { parsed[key] = value.replace(/['"]/g, '') } else { _currentKey = key currentObject = {} parsed[key] = currentObject } } else if (currentObject && line.match(/^\s+[a-zA-Z]/)) { // Nested key const [key, value] = trimmed.split(':').map((s) => s.trim()) if (value && value !== '') { currentObject[key] = value.replace(/['"]/g, '') } } } return parsed } catch (error) { console.log(`Failed to parse command frontmatter: ${error.message}`) return null } } /** * Validate Claude Code command against the official schema */ async validateClaudeCodeCommand(commandData) { if (!commandData) { return false } try { const validation = await validateCommand(commandData) if (!validation.valid) { if (this.verbose) { console.log('Command validation failed:') validation.errors.forEach((error) => console.log(` - ${error}`)) } return false } return true } catch (error) { console.log(`Schema validation error: ${error.message}`) return false } } /** * Calculate command relevance based on project context */ calculateCommandRelevance(commandData, projectContext) { let score = 0.5 // Base score const frameworks = projectContext.techStack?.frameworks || [] const languages = projectContext.techStack?.primaryLanguages || [] const tags = commandData.tags || [] const category = commandData.category || '' // Framework matching frameworks.forEach((framework) => { if (tags.some((tag) => tag.toLowerCase().includes(framework.toLowerCase()))) { score += 0.3 } }) // Language matching languages.forEach((language) => { if (tags.some((tag) => tag.toLowerCase().includes(language.toLowerCase()))) { score += 0.2 } }) // Category relevance if (category === 'development') { score +=