detect_package_manager
Identifies the package manager in use by analyzing lockfiles and package.json, then provides the correct commands for installing dependencies, running scripts, and executing packages.
Instructions
Detect which package manager (pnpm, yarn, npm, bun) is being used in a workspace by examining lockfiles and package.json. Returns the appropriate commands to use for installing dependencies, running scripts, and executing packages. Use this tool BEFORE suggesting package installation or script execution commands to ensure you use the correct package manager.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| workspacePath | Yes | Absolute path to the workspace directory to analyze (e.g., '/path/to/project') |
Implementation Reference
- index.js:51-187 (registration)Tool registration in ListToolsRequestSchema handler. The 'detect_package_manager' tool is declared at lines 170-185 with name, description, and inputSchema (requires workspacePath).
setupHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "search_ember_docs", description: "Search through Ember.js documentation including API docs, guides, and community content. Returns relevant documentation with links to official sources. Use this for general queries about Ember concepts, features, or usage.", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query (e.g., 'component lifecycle', 'tracked properties', 'routing')", }, category: { type: "string", enum: ["all", "api", "guides", "community"], description: "Filter by documentation category (default: all)", }, limit: { type: "number", description: "Maximum number of results (default: 5)", default: 5, }, }, required: ["query"], }, }, { name: "get_api_reference", description: "Get detailed API reference documentation for a specific Ember class, module, or method. Returns full API documentation including parameters, return values, examples, and links to official API docs.", inputSchema: { type: "object", properties: { name: { type: "string", description: "Name of the API element (e.g., 'Component', '@glimmer/component', 'Service', 'Router')", }, type: { type: "string", enum: ["class", "module", "method", "property"], description: "Type of API element (optional)", }, }, required: ["name"], }, }, { name: "get_best_practices", description: "Get Ember best practices and recommendations for specific topics. This includes modern patterns, anti-patterns to avoid, performance tips, and community-approved approaches. Always use this when providing implementation advice.", inputSchema: { type: "object", properties: { topic: { type: "string", description: "Topic to get best practices for (e.g., 'component patterns', 'state management', 'testing', 'performance')", }, }, required: ["topic"], }, }, { name: "get_ember_version_info", description: "Get information about Ember versions, including current stable version, what's new in recent releases, and migration guides. Useful for understanding version-specific features and deprecations.", inputSchema: { type: "object", properties: { version: { type: "string", description: "Specific version to get info about (optional, returns latest if not specified)", }, }, }, }, { name: "get_npm_package_info", description: "Get comprehensive information about an npm package including latest version, description, dependencies, maintainers, and more. Essential for understanding package details before upgrading dependencies.", inputSchema: { type: "object", properties: { packageName: { type: "string", description: "Name of the npm package (e.g., 'ember-source', '@glimmer/component', 'ember-cli')", }, }, required: ["packageName"], }, }, { name: "compare_npm_versions", description: "Compare a current package version with the latest available version on npm. Shows if an update is needed and provides version details to help with dependency upgrades.", inputSchema: { type: "object", properties: { packageName: { type: "string", description: "Name of the npm package (e.g., 'ember-source', '@glimmer/component')", }, currentVersion: { type: "string", description: "Current version being used (e.g., '4.12.0', '1.1.2')", }, }, required: ["packageName", "currentVersion"], }, }, { name: "detect_package_manager", description: "Detect which package manager (pnpm, yarn, npm, bun) is being used in a workspace by examining lockfiles and package.json. Returns the appropriate commands to use for installing dependencies, running scripts, and executing packages. Use this tool BEFORE suggesting package installation or script execution commands to ensure you use the correct package manager.", inputSchema: { type: "object", properties: { workspacePath: { type: "string", description: "Absolute path to the workspace directory to analyze (e.g., '/path/to/project')", }, }, required: ["workspacePath"], }, }, ], })); - index.js:170-185 (schema)Input schema for detect_package_manager tool. Defines a single required parameter 'workspacePath' (string) - absolute path to the workspace directory.
{ name: "detect_package_manager", description: "Detect which package manager (pnpm, yarn, npm, bun) is being used in a workspace by examining lockfiles and package.json. Returns the appropriate commands to use for installing dependencies, running scripts, and executing packages. Use this tool BEFORE suggesting package installation or script execution commands to ensure you use the correct package manager.", inputSchema: { type: "object", properties: { workspacePath: { type: "string", description: "Absolute path to the workspace directory to analyze (e.g., '/path/to/project')", }, }, required: ["workspacePath"], }, }, - index.js:215-216 (handler)CallToolRequestSchema switch case dispatching 'detect_package_manager' to handleDetectPackageManager(args).
case "detect_package_manager": return await this.handleDetectPackageManager(args); - index.js:489-515 (handler)handleDetectPackageManager method: calls packageManagerDetector.detectPackageManager(workspacePath), formats result via formatDetectionResult, returns text content or error.
async handleDetectPackageManager(args) { const { workspacePath } = args; try { const result = await this.packageManagerDetector.detectPackageManager(workspacePath); const formattedResult = this.packageManagerDetector.formatDetectionResult(result); return { content: [ { type: "text", text: formattedResult, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error detecting package manager: ${error.message}`, }, ], isError: true, }; } } - PackageManagerDetector class containing detectPackageManager() (checks lockfiles: pnpm-lock.yaml, yarn.lock, package-lock.json, bun.lockb; then package.json packageManager field; falls back to npm), fileExists(), formatDetectionResult(), getCommandForScenario(), and getRunner().
import { promises as fs } from 'fs'; import { join, resolve } from 'path'; /** * Service for detecting which package manager is being used in a project */ export class PackageManagerDetector { constructor() { // Priority order for detection (lockfile -> manager name) this.lockfiles = { 'pnpm-lock.yaml': 'pnpm', 'yarn.lock': 'yarn', 'package-lock.json': 'npm', 'bun.lockb': 'bun', }; } getRunner(manager) { return manager === 'npm' ? 'npx' : manager === 'bun' ? 'bunx' : manager; } /** * Detect the package manager used in a workspace * @param {string} workspacePath - Absolute path to the workspace directory * @returns {Promise<Object>} Package manager information */ async detectPackageManager(workspacePath) { const resolvedPath = resolve(workspacePath); // Check for lockfiles for (const [lockfile, manager] of Object.entries(this.lockfiles)) { if (await this.fileExists(join(resolvedPath, lockfile))) { return { manager, lockfile, runner: this.getRunner(manager), detectionMethod: 'lockfile', confidence: 'high', }; } } // Check package.json for packageManager field const packageJsonPath = join(resolvedPath, 'package.json'); if (await this.fileExists(packageJsonPath)) { try { const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8')); if (packageJson.packageManager) { const manager = packageJson.packageManager.match(/^([a-z]+)@/)?.[1]; if (manager) { return { manager, lockfile: null, runner: this.getRunner(manager), detectionMethod: 'packageManager field', confidence: 'high', }; } } } catch (error) { // Continue to fallback } } // Fallback to npm (most common default) return { manager: 'npm', lockfile: null, runner: 'npx', detectionMethod: 'default fallback', confidence: 'low', }; } /** * Check if a file exists * @param {string} filePath - Path to check * @returns {Promise<boolean>} True if file exists */ async fileExists(filePath) { try { await fs.access(filePath); return true; } catch { return false; } } /** * Format detection result as human-readable text * @param {Object} result - Detection result from detectPackageManager * @returns {string} Formatted output */ formatDetectionResult(result) { const { manager, runner, lockfile, detectionMethod, confidence } = result; let text = `# Package Manager: ${manager}\n\n`; if (lockfile) { text += `Detected via **${lockfile}** (${confidence} confidence)\n\n`; } else { text += `Detected via **${detectionMethod}** (${confidence} confidence)\n\n`; } text += `## Commands to use:\n\n`; text += `- Install: \`${manager} install\`\n`; text += `- Add package: \`${manager} ${manager === 'npm' ? 'install' : 'add'} <pkg>\`\n`; text += `- Remove package: \`${manager} ${manager === 'npm' ? 'uninstall' : 'remove'} <pkg>\`\n`; text += `- Run script: \`${manager} run <script>\`\n`; text += `- Execute binary: \`${runner} <command>\`\n`; if (manager === 'npm') { text += `\n**npm-specific notes:**\n`; text += `- Scripts require \`npm run <script>\` (can't omit \`run\`)\n`; text += `- Binaries use \`npx <command>\` (not \`npm exec\`)\n`; text += `- Pass args to scripts with \`--\`: \`npm run test -- --watch\`\n`; } if (confidence === 'low') { text += `\n⚠️ **Warning:** No lockfile found. Defaulting to npm. Consider committing your lockfile.\n`; } return text; } /** * Get usage instructions for a specific scenario * @param {Object} result - Detection result * @param {string} scenario - Scenario type (install, add, remove, run, execute) * @returns {string} Command to use */ getCommandForScenario(result, scenario) { const { manager, runner } = result; const commands = { install: `${manager} install`, add: `${manager} ${manager === 'npm' ? 'install' : 'add'}`, remove: `${manager} ${manager === 'npm' ? 'uninstall' : 'remove'}`, run: `${manager} run`, execute: runner, }; return commands[scenario] || manager; } }