Skip to content

fix(packages): route npm installs through pnpm in pnpm projects (#1102) - #1213

Closed
potoior wants to merge 2 commits into
Tencent:mainfrom
potoior:fix/pnpm-package-install-1102
Closed

potoior wants to merge 2 commits into
Tencent:mainfrom
potoior:fix/pnpm-package-install-1102

Conversation

@potoior

@potoior potoior commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Summary

NpmAdapter.install() unconditionally shelled out to npm install, so teamai packages install in a pnpm project ran npm into pnpm's symlinked node_modules/.pnpm store and crashed inside Arborist (Cannot read properties of null (reading 'matches'), exit 1).

This detects a pnpm project and routes the install/snapshot through pnpm, leaving the npm path byte-for-byte unchanged for non-pnpm projects. Fixes #1102.

Detection

A pnpm workspace member usually has its own package.json while the markers live at the root, so detection walks from cwd upward and returns the nearest directory that carries:

  • pnpm-workspace.yaml, or
  • pnpm-lock.yaml, or
  • node_modules/.pnpm, or
  • a package.json#packageManager pin starting with pnpm.

Install / snapshot

  • Install: pnpm add <specs> (run in the member's own cwd), adding --workspace-root when cwd is itself the workspace root — pnpm otherwise rejects a root install with ERR_PNPM_ADDING_TO_ROOT. Global: pnpm add --global.
  • Snapshot: pnpm list --json --depth=0, selecting the cwd project from the workspace array (pnpm prints every workspace project) and merging dependencies + devDependencies + optionalDependencies — not just dependencies. Global: pnpm list --global --json --depth=0.
  • dry-run spawns neither manager.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Test Plan

  • npx tsc --noEmit — clean
  • Repo lint (oxlint --type-aware --deny-warnings) — clean
  • Unit (pkg-adapters, pkg-commands, pkg-manifest, pkg-hint, pkg-register-command, pkg-team-distribution): 52 passed. New NpmAdapter (pnpm) cases cover member-dir detection from the root, the workspace-root flag, global install, and dev/optional snapshotting.
  • Real CLI: npm run test:e2e -- machine-state-993 — 7/7 passed, including the new routes a pnpm project's packages install through pnpm, not npm (#1102) (stub pnpm records its args).
  • Real CLI, real pnpm: a throwaway git team + pnpm workspace, teamai init/pull then teamai packages install:
$ teamai packages install
Done in 1.1s using pnpm v11.9.0
✔ TeamAI packages installed; wrote teamai.lock
# package.json dependencies: {"left-pad": "1.3.0"}
# node_modules/.pnpm present; node_modules/left-pad/package.json present

Related Issues

Closes #1102. Supersedes the closed #1105 / #1116. Addresses their P1 findings: workspace-root install (--workspace-root), pnpm detection in workspace ancestors, and snapshot coverage of dev/optional dependencies.

…ent#1102)

`NpmAdapter.install()` always shelled out to `npm install`, so in a pnpm
project npm's Arborist walked pnpm's symlinked `node_modules/.pnpm` store and
crashed (`Cannot read properties of null (reading 'matches')`).

Detect a pnpm project by walking from cwd upward for `pnpm-workspace.yaml`,
`pnpm-lock.yaml`, `node_modules/.pnpm` or a `packageManager` pin — a workspace
member keeps its `package.json` while those markers sit at the root — and:

- install with `pnpm add` (a workspace member's own cwd; `--workspace-root`
  when cwd is the workspace root, which pnpm otherwise rejects), global with
  `pnpm add -g`;
- snapshot from `pnpm list --json --depth=0`, selecting the cwd project from
  the workspace array and merging dependencies, devDependencies and
  optionalDependencies instead of reading only package-lock.json.

The npm path is unchanged for non-pnpm projects.
@github-actions

Copy link
Copy Markdown
  • [P2 non-blocking] Respect an explicit npm pin before searching ancestors — src/pkg/adapters/npm.ts:80: An independent npm project nested beneath a pnpm repository, with its own packageManager: "npm@..." and package-lock.json, is still classified as pnpm because detection ignores the npm pin and continues upward. Its install now runs pnpm add, and snapshots ignore its npm lockfile. Stop ancestor detection when the project explicitly selects another manager.

The PR description documents sufficient testing, including representative real-CLI verification with real pnpm. No testing-record finding. I only read the specified diff; I did not run, build, or install PR code.

@SaulMoro

Copy link
Copy Markdown
Collaborator

@potoior Codex flagged respecting an explicit npm pin before ancestor detection. Mind taking a look?

…t#1102)

A nested npm project (its own `packageManager: "npm@..."` and
package-lock.json) inside a pnpm repository was still classified as pnpm,
because detection ignored the npm pin and kept walking up to the pnpm root —
so its install ran `pnpm add` and its snapshot skipped package-lock.json.

Stop walking when a directory pins a manager explicitly: pnpm keeps it, any
other manager ends the search as npm.
@potoior

potoior commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

@potoior addressed in e57dc52, pushed. Detection now stops walking when a
directory pins a manager explicitly: packageManager: pnpm* keeps it as the
pnpm project, any other manager (a nested npm@… project inside a pnpm repo)
ends the search and stays on the npm path — its install runs npm install and
its snapshot reads package-lock.json again.

Added a unit test for the nested npm-in-pnpm case (21 pkg-adapters tests pass);
tsc + lint clean; rebuilt and re-ran the real-CLI machine-state-993 e2e, 7/7 pass.

@github-actions

Copy link
Copy Markdown

No new findings in the specified diff.

  • Resolved: The earlier explicit-npm-pin finding is fixed: ancestor detection stops at a non-pnpm packageManager pin, with a regression test covering the nested npm project.
  • Testing record sufficient: The PR description documents representative real-CLI verification with real pnpm; no testing-record finding.

I only read the specified diff. I did not run, build, or install PR code.

@SaulMoro

Copy link
Copy Markdown
Collaborator

@potoior This now has merge conflicts with main. Mind rebasing?

@potoior

potoior commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

@SaulMoro this is now redundant rather than conflicted: #1102 is closed by the merged #1211,
which implements the same fix (ancestor pnpm detection, pnpm add, --workspace-root at the
workspace root, explicit packageManager pin winning, and a pnpm list snapshot). Rebasing
would only duplicate what already landed, so I'll close this as superseded by #1211.

Thanks for the reviews along the way.

@SaulMoro

Copy link
Copy Markdown
Collaborator

@potoior Correction to my earlier comment: #1211 landed the same fix for #1102 on main this morning (pnpm detection, --workspace-root, explicit npm pin, merged dev/optional snapshot), so there is nothing left to rebase onto. Closing as superseded. Thanks for the work here, and sorry for the noise.

@potoior potoior closed this Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] teamai packages install crashes in pnpm projects (npm install run in pnpm node_modules tree)

3 participants