Skip to main content
Glama

MCP Memory Service

project-detector.js11.6 kB
/** * Project Context Detection Utility * Analyzes the current directory to determine project type, language, and context */ const fs = require('fs').promises; const path = require('path'); const { execSync } = require('child_process'); /** * Detect programming language from file extensions */ async function detectLanguage(directory) { try { const files = await fs.readdir(directory, { withFileTypes: true }); const extensions = new Map(); // Count file extensions for (const file of files) { if (file.isFile()) { const ext = path.extname(file.name).toLowerCase(); if (ext) { extensions.set(ext, (extensions.get(ext) || 0) + 1); } } } // Language detection rules const languageMap = { '.js': 'JavaScript', '.ts': 'TypeScript', '.jsx': 'React/JavaScript', '.tsx': 'React/TypeScript', '.py': 'Python', '.rs': 'Rust', '.go': 'Go', '.java': 'Java', '.cpp': 'C++', '.c': 'C', '.cs': 'C#', '.php': 'PHP', '.rb': 'Ruby', '.swift': 'Swift', '.kt': 'Kotlin', '.scala': 'Scala', '.sh': 'Shell', '.md': 'Documentation' }; // Find most common language extension let primaryLanguage = 'Unknown'; let maxCount = 0; for (const [ext, count] of extensions.entries()) { if (languageMap[ext] && count > maxCount) { maxCount = count; primaryLanguage = languageMap[ext]; } } return { primary: primaryLanguage, extensions: Object.fromEntries(extensions), confidence: maxCount > 0 ? Math.min(maxCount / 10, 1) : 0 }; } catch (error) { return { primary: 'Unknown', extensions: {}, confidence: 0 }; } } /** * Detect framework and tools from configuration files */ async function detectFramework(directory) { const frameworks = []; const tools = []; try { const files = await fs.readdir(directory); // Check for common configuration files const configFiles = { 'package.json': async () => { const pkg = JSON.parse(await fs.readFile(path.join(directory, 'package.json'), 'utf8')); // Check dependencies for frameworks const deps = { ...pkg.dependencies, ...pkg.devDependencies }; if (deps.react || deps['@types/react']) frameworks.push('React'); if (deps.vue || deps['@vue/cli']) frameworks.push('Vue.js'); if (deps.angular || deps['@angular/core']) frameworks.push('Angular'); if (deps.next || deps['next']) frameworks.push('Next.js'); if (deps.express || deps['express']) frameworks.push('Express.js'); if (deps.fastify) frameworks.push('Fastify'); if (deps.svelte || deps['svelte']) frameworks.push('Svelte'); tools.push('npm'); return pkg.name || 'node-project'; }, 'pyproject.toml': async () => { tools.push('Python'); const content = await fs.readFile(path.join(directory, 'pyproject.toml'), 'utf8'); // Extract project name from pyproject.toml const nameMatch = content.match(/^name\s*=\s*["']([^"']+)["']/m); if (content.includes('django')) frameworks.push('Django'); if (content.includes('flask')) frameworks.push('Flask'); if (content.includes('fastapi')) frameworks.push('FastAPI'); if (content.includes('pytest')) tools.push('pytest'); if (content.includes('poetry')) tools.push('Poetry'); return nameMatch ? nameMatch[1] : 'python-project'; }, 'Cargo.toml': async () => { tools.push('Cargo'); const content = await fs.readFile(path.join(directory, 'Cargo.toml'), 'utf8'); const nameMatch = content.match(/^name\s*=\s*["']([^"']+)["']/m); if (content.includes('actix-web')) frameworks.push('Actix Web'); if (content.includes('rocket')) frameworks.push('Rocket'); if (content.includes('warp')) frameworks.push('Warp'); if (content.includes('tokio')) frameworks.push('Tokio'); return nameMatch ? nameMatch[1] : 'rust-project'; }, 'go.mod': async () => { tools.push('Go Modules'); const content = await fs.readFile(path.join(directory, 'go.mod'), 'utf8'); const moduleMatch = content.match(/^module\s+(.+)$/m); if (content.includes('gin-gonic/gin')) frameworks.push('Gin'); if (content.includes('gorilla/mux')) frameworks.push('Gorilla Mux'); if (content.includes('fiber')) frameworks.push('Fiber'); return moduleMatch ? path.basename(moduleMatch[1]) : 'go-project'; }, 'pom.xml': () => { tools.push('Maven'); frameworks.push('Java/Maven'); return 'java-maven-project'; }, 'build.gradle': () => { tools.push('Gradle'); frameworks.push('Java/Gradle'); return 'java-gradle-project'; }, 'docker-compose.yml': () => { tools.push('Docker Compose'); return null; }, 'Dockerfile': () => { tools.push('Docker'); return null; }, '.env': () => { tools.push('Environment Config'); return null; } }; let projectName = null; for (const file of files) { if (configFiles[file]) { const result = await configFiles[file](); if (result && !projectName) { projectName = result; } } } return { frameworks, tools, projectName }; } catch (error) { return { frameworks: [], tools: [], projectName: null }; } } /** * Get Git repository information */ function getGitInfo(directory) { try { const gitDir = path.join(directory, '.git'); // Check if this is a git repository const isGitRepo = require('fs').existsSync(gitDir); if (!isGitRepo) { return { isRepo: false }; } // Get repository information const remoteBranch = execSync('git branch --show-current', { cwd: path.resolve(directory), encoding: 'utf8' }).trim(); const remoteUrl = execSync('git config --get remote.origin.url', { cwd: path.resolve(directory), encoding: 'utf8' }).trim(); const lastCommit = execSync('git log -1 --pretty=format:"%h %s"', { cwd: path.resolve(directory), encoding: 'utf8' }).trim(); // Extract repository name from URL let repoName = 'unknown-repo'; if (remoteUrl) { const match = remoteUrl.match(/([^\/]+)(?:\.git)?$/); if (match) { repoName = match[1].replace('.git', ''); } } return { isRepo: true, branch: remoteBranch, remoteUrl, repoName, lastCommit }; } catch (error) { return { isRepo: false, error: error.message }; } } // ANSI Colors for console output const COLORS = { RESET: '\x1b[0m', BRIGHT: '\x1b[1m', DIM: '\x1b[2m', CYAN: '\x1b[36m', GREEN: '\x1b[32m', BLUE: '\x1b[34m', YELLOW: '\x1b[33m', GRAY: '\x1b[90m', RED: '\x1b[31m' }; /** * Main project context detection function with enhanced visual output */ async function detectProjectContext(directory = process.cwd()) { try { const directoryName = path.basename(directory); console.log(`${COLORS.BLUE}📂 Project Detector${COLORS.RESET} ${COLORS.DIM}→${COLORS.RESET} Analyzing ${COLORS.BRIGHT}${directoryName}${COLORS.RESET}`); // Get basic directory information // Detect language const language = await detectLanguage(directory); // Detect framework and tools const framework = await detectFramework(directory); // Get Git information const git = getGitInfo(directory); // Determine project name (priority: git repo > config file > directory name) const projectName = framework.projectName || git.repoName || directoryName; // Calculate confidence score let confidence = 0.5; // Base confidence if (git.isRepo) confidence += 0.3; if (framework.frameworks.length > 0) confidence += 0.2; if (language.confidence > 0.5) confidence += language.confidence * 0.3; const context = { name: projectName, directory, language: language.primary, languageDetails: language, frameworks: framework.frameworks, tools: framework.tools, git: git, confidence: Math.min(confidence, 1.0), metadata: { detectedAt: new Date().toISOString(), analyzer: 'claude-hooks-project-detector', version: '1.1.0' } }; // Enhanced output with confidence indication const confidencePercent = (context.confidence * 100).toFixed(0); const confidenceColor = context.confidence > 0.8 ? COLORS.GREEN : context.confidence > 0.6 ? COLORS.YELLOW : COLORS.GRAY; console.log(`${COLORS.BLUE}📊 Detection Result${COLORS.RESET} ${COLORS.DIM}→${COLORS.RESET} ${COLORS.BRIGHT}${context.name}${COLORS.RESET} ${COLORS.GRAY}(${context.language})${COLORS.RESET} ${COLORS.DIM}•${COLORS.RESET} ${confidenceColor}${confidencePercent}%${COLORS.RESET}`); return context; } catch (error) { console.error(`${COLORS.RED}❌ Project Detector Error${COLORS.RESET} ${COLORS.DIM}→${COLORS.RESET} ${error.message}`); // Return minimal context on error return { name: path.basename(directory), directory, language: 'Unknown', frameworks: [], tools: [], confidence: 0.1, error: error.message }; } } module.exports = { detectProjectContext, detectLanguage, detectFramework, getGitInfo }; // Direct execution support for testing if (require.main === module) { detectProjectContext(process.cwd()) .then(context => { console.log('\n=== PROJECT CONTEXT ==='); console.log(JSON.stringify(context, null, 2)); console.log('=== END CONTEXT ===\n'); }) .catch(error => console.error('Detection failed:', error)); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/doobidoo/mcp-memory-service'

If you have feedback or need assistance with the MCP directory API, please join our Discord server