Skip to main content
Better PM detects your position in a monorepo and automatically adjusts install behavior to prevent accidental full-monorepo installations.

How Context Detection Works

When you run pm i, Better PM analyzes your current directory to determine the monorepo context:
1

Find the nearest package.json

Better PM searches upward from your current directory using findUpward('package.json') to locate the nearest package definition.
// From src/commands/install.ts:27-39
const findPackageJson = Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem;
  const path = yield* Path.Path;
  
  const pkgPath = yield* findUpward('package.json').pipe(Effect.option);
  if (pkgPath._tag === 'None') {
    return null;
  }
  
  const content = yield* fs.readFileString(pkgPath.value);
  const pkg = yield* Schema.decode(Schema.parseJson(PackageJson))(content);
  return { dir: path.dirname(pkgPath.value), name: pkg.name };
});
2

Determine context type

Better PM compares the package.json location with the lock file directory to determine if you’re in:
  • A package directory: When the package.json is in a subdirectory of the lock file location
  • The root directory: When the package.json is at the same level as the lock file
// From src/commands/install.ts:58-66
if (
  lockDirNormalized !== pkgDirNormalized &&
  pkgDirNormalized.startsWith(lockDirNormalized)
) {
  return {
    type: 'package',
    lockDir: pm.lockDir,
    packageName: pkgResult.name,
  } as MonorepoContext;
}
3

Apply appropriate install strategy

Based on the detected context, Better PM either:
  • Auto-scopes the install to the current package and its dependencies
  • Shows a warning and requires confirmation for root-level installs

Auto-Scoping from Package Directories

When you run pm i from inside a workspace package, Better PM automatically filters the install to that package:
cd apps/web
pm i  # Only installs @myapp/web and its workspace dependencies

How Auto-Filtering Works

Better PM builds a filtered install command based on the package manager:
// From src/pm/pnpm.ts:46-47
resolveInstallFilters: (_lockDir: string, packageName: string) =>
  Effect.succeed([`${packageName}...`]),
pnpm uses the ... suffix (e.g., @myapp/web...) which installs the package and all its dependencies.bun and npm resolve the full dependency graph of workspace packages by reading package.json files and following workspace: protocol dependencies.
The install command then executes:
// From src/commands/install.ts:140-149
if (ctx.type === 'package') {
  const filters = yield* pm.resolveInstallFilters(
    ctx.lockDir,
    ctx.packageName,
  );
  const cmd = pm.buildFilteredInstallCommand(filters);
  yield* Console.log(
    `Running ${pm.name} install filtered to ${filters.join(', ')}`
  );
  yield* runShellCommand(cmd);
  return;
}

Root Directory Warnings

When you run pm i at the monorepo root, Better PM protects you from accidentally installing all packages:
# From the root directory
pm i

[WARNING] You are at the monorepo root. This will install ALL packages.

Workspace packages:
├── packages/
   ├── core "@myapp/core"
   └── utils "@myapp/utils"
└── apps/
    └── web "@myapp/web"

To install a specific package:
  pm i -F <package-name>

To install everything:
  pm i -y

Safety Features

Workspace Package List

The warning displays a formatted tree of all workspace packages using the formatWorkspaceTree function, helping you understand the scope of what would be installed.

Confirmation Prompt

In interactive environments, Better PM prompts for confirmation before proceeding with a full install. The prompt can be bypassed with the -y or --sure flag.
// From src/commands/install.ts:153-160
if (ctx.hasWorkspaces && !args.sure) {
  const packages = yield* pm.listWorkspacePackages(ctx.lockDir);
  const proceed = yield* confirmRootInstall({
    packages,
    pathSep: path.sep,
  });
  if (!proceed) return;
}

Filter Flag Usage

You can manually specify which packages to install using the -F or --filter flag:
# Install a single package
pm i -F @myapp/web

# Install multiple packages
pm i -F @myapp/web -F @myapp/api

# Works from anywhere in the monorepo
pm i -F @myapp/core

How Filters Override Auto-Scoping

When filters are provided, they take precedence over context detection:
// From src/commands/install.ts:131-138
if (filters.length > 0) {
  const cmd = pm.buildFilteredInstallCommand(filters);
  yield* Console.log(
    `Running: ${pm.name} install with filters: ${filters.join(', ')}`
  );
  yield* runShellCommand(cmd);
  return;
}
The filter flag is converted to the appropriate package manager syntax:
pnpm -F @myapp/web -F @myapp/api install
Filters bypass the root directory warning, so use them carefully when installing multiple packages from the root.

Context Type Reference

Better PM uses two context types internally:
// From src/commands/install.ts:11-21
type MonorepoContext =
  | {
      type: 'root';
      lockDir: string;
      hasWorkspaces: boolean;
    }
  | {
      type: 'package';
      lockDir: string;
      packageName: string;
    };
  • root: You’re at the monorepo root or in a directory without a package.json
  • package: You’re inside a workspace package directory
This context drives all auto-scoping and warning behavior in Better PM.