Skip to main content
Glama
cbunting99

MCP Code Analysis & Quality Server

by cbunting99
PackageManagerDetector.ts6.33 kB
// Copyright 2025 Chris Bunting // Brief: Detects package managers in project directories // Scope: Identifies which package managers are being used in a project import { PackageManager } from '@mcp-code-analysis/shared-types'; import * as fs from 'fs'; import * as path from 'path'; import { Logger } from '../utils/Logger.js'; export interface PackageManagerDetection { manager: PackageManager; confidence: number; files: string[]; } export class PackageManagerDetector { private logger: Logger; private packageManagerSignatures: Map<PackageManager, string[]>; constructor(logger?: Logger) { this.logger = logger || new Logger(); this.packageManagerSignatures = new Map([ [PackageManager.NPM, ['package.json', 'package-lock.json', 'node_modules']], [PackageManager.YARN, ['package.json', 'yarn.lock', 'node_modules']], [PackageManager.PIP, ['requirements.txt', 'setup.py', 'pyproject.toml', 'Pipfile', 'poetry.lock']], [PackageManager.CARGO, ['Cargo.toml', 'Cargo.lock']], [PackageManager.MAVEN, ['pom.xml', 'mvnw', '.mvn']], [PackageManager.GRADLE, ['build.gradle', 'build.gradle.kts', 'gradlew', '.gradle']], [PackageManager.GO, ['go.mod', 'go.sum', 'go.work']], ]); } async detectPackageManagers(projectPath: string): Promise<PackageManagerDetection[]> { this.logger.debug(`Detecting package managers in: ${projectPath}`); if (!fs.existsSync(projectPath)) { throw new Error(`Project path does not exist: ${projectPath}`); } const detections: PackageManagerDetection[] = []; for (const [manager, signatures] of this.packageManagerSignatures) { const detection = await this.detectPackageManager(projectPath, manager, signatures); if (detection.confidence > 0) { detections.push(detection); } } // Sort by confidence level detections.sort((a, b) => b.confidence - a.confidence); this.logger.debug(`Detected package managers: ${detections.map(d => `${d.manager} (${d.confidence})`).join(', ')}`); return detections; } private async detectPackageManager( projectPath: string, manager: PackageManager, signatures: string[] ): Promise<PackageManagerDetection> { const foundFiles: string[] = []; let confidence = 0; for (const signature of signatures) { const filePath = path.join(projectPath, signature); if (fs.existsSync(filePath)) { foundFiles.push(signature); // Assign confidence based on file importance if (this.isPrimaryManifest(signature, manager)) { confidence += 0.6; } else if (this.isLockFile(signature, manager)) { confidence += 0.3; } else { confidence += 0.1; } } } // Additional confidence checks confidence += await this.getAdditionalConfidence(projectPath, manager, foundFiles); return { manager, confidence: Math.min(confidence, 1.0), files: foundFiles, }; } private isPrimaryManifest(filename: string, manager: PackageManager): boolean { const primaryManifests: Record<PackageManager, string[]> = { [PackageManager.NPM]: ['package.json'], [PackageManager.YARN]: ['package.json'], [PackageManager.PIP]: ['requirements.txt', 'setup.py', 'pyproject.toml'], [PackageManager.CARGO]: ['Cargo.toml'], [PackageManager.MAVEN]: ['pom.xml'], [PackageManager.GRADLE]: ['build.gradle', 'build.gradle.kts'], [PackageManager.GO]: ['go.mod'], }; return primaryManifests[manager].includes(filename); } private isLockFile(filename: string, manager: PackageManager): boolean { const lockFiles: Record<PackageManager, string[]> = { [PackageManager.NPM]: ['package-lock.json'], [PackageManager.YARN]: ['yarn.lock'], [PackageManager.PIP]: ['poetry.lock', 'Pipfile.lock'], [PackageManager.CARGO]: ['Cargo.lock'], [PackageManager.MAVEN]: [], [PackageManager.GRADLE]: [], [PackageManager.GO]: ['go.sum'], }; return lockFiles[manager].includes(filename); } private async getAdditionalConfidence( projectPath: string, manager: PackageManager, foundFiles: string[] ): Promise<number> { let additionalConfidence = 0; // Check for workspace/monorepo configurations if (manager === PackageManager.NPM || manager === PackageManager.YARN) { if (foundFiles.includes('package.json')) { const packageJsonPath = path.join(projectPath, 'package.json'); try { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); if (packageJson.workspaces) { additionalConfidence += 0.1; } } catch (error) { this.logger.debug(`Failed to parse package.json: ${error}`); } } } // Check for Go workspaces if (manager === PackageManager.GO) { if (foundFiles.includes('go.work')) { additionalConfidence += 0.2; } } // Check for Maven wrapper if (manager === PackageManager.MAVEN) { if (foundFiles.includes('mvnw') || fs.existsSync(path.join(projectPath, '.mvn'))) { additionalConfidence += 0.1; } } // Check for Gradle wrapper if (manager === PackageManager.GRADLE) { if (foundFiles.includes('gradlew') || fs.existsSync(path.join(projectPath, '.gradle'))) { additionalConfidence += 0.1; } } return additionalConfidence; } async getPrimaryPackageManager(projectPath: string): Promise<PackageManager | null> { const detections = await this.detectPackageManagers(projectPath); if (detections.length === 0) { return null; } // Return the manager with highest confidence return detections[0].manager; } async isPackageManagerUsed(projectPath: string, manager: PackageManager): Promise<boolean> { const detections = await this.detectPackageManagers(projectPath); return detections.some(d => d.manager === manager); } async getPackageManagerFiles(projectPath: string, manager: PackageManager): Promise<string[]> { const detections = await this.detectPackageManagers(projectPath); const detection = detections.find(d => d.manager === manager); return detection ? detection.files : []; } }

Latest Blog Posts

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/cbunting99/mcp-code-analysis-server'

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