* chore: reduce noisy warnings in test output
- Suppress ExperimentalWarning and DEP0169 via --disable-warning in NODE_OPTIONS
- Fix MaxListenersExceededWarning by raising limit in StoreIndex when adding exit listeners
- Update meta-updater to generate the new _test scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: stop streaming pnpm subprocess output during CLI tests
Buffer stdout/stderr from execPnpm instead of writing to the parent
process in real time. Output is still included in the error message on
failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: pipe all subprocess output in CLI tests
Use stdio: 'pipe' for all pnpm/pnpx spawn helpers so subprocess output
is buffered instead of printed. Output is still included in error
messages on failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove duplicate @pnpm/installing.env-installer in pnpm/package.json
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: use pipe stdio in dlx and errorHandler tests
Replace stdio: 'inherit' and [null, 'pipe', 'inherit'] with 'pipe' to
prevent subprocess output from leaking into test output.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: skip maxListeners adjustment when set to unlimited (0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: merge @pnpm/fs.find-packages into @pnpm/workspace.projects-reader
The find-packages package had only one production consumer (projects-reader).
Inlining it removes a separate published package with minimal value as a
standalone utility.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: sort devDependencies and add tsconfig reference for meta-updater
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove circular tsconfig reference
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: exclude circular tsconfig reference in meta-updater
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: move getConfig from cli/utils to pnpm CLI package
getConfig and installConfigDepsAndLoadHooks were the only functions in
cli/utils that pulled in heavy deps (env-installer, store.connection-manager,
hooks.pnpmfile, default-reporter). Moving them to the pnpm CLI package
(their only consumer) dramatically reduces the dependency weight of
cli/utils, breaking the circular tsconfig reference chain that previously
required fs.find-packages to be a separate package.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: sort imports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Commit 6eedf828b removed the ['.'] fallback for workspacePackagePatterns
when pnpm-workspace.yaml has no packages field. This caused findPackages
to default to ['.', '**'], discovering ALL directories with package.json
as workspace projects. This is the same regression that was previously
reverted in 595cd414f (close#10571), reintroduced by #10127.
Projects like cdxgen that use pnpm-workspace.yaml only for settings
(e.g. minimumReleaseAge) without a packages field were broken because
test data directories were picked up as workspace projects.
close#10909
* feat(publish): handle OTP and web-based authentication flows (#10834)
Add OTP handling to `pnpm publish` with support for:
- Classic OTP prompt (manual code entry)
- Web-based authentication flow with QR code display and doneUrl polling
- `npm-auth-type: web` header to signal web auth support to the registry
Extract OTP logic into a dedicated `otp.ts` module with dependency
injection for testability. Consolidate shared context for OIDC and OTP.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
When `wt` is called with a PR number, automatically launch Claude Code
to analyze the PR diff, read review comments, resolve merge conflicts
with the base branch, fix issues, and run tests.
* Initial plan
* chore: add .devcontainer for GitHub Codespaces support
Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>
* docs: remove comments
* fix: chown pnpm home to node user in onCreateCommand to fix EACCES on volume mount
Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>
* fix: consolidate devcontainer setup into updateContentCommand
Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>
* fix: replace corepack with pnpm install script in devcontainer
Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>
* chore: remove error-prone `PNPM_VERSION` expression
* fix: add pnpm to PATH via remoteEnv in devcontainer
Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>
* fix: removing copilot's bullshit
* chore(git): revert erroneous change
I don't understand why it didn't work
This reverts commit eb01873dbd.
* chore(git): revert erroneous change
I don't understand why it didn't work
This reverts commit 4ebbb6467d.
* chore(git): revert erroneous change
I don't understand why it didn't work
This reverts commit 468848e4da.
* chore(git): revert erroneous change
I don't understand why it didn't work
This reverts commit c4f9e64402.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: KSXGitHub <11488886+KSXGitHub@users.noreply.github.com>
Co-authored-by: Khải <hvksmr1996@gmail.com>
The targeted fetch refspec doesn't create a remote-tracking branch,
causing `git branch --set-upstream-to` to fail. Use `git config` to
set branch.remote and branch.merge directly instead.
* refactor: rename workspace.sort-packages and workspace.pkgs-graph
- workspace.sort-packages -> workspace.projects-sorter
- workspace.pkgs-graph -> workspace.projects-graph
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: rename packages/ to core/ and pkg-manifest.read-package-json to reader
- Rename packages/ directory to core/ for clarity
- Rename pkg-manifest/read-package-json to pkg-manifest/reader (@pnpm/pkg-manifest.reader)
- Update all tsconfig, package.json, and lockfile references
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: consolidate runtime resolvers under engine/runtime domain
- Remove unused @pnpm/engine.runtime.node.fetcher package
- Rename engine/runtime/node.resolver to node-resolver (dash convention)
- Move resolving/bun-resolver to engine/runtime/bun-resolver
- Move resolving/deno-resolver to engine/runtime/deno-resolver
- Update all package names, tsconfig paths, and lockfile references
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update lockfile after removing node.fetcher
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: sort tsconfig references and package.json deps alphabetically
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: auto-fix import sorting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update __typings__ paths in tsconfig.lint.json for moved resolvers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove deno-resolver from deps of bun-resolver
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(core): clarify non-interactive modules purge guidance
Add confirmModulesPurge=false workaround to the no-TTY error hint and
add a regression test for the non-interactive purge confirmation flow.
Refs #6778
* test(core): assert non-tty purge hint on error hint field
---------
Co-authored-by: zubeyralmaho <zubeyralmaho@users.noreply.github.com>
existsSync() follows symlinks and returns false for broken symlinks,
so a dangling symlink from a previous node install would not be removed
before creating the new symlink, causing an EEXIST error.
Use rimraf unconditionally instead, which handles both existing files
and non-existent paths gracefully.
* feat: add virtualStoreOnly option to skip post-import linking
Adds a new `virtualStoreOnly` config option that populates the virtual
store (standard or GVS) without creating importer symlinks, hoisting,
bin links, or running lifecycle scripts.
- Config: add virtual-store-only to types, Config interface, defaults
- extendInstallOptions: validate against enableModulesDir=false, force
ignoreScripts=true and empty hoist patterns when enabled
- headless: add skipPostImportLinking flag guarding 7 post-import steps
- core install: guard buildModules, bin linking, and lifecycle hooks
- link.ts: skip hoisting and symlink creation
- fetch command: use virtualStoreOnly internally
- CLI: wire through rcOptionsTypes and installDeps Pick type
Closes#10840
* fix: address virtualStoreOnly review comments
- Remove ignoreScripts=true forcing (allow builds with virtualStoreOnly)
- Allow virtualStoreOnly + enableModulesDir=false when GVS is enabled
- Guard linkHoistedModules with skipPostImportLinking in hoisted branch
- Un-guard buildModules so lifecycle scripts can run with virtualStoreOnly
- Split metadata block so writeModulesManifest persists with virtualStoreOnly
- Add enableModulesDir=true to pnpm fetch to avoid config conflict
- Fix test bugs: dep version 100.0.0→100.1.0, globalVirtualStoreDir→virtualStoreDir
- Add test for virtualStoreOnly + enableModulesDir=false + GVS
- Relax headless import guard to allow GVS with enableModulesDir=false
* fix: pin dep-of-pkg-with-1-dep dist-tag in virtualStoreOnly tests
The tests hardcode dep-of-pkg-with-1-dep@100.1.0 in path assertions
but didn't call addDistTag to pin the latest version. Since test files
run concurrently, other tests can change the dist-tag to 100.0.0,
causing resolution to pick a different version and the path check to
fail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add package.json to linkExePlatformBinary test fixtures
On Windows, linkExePlatformBinary reads @pnpm/exe/package.json to
rewrite bin.pnpm from "pnpm" to "pnpm.exe". The tests didn't create
this file, causing ENOENT on Windows. Non-Windows platforms skip this
code path so the tests passed elsewhere.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: update lockfile
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When running `pnpm self-update` with `@pnpm/exe`, `linkExePlatformBinary`
looked for the platform binary (e.g. `@pnpm/macos-arm64`) at the top level
of `node_modules`. In pnpm's symlinked layout, the platform package is not
hoisted — it only exists as a sibling of `@pnpm/exe` in the virtual store.
The fix resolves through the `@pnpm/exe` symlink to find the platform
binary as a sibling, which works for both symlinked and flat layouts.
Previously, config dependencies could only be installed via `pnpm add --config`.
If a user manually added config deps to pnpm-workspace.yaml or deleted
pnpm-lock.env.yaml, `pnpm install` would fail because the resolution step
(fetching integrity from the registry) was missing.
Add `resolveAndInstallConfigDeps` which checks the env lockfile for missing
entries and resolves them from the npm registry before installing. This enables
two new workflows:
1. Manually adding config deps to pnpm-workspace.yaml
2. Deleting pnpm-lock.env.yaml and having `pnpm install` recreate it
Instead of a separate pnpm-lock.env.yaml file, the env lockfile
(configDependencies and packageManagerDependencies) is now stored as
the first YAML document in pnpm-lock.yaml, separated by `---`.
The combined file starts with `---\n` when an env document is present,
allowing pnpm to check just the first 4 bytes to know whether
the file contains an env document. Reading uses streaming I/O that
stops as soon as the document separator is found, avoiding parsing
of the full lockfile.
Writing preserves both documents: when the env lockfile is updated
the main lockfile portion is kept, and vice versa.
When devEngines.runtime (or engines.runtime) specifies a Node.js version
with onFail: "download", use that version for engine strictness checks
instead of the system Node.js version.
close#10033
* ci: run Linux/Node 24 tests first, then the rest of the matrix
Run tests on ubuntu-latest / Node.js 24 as a smoke test first.
The remaining 5 matrix combinations only start if it passes,
saving CI resources on failing PRs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(ci): extract test steps into reusable workflow
Reduces duplication by moving all test steps into test.yml as a
reusable workflow. ci.yml now calls it twice: once for the smoke
test (Linux/Node 24) and once for the remaining matrix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(ci): remove redundant if conditions from dependent jobs
The if condition only needs to be on compile-and-lint. Downstream
jobs are automatically skipped when their needs are skipped.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor(ci): clean up check names for reusable workflow
Drop redundant "Test" prefix from caller job names since the
reusable workflow job key "test" is automatically appended by
GitHub, e.g. "CI / ubuntu-latest / Node.js 24 / test".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* style(ci): capitalize Test in reusable workflow job name
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: correctly identify workspace packages in all operations
* fix: use Set for workspacePackages lookup and add uninstallSome test
Use Set<string> instead of string[] for workspacePackages in
dedupeInjectedDeps for O(1) lookups. Add test covering pnpm rm
(uninstallSome) to directly reproduce the scenario from #9518.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: rename workspacePackages to workspaceProjectIds and add defensive guard
Address Copilot review comments:
- Rename `workspacePackages` to `workspaceProjectIds` to avoid confusion
with the `WorkspacePackages` type used elsewhere in the codebase.
- Add a defensive guard in `getDedupeMap` to skip deps whose target
project is not in `dependenciesByProjectId`, preventing a potential
runtime error if the function is called with a partial project set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use allProjectIds from ctx.projects instead of wantedLockfile.importers
wantedLockfile.importers may not always contain all workspace projects
(e.g. when pruneLockfileImporters is true during subset operations).
Pass allProjectIds explicitly from ctx.projects, which is the
authoritative source for all workspace project IDs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make allProjectIds a required field
All callers of resolveDependencies now explicitly pass allProjectIds
rather than falling back to wantedLockfile.importers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: rewrite test to actually reproduce the bug from #9518
The previous tests used mutateModules with all projects in allProjects,
which caused installInContext to expand the projects list — hiding the
bug. The real bug occurs when mutateModulesInSingleProject is used
(as pnpm rm does from a single package directory), where allProjects
contains only the operated-on package.
The new test uses mutateModulesInSingleProject for the removal step,
matching the actual pnpm rm code path. It correctly fails without the
fix (receives "file:b" instead of "link:../b") and passes with it.
Also fixes the workspaceProjectIds source to merge both allProjectIds
and wantedLockfile.importers, since in single-project operations
allProjectIds only has one project while the lockfile has all of them.
Refines the defensive guard in getDedupeMap to allow deduplication when
the target project has no children (empty set is always a subset).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make workspaceProjectIds required in resolvePeers
Remove the optional marker and empty-set fallback. All callers now
provide this explicitly, and test call sites pass new Set().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix(outdated): handle NO_MATCHING_VERSION when minimumReleaseAge is set
When minimumReleaseAge filters out all versions of a package (including
the one the "latest" dist-tag points to), the resolver throws
NO_MATCHING_VERSION instead of NO_MATURE_MATCHING_VERSION. The
getManifest function in outdated only caught the latter, causing
`pnpm outdated` to crash.
Now both error codes are caught when publishedBy is set, so outdated
gracefully skips packages with no mature versions.
Fixes#10605
* test(outdated): remove redundant getManifest test
The 'handles NO_MATCHING_VERSION error gracefully' test was misleadingly
named (it actually threw ERR_PNPM_NO_MATURE_MATCHING_VERSION) and
duplicated the existing 'with minimumReleaseAge filters latest when too
new' test. It also passed without the fix, so it wasn't verifying the
actual bug.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Fall back to wanted lockfile when current lockfile is missing so list works
without node_modules (e.g. workspace-with-nested-workspace-deps fixture).
- Pass onlyProjects from CLI opts (cliOptions['only-projects']) into list render.
- In buildDependencyGraph, when onlyProjects is true only add edges to workspace
importers so the graph is restricted to workspace projects.
- Normalize ref from lockfile (support inline { version, specifier } format).
* chore: add script to clean up worktrees with merged PRs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use safe arithmetic to avoid set -e exit on zero increment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
When creating a worktree from a PR number, use `gh pr view` to get the
fork owner/repo and branch name, add the fork as a remote, and set the
upstream tracking branch. This makes `git push` work out of the box for
PRs from forks, matching the behavior of `gh pr checkout`.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: depPath format used in time pruning
The local `refToRelative` helper in `lockfileFormatConverters.ts` produced
dependency paths with a leading slash (e.g. `/foo@1.0.0`), while the keys
stored in the `time` field do not have one (e.g. `foo@1.0.0`).
Because of this mismatch, `rootDepPaths.has(depPath)` always returned false
inside `pruneTimeInLockfile`, so `pickBy` filtered out every entry and the
entire `time` field was cleared on every install.
Fix by replacing the local helper with `refToRelative` from
`@pnpm/dependency-path`, which produces the correct format.
* chore: add pnpm to changeset
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle verifyDepsBeforeRun prompt in non-TTY environments
In non-interactive environments like CI, verifyDepsBeforeRun: 'prompt' would
silently exit with code 0 even when node_modules were out of sync. This could
cause tests to pass when they should fail.
Now, pnpm throws an error in non-TTY environments, alerting users that they
need to run 'pnpm install' first.
Also handles Ctrl+C gracefully during the prompt - exits cleanly without
showing a stack trace.
Fixes#10889Fixes#10888
* fix: improve Ctrl+C handling and fix prompt TTY guard
- Replace brittle ERR_USE_AFTER_CLOSE check with generic catch for prompt
cancellation (enquirer rejects with empty string on Ctrl+C, not that error)
- Fix prompt test to mock isTTY=true since Jest runs in non-TTY environment
- Fix test description "noTTY" → "non-TTY"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: narrow try/catch to only wrap enquirer.prompt
Avoid catching errors from install() which should propagate normally.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: zubeyralmaho <zubeyralmaho@users.noreply.github.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add n/prefer-node-protocol rule and autofix all bare builtin imports
to use the node: prefix. Simplify the simple-import-sort builtins
pattern to just ^node: since all imports now use the prefix.
Add eslint-plugin-simple-import-sort to enforce consistent import ordering:
- Node.js builtins first
- External packages second
- Relative imports last
- Named imports sorted alphabetically within each statement
Both `pnpm` and `@pnpm/exe` packages legitimately own the `pnpm` and
`pnpx` bins. Previously, installing one while the other was already
globally installed would silently skip the bin or allow an override.
Now both are recognized as owners via BIN_OWNER_OVERRIDES, and when
both the new and existing packages own a bin, a GLOBAL_BIN_CONFLICT
error is raised instead of silently proceeding.
sbom always outputs JSON to stdout, but the pnpm log reporter could
write warnings (e.g. engine mismatch) to stdout before the JSON,
breaking parsers and piping to files.
Refs: #10592close#10923
* feat(audit): add fix update mode
Add the ability to fix vulnerabilities by updating packages in the
lockfile instead of adding overrides.
* revert: remove audit-registry parameter
* fix: properly invoke audit command recursively on workspace
* fix: negative weight version priority & top-level pinned dep updating
* refactor: apply packageVulnerabilityAudit version preferences earlier
* chore: update changeset
* fix: vulnerability penalties are greater than direct dep weight
* test: use nock on mock registry directly
* fix: exit with 1 if it can't resolve all vulnerabilities to match npm
* fix: properly update workspace top-level pinned vulnerable dependencies
* fix: update lockfile
* fix: update vulnerabilities in catalogs
* chore: sync pnpm-lock.yaml with main