@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
JavaScript
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 +=