# How Claude Code's Package Manager Detection System Works: 7-Step Priority Hierarchy Explained

> Discover how Claude Code's package manager detection works with its 7-step priority hierarchy. Learn about environment variables, config files, lock files, and binary checks.

- Repository: [WorldFlowAI/everything-claude-code](https://github.com/WorldFlowAI/everything-claude-code)
- Tags: internals
- Published: 2026-09-07

---

**Claude Code detects which JavaScript package manager to use through a cascading 7-step priority system defined in [`scripts/lib/package-manager.js`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/scripts/lib/package-manager.js), checking environment variables, config files, lock files, and installed binaries before defaulting to npm.**

The everything-claude-code repository implements a robust package manager detection system that automatically identifies whether a project uses **npm**, **pnpm**, **yarn**, or **bun**. This logic ensures Claude Code executes the correct commands for installing dependencies, running scripts, and executing binaries—regardless of how a project is configured.

## The 7-Step Detection Hierarchy

The `getPackageManager()` function in [`scripts/lib/package-manager.js`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/scripts/lib/package-manager.js) (lines 57-136) evaluates sources in strict priority order:

### 1. Environment Variable Override

The system first checks for `CLAUDE_PACKAGE_MANAGER`. If set to a supported manager, this value wins immediately.

```javascript
// Force pnpm regardless of project files
export CLAUDE_PACKAGE_MANAGER=pnpm

```

This override allows CI pipelines and power users to bypass all automatic detection (lines 60-68).

### 2. Project-Specific Config File

Next, the detector looks for [`.claude/package-manager.json`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/.claude/package-manager.json) in the project root. This JSON file can pin a manager for that specific codebase.

```json
{
  "packageManager": "yarn"
}

```

The `setProjectPackageManager(pmName, projectDir)` helper persists this config (lines 70-86).

### 3. package.json `packageManager` Field

The detector reads the standard `packageManager` field from [`package.json`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/package.json), respecting the npm specification. Values like `"pnpm@8.6.0"` are parsed to extract just the manager name before the `@` character (lines 108-125).

### 4. Lock-File Detection

When no explicit config exists, the system scans for lock files with this priority:

| Priority | Lock File | Manager |
|----------|-----------|---------|
| 1 | [`pnpm-lock.yaml`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/pnpm-lock.yaml) | pnpm |
| 2 | `bun.lockb` | bun |
| 3 | `yarn.lock` | yarn |
| 4 | [`package-lock.json`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/package-lock.json) | npm |

This ordering reflects modern JavaScript ecosystem preferences (lines 92-101).

### 5. Global User Preference

The detector loads `~/.claude/package-manager.json` to check for a user-wide default. The `setPreferredPackageManager(pmName)` function writes this file (lines 108-116).

### 6. Installed Binary Check

If no configuration indicates a preference, the system calls `commandExists` for each manager in priority order (`['pnpm','bun','yarn','npm']`) and returns the first one found on `PATH` (lines 118-128).

### 7. npm Fallback

When all else fails, the system defaults to **npm** (lines 130-135).

## Core API Functions

The package manager module exports several utilities for consuming the detection results:

### `getPackageManager(options)`

Returns a descriptor object with the selected manager's details:

```javascript
const { getPackageManager } = require('./scripts/lib/package-manager');

const pm = getPackageManager();
// {
//   name: 'pnpm',
//   config: { lockFile: 'pnpm-lock.yaml', installCmd: 'install', ... },
//   source: 'lock-file'
// }

```

The `source` property indicates which detection step succeeded—useful for debugging.

### `getRunCommand(script, options)`

Generates the correct command to run package scripts:

```javascript
const { getRunCommand } = require('./scripts/lib/package-manager');

getRunCommand('dev');      // "pnpm dev"
getRunCommand('test');     // "pnpm test"
getRunCommand('install');  // "pnpm install"

```

### `getExecCommand(binary, args, options)`

Builds commands to execute binaries through the detected manager:

```javascript
const { getExecCommand } = require('./scripts/lib/package-manager');

getExecCommand('tsc', ['--noEmit']);
// "pnpm exec tsc --noEmit" or "npx tsc --noEmit" etc.

```

### `getCommandPattern(action)`

Produces a regex matching all supported ways to invoke an action:

```javascript
const { getCommandPattern } = require('./scripts/lib/package-manager');

const installPattern = getCommandPattern('install');
// /(?:npm install|pnpm install|yarn(?: install)?|bun install)/

```

This powers Claude's ability to recognize user intent across different manager syntaxes (lines 132-174).

## Setting and Persisting Preferences

### Global Preference

```javascript
const { setPreferredPackageManager } = require('./scripts/lib/package-manager');

setPreferredPackageManager('yarn');
// Writes ~/.claude/package-manager.json

```

### Project-Level Preference

```javascript
const { setProjectPackageManager } = require('./scripts/lib/package-manager');

setProjectPackageManager('bun', '/path/to/project');
// Creates /path/to/project/.claude/package-manager.json

```

## PACKAGE_MANAGERS Constant

Each supported manager is defined in the `PACKAGE_MANAGERS` constant (lines 13-53):

```javascript
{
  npm: {
    lockFile: 'package-lock.json',
    install: 'install',
    run: 'run',
    exec: 'exec',
    defaultScripts: ['test', 'build', 'start', 'lint']
  },
  pnpm: {
    lockFile: 'pnpm-lock.yaml',
    install: 'install',
    run: '',
    exec: 'exec',
    defaultScripts: ['dev', 'test', 'build', 'lint', 'format']
  },
  // ... yarn, bun
}

```

This central configuration drives command generation and lock-file detection throughout the codebase.

## Integration Points

The detection system is invoked by:

- **[`scripts/hooks/session-start.js`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/scripts/hooks/session-start.js)** – Reports the detected manager at session startup
- **[`scripts/setup-package-manager.js`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/scripts/setup-package-manager.js)** – Interactive CLI for viewing and changing preferences
- **Test suites** – [`tests/lib/package-manager.test.js`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/tests/lib/package-manager.test.js) verifies each detection layer

## Summary

- **Seven-step priority hierarchy** determines the active manager: environment variable → project config → [`package.json`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/package.json) field → lock file → global config → installed binary → npm default
- **[`scripts/lib/package-manager.js`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/scripts/lib/package-manager.js)** contains all detection logic in ~175 lines
- **Four exported functions** provide manager detection, command generation, and preference persistence
- **Regex patterns** enable cross-manager command recognition via `getCommandPattern()`
- **Lock-file priority** of `pnpm > bun > yarn > npm` reflects modern tooling trends

## Frequently Asked Questions

### How do I force Claude Code to use a specific package manager?

Set the `CLAUDE_PACKAGE_MANAGER` environment variable to `npm`, `pnpm`, `yarn`, or `bun`. This overrides all automatic detection and project-level settings.

### Where does Claude Code store my preferred package manager?

Global preferences live in `~/.claude/package-manager.json`. Project-specific preferences are stored in [`.claude/package-manager.json`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/.claude/package-manager.json) relative to the project root.

### Why does pnpm take priority over yarn in lock-file detection?

The priority list `['pnpm','bun','yarn','npm']` in [`scripts/lib/package-manager.js`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/scripts/lib/package-manager.js) (line 92) reflects modern JavaScript ecosystem trends where pnpm and bun are increasingly preferred for their performance characteristics.

### Can I use the package manager detection in my own scripts?

Yes. Require [`scripts/lib/package-manager.js`](https://github.com/WorldFlowAI/everything-claude-code/blob/main/scripts/lib/package-manager.js) and call `getPackageManager()` to receive the detected manager with its configuration and detection source.