Skip to content

feat(cli): implement prefix command#12728

Merged
zkochan merged 5 commits into
pnpm:mainfrom
kairosci:feat/prefix-command
Jul 2, 2026
Merged

feat(cli): implement prefix command#12728
zkochan merged 5 commits into
pnpm:mainfrom
kairosci:feat/prefix-command

Conversation

@kairosci

@kairosci kairosci commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR implements the prefix command in both the TypeScript pnpm CLI and the Rust pacquet port. The command prints the path to the current package prefix (the nearest parent directory containing package.json, node_modules, package.json5, package.yaml, or pnpm-workspace.yaml), or the global prefix directory if global mode is active.

Squash Commit Body

feat(cli): implement prefix command in TypeScript and Rust

Adds the `prefix` command that prints the local prefix path (the nearest
ancestor directory that contains a package.json, node_modules,
pnpm-workspace.yaml, package.json5, or package.yaml). With --global it
prints the global prefix.

TypeScript: new cmd/prefix.ts module, registered in cmd/index.ts and
removed from notImplemented.ts. Full integration test suite in
test/prefix.ts.

Rust (pacquet): PrefixArgs struct and find_local_prefix walk-up in
cli_args/prefix.rs; subcommand wired through cli_command.rs,
dispatch.rs, and dispatch_query.rs. Integration tests in
crates/cli/tests/prefix.rs.

Checklist

The prefix feature is implemented in both the TypeScript CLI and the Rust pacquet port.
A changeset has been added for the minor version bump of pnpm.
Comprehensive unit and integration tests have been added and run successfully.

Summary by CodeRabbit

  • New Features
    • Added a prefix command to the pacquet CLI that prints the resolved local prefix directory.
    • Local resolution walks upward to find the nearest workspace/package root (including from nested directories or node_modules).
    • Added a pnpm prefix command; pnpm prefix -g/--global outputs the global prefix directory.
    • pacquet prefix does not support --global.
  • Bug Fixes
    • Updated prefix output routing and tightened permission handling for the global bin directory.
  • Tests
    • Added integration tests for pacquet prefix (including --global failure) and Jest coverage for pnpm prefix (local + global).

@kairosci
kairosci requested a review from zkochan as a code owner June 29, 2026 15:29
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds the prefix command to pnpm and pacquet, with command registration, prefix lookup, -g handling, dispatcher wiring, tests, and release metadata.

Changes

Prefix command implementation

Layer / File(s) Summary
pnpm command and handler
pnpm11/pnpm/src/cmd/prefix.ts, pnpm11/pnpm/src/cmd/index.ts, pnpm11/pnpm/src/cmd/notImplemented.ts, pnpm11/pnpm/src/main.ts, .changeset/implement-prefix-command.md
Adds the prefix command module, registers it in the command list, removes it from the not-implemented set, updates stderr reporter selection, broadens global write permissions, and records the release note entry.
pnpm prefix tests
pnpm11/pnpm/test/prefix.ts
Adds Jest coverage for local prefix resolution, nested directory behavior, and -g output.
pacquet CLI wiring
pacquet/crates/cli/src/cli_args.rs, pacquet/crates/cli/src/cli_args/cli_command.rs, pacquet/crates/cli/src/cli_args/dispatch.rs, pacquet/crates/cli/src/cli_args/dispatch_query.rs
Adds the prefix subcommand to the CLI module tree, command enum, dispatch routing, and query dispatch entry point.
pacquet prefix resolution
pacquet/crates/cli/src/cli_args/prefix.rs
Defines PrefixArgs, PrefixError, local prefix walk-up lookup, filesystem probing, and command execution for the new subcommand.
pacquet prefix tests
pacquet/crates/cli/tests/prefix.rs
Adds integration tests for local lookup, nested directory traversal, node_modules traversal, -g rejection, and workspace member resolution.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • zkochan

Poem

🐇 I hopped to the prefix, neat and true,
past node_modules, and up I flew.
-g said “nope,” so I nibbled a grin,
then printed the path where the roots begin.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately names the main change: implementing the prefix command.
Description check ✅ Passed The description includes the required summary, squash commit body, and checklist coverage for TypeScript, Rust, changeset, and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Implement prefix command in pnpm (TS) and pacquet (Rust)

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add pnpm prefix command with -g/--global support and help text.
• Port prefix to pacquet with a filesystem walk-up local-prefix resolver.
• Add integration tests for both CLIs and ship a minor-version changeset.
Diagram

graph TD
  U([User]) --> TS["pnpm CLI (TS)"] --> TSP["cmd/prefix.ts"] --> OUT["Print prefix"]
  U([User]) --> RS["pacquet CLI (Rust)"] --> RSP["cli_args/prefix.rs"] --> FS[(Filesystem)] --> OUT["Print prefix"]

  subgraph Legend
    direction LR
    _usr([User]) ~~~ _mod["CLI/module"] ~~~ _db[(Filesystem)]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Factor pacquet prefix/root into shared “global gating + print path” helper
  • ➕ Reduces duplicated --global not supported yet error plumbing across similar commands
  • ➕ Keeps CLI behavior consistent (diagnostic codes, messaging, exit semantics)
  • ➖ May introduce an abstraction layer that is overkill until more path-query commands land
  • ➖ Refactor risk (touches stable command wiring) for limited immediate gain
2. Use a reusable prefix-locator utility module in pacquet (instead of local function)
  • ➕ Allows other commands to reuse the same prefix-discovery semantics (workspace detection, future --dir behaviors)
  • ➕ Improves testability by isolating filesystem-walk logic
  • ➖ Slightly more upfront structure for a single command today
  • ➖ Needs careful API design to avoid leaking CLI concerns into utility code

Recommendation: The PR’s approach is solid: pnpm’s TS command prints the already-resolved dir and adds a robust global-prefix fallback chain, while pacquet correctly ports local prefix discovery and explicitly fails --global to avoid incorrect output. Consider a small follow-up refactor in pacquet to deduplicate the repeated “global unsupported” pattern shared with root and to centralize prefix-discovery utilities as more commands need similar path semantics.

Files changed (11) +329 / -1

Enhancement (8) +168 / -1
cli_args.rsExpose 'prefix' CLI args module +1/-0

Expose 'prefix' CLI args module

• Registers the new 'prefix' module in the pacquet CLI args module list so it can be referenced by the command enum.

pacquet/crates/cli/src/cli_args.rs

cli_command.rsAdd 'Prefix' subcommand to pacquet CLI +3/-0

Add 'Prefix' subcommand to pacquet CLI

• Imports 'PrefixArgs' and adds a 'Prefix' variant to the 'CliCommand' enum with help text. Enables clap to parse 'pacquet prefix' arguments.

pacquet/crates/cli/src/cli_args/cli_command.rs

dispatch.rsRoute 'prefix' subcommand to query dispatcher +1/-0

Route 'prefix' subcommand to query dispatcher

• Extends the dispatch router to map 'CliCommand::Prefix' to 'dispatch_query::prefix', making the command runnable end-to-end.

pacquet/crates/cli/src/cli_args/dispatch.rs

dispatch_query.rsImplement 'dispatch_query::prefix' entrypoint +6/-0

Implement 'dispatch_query::prefix' entrypoint

• Adds a synchronous query dispatcher function that calls 'PrefixArgs::run' and returns a ready future. Mirrors other read-only query commands like 'root'.

pacquet/crates/cli/src/cli_args/dispatch_query.rs

prefix.rsImplement pacquet 'prefix' command and local prefix discovery +85/-0

Implement pacquet 'prefix' command and local prefix discovery

• Adds 'PrefixArgs' with '-g/--global' flag, a diagnostic error for unsupported global mode, and a 'find_local_prefix' walk-up algorithm that looks for common prefix markers. Prints the resolved local prefix directory to stdout.

pacquet/crates/cli/src/cli_args/prefix.rs

index.tsRegister 'prefix' command in pnpm command list +2/-0

Register 'prefix' command in pnpm command list

• Imports the new 'cmd/prefix' module and adds it to the command registry so 'pnpm prefix' is recognized and dispatched.

pnpm11/pnpm/src/cmd/index.ts

notImplemented.tsRemove 'prefix' from not-implemented commands +0/-1

Remove 'prefix' from not-implemented commands

• Drops 'prefix' from the 'NOT_IMPLEMENTED_COMMANDS' list now that it has a real implementation.

pnpm11/pnpm/src/cmd/notImplemented.ts

prefix.tsImplement 'pnpm prefix' handler and help +70/-0

Implement 'pnpm prefix' handler and help

• Adds a new command module defining CLI/rc options, help text, and a handler. Local mode returns 'opts.dir'; global mode returns 'globalPrefix'/'prefix' when provided, otherwise derives a prefix from 'PREFIX'/platform-specific defaults.

pnpm11/pnpm/src/cmd/prefix.ts

Tests (2) +156 / -0
prefix.rsAdd pacquet integration tests for 'prefix' +114/-0

Add pacquet integration tests for 'prefix'

• Adds tests asserting local prefix output, walk-up behavior from nested directories, and explicit failure for '-g' until global support is ported. Includes a parity test comparing pacquet output to 'pnpm prefix' (ignored on Windows due to shim resolution).

pacquet/crates/cli/tests/prefix.rs

prefix.tsAdd pnpm integration tests for 'prefix' +42/-0

Add pnpm integration tests for 'prefix'

• Adds Jest tests validating local prefix output in a project root and from a subdirectory. Adds a smoke test ensuring 'pnpm prefix -g' returns a non-empty value.

pnpm11/pnpm/test/prefix.ts

Other (1) +5 / -0
implement-prefix-command.mdAdd minor-version changeset for new 'prefix' command +5/-0

Add minor-version changeset for new 'prefix' command

• Introduces a changeset marking a minor bump for pnpm. Documents the new 'prefix' command behavior, including '--global' support.

.changeset/implement-prefix-command.md

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. issues alias overridden 🐞 Bug ≡ Correctness
Description
The PR adds issues as an alias of bugs, but pnpm still registers an issues not-implemented
stub afterwards, overwriting the alias handler. As a result, pnpm issues will throw
NOT_IMPLEMENTED instead of opening the bug tracker URL.
Code

pnpm11/deps/inspection/commands/src/bugs/index.ts[19]

+export const commandNames = ['bugs', 'issues']
Evidence
bugs now declares issues as a command name, but pnpm appends notImplementedCommandDefinitions
after bugs in its command list and overwrites handlers by command name in insertion order; since
issues is still listed as not implemented, the stub replaces the alias.

pnpm11/deps/inspection/commands/src/bugs/index.ts[15-26]
pnpm11/pnpm/src/cmd/index.ts[125-238]
pnpm11/pnpm/src/cmd/notImplemented.ts[5-14]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`issues` was added as an alias for the `bugs` command, but pnpm’s command registry later registers a not-implemented `issues` command which overwrites the alias handler.
### Issue Context
The pnpm CLI builds `handlerByCommandName` by iterating through `commands` in order; later entries win. Since `...notImplementedCommandDefinitions` is appended after real commands, the `issues` stub overrides the alias.
### Fix Focus Areas
- pnpm11/pnpm/src/cmd/notImplemented.ts[5-14]
- pnpm11/pnpm/src/cmd/index.ts[125-204]
- pnpm11/deps/inspection/commands/src/bugs/index.ts[15-26]
### Expected fix
- Remove `'issues'` from `NOT_IMPLEMENTED_COMMANDS` (preferred, since the alias is now implemented via `bugs`).
- (Optional) Add/adjust a pnpm integration test ensuring `pnpm issues` routes to the `bugs` handler (opens URL / at least reaches same handler).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Prefix -g requires write access ✓ Resolved 🐞 Bug ☼ Reliability
Description
pnpm prefix -g triggers global-mode config initialization which may throw PNPM_DIR_NOT_WRITABLE
because main.ts only disables the global-bin writability check for root. This makes the new
read-only prefix command fail in environments where the global bin dir is intentionally not
writable.
Code

pnpm11/pnpm/src/cmd/prefix.ts[R46-48]

+  if (opts.global) {
+    return opts.globalPkgDir ? path.dirname(opts.globalPkgDir) : ''
+  }
Evidence
The new prefix command supports --global, which makes getConfig() enter the global branch and
call checkGlobalBinDir() with shouldAllowWrite set from main.ts. Because main.ts only
disables that write-check for root, prefix -g can incorrectly fail with PNPM_DIR_NOT_WRITABLE
despite being read-only.

pnpm11/pnpm/src/cmd/prefix.ts[39-49]
pnpm11/pnpm/src/main.ts[100-114]
pnpm11/config/reader/src/index.ts[383-391]
pnpm11/config/reader/src/checkGlobalBinDir.ts[9-25]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pnpm prefix -g` is a read-only command but it currently runs through the same global-mode config path that can enforce global bin directory writability. This can cause `pnpm prefix -g` to fail with `PNPM_DIR_NOT_WRITABLE` even though it only needs to print the global prefix.
### Issue Context
- `main.ts` computes `globalDirShouldAllowWrite` as `cmd !== 'root'` and passes it into `getConfig()`.
- The config reader calls `checkGlobalBinDir(... shouldAllowWrite: opts.globalDirShouldAllowWrite)` when `--global` is set.
- `checkGlobalBinDir()` throws if `shouldAllowWrite` is true and the global bin dir is not writable.
### Fix Focus Areas
- pnpm11/pnpm/src/main.ts[100-114]
Suggested change:
- Treat `prefix` like `root` for this flag (or more precisely: when `cmd === 'prefix'` and `cliOptions.global === true`, pass `globalDirShouldAllowWrite: false`). This keeps global install commands protected while allowing `pnpm prefix -g` to remain a reliable query command.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Wrong global prefix path ✓ Resolved 🐞 Bug ⛨ Security
Description
pnpm prefix -g returns opts.prefix / $PREFIX / execPath-derived values, which can diverge
from pnpm’s actual global install root (derived from pnpmHomeDir/global or globalDir). Because
project/workspace .npmrc is merged into config, this also allows repo-controlled config to
influence the printed “global” prefix.
Code

pnpm11/pnpm/src/cmd/prefix.ts[R47-67]

+  if (opts.global) {
+    if (opts.globalPrefix) {
+      return opts.globalPrefix
+    }
+    if (opts.prefix) {
+      return opts.prefix
+    }
+    let prefix = process.env.PREFIX
+    if (!prefix) {
+      if (process.platform === 'win32') {
+        prefix = process.env.APPDATA ? path.join(process.env.APPDATA, 'npm') : path.dirname(process.execPath)
+      } else {
+        const binDir = path.dirname(process.execPath)
+        if (path.basename(binDir) === 'bin') {
+          prefix = path.dirname(binDir)
+        } else {
+          prefix = binDir
+        }
+      }
+    }
+    return prefix
Evidence
The new handler explicitly prefers opts.prefix/env/execPath for --global, while pnpm’s config
reader derives the global install location from pnpmHomeDir/globalDir into globalPkgDir (whose
parent is the global root). Also, workspace .npmrc is merged into config, so prefix can come
from repo-controlled config.

pnpm11/pnpm/src/cmd/prefix.ts[39-70]
pnpm11/config/reader/src/index.ts[376-388]
pnpm11/config/reader/src/loadNpmrcFiles.ts[84-163]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pnpm prefix --global` currently falls back to `opts.prefix` / environment / `process.execPath` heuristics. This does not match pnpm’s real global install root (the parent of `globalPkgDir`) and also makes the output influenceable by project `.npmrc` configuration, despite `--global`.
### Issue Context
pnpm’s config reader computes `globalPkgDir` from `globalDirRoot`, and `globalDirRoot` is what should be considered the global prefix/root for pnpm global installs.
### Fix Focus Areas
- pnpm11/pnpm/src/cmd/prefix.ts[39-70]
- pnpm11/config/reader/src/index.ts[376-388]
### Suggested fix
- In the `opts.global` branch, return `path.dirname(opts.globalPkgDir)` (or an equivalent derivation), and remove/avoid using `opts.prefix` / `$PREFIX` / `execPath` fallbacks.
- Extend the handler opts type to include `globalPkgDir: string` (already present on pnpm Config) so this is type-safe.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Prefix triggers config hooks 🐞 Bug ⛨ Security
Description
pnpm prefix currently runs through installConfigDepsAndLoadHooks() even though the handler only
prints a computed directory path, so it may install configDependencies and load pnpmfile hooks
first. This makes pnpm prefix unexpectedly side-effectful (possible network access / hook
execution) and can cause the command to fail if configDependencies cannot be fetched, despite not
needing them to print a prefix.
Code

pnpm11/pnpm/src/main.ts[103]

+    const globalDirShouldAllowWrite = cmd !== 'root' && cmd !== 'prefix'
Evidence
The new prefix command is now part of the normal command flow, which always runs
configDependencies installation + hook loading; repo-controlled workspace manifest settings can
supply configDependencies, so the new command inherits those side effects even though it doesn’t
need them to print the prefix.

pnpm11/pnpm/src/main.ts[100-153]
pnpm11/pnpm/src/getConfig.ts[45-97]
pnpm11/config/reader/src/index.ts[458-469]
pnpm11/config/reader/src/index.ts[1040-1060]
pnpm11/config/reader/src/getOptionsFromRootManifest.ts[16-28]
pnpm11/pnpm/src/cmd/prefix.ts[40-54]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pnpm prefix` is a read-only “print a path” command, but it still executes the generic pnpm startup pipeline that installs `configDependencies` and loads pnpmfile hooks. This introduces unexpected side effects (including potential network fetches and execution of hook code) and can also make `pnpm prefix` fail when configDependencies cannot be installed.
## Issue Context
- `main.ts` always calls `installConfigDepsAndLoadHooks()` before dispatching the command.
- `installConfigDepsAndLoadHooks()` may install `configDependencies` and then calls `requireHooks()`.
- Repo-controlled `pnpm-workspace.yaml` settings are applied into config (including `configDependencies`).
## Fix Focus Areas
- pnpm11/pnpm/src/main.ts[100-153]
- pnpm11/pnpm/src/getConfig.ts[45-97]
- pnpm11/config/reader/src/index.ts[458-469]
- pnpm11/config/reader/src/index.ts[1040-1060]
### Suggested fix direction
Add a small command-level gate in `main.ts` so that for `cmd === 'prefix'` (and potentially other pure-query commands), pnpm does **not** call `installConfigDepsAndLoadHooks()` (or calls a variant that skips both configDependencies installation and hook loading). This keeps `pnpm prefix` fast and avoids unexpected side effects for a command that only prints a path.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Prefix panics on broken pipe 🐞 Bug ☼ Reliability
Description
pacquet prefix prints via println!, which can panic if stdout write fails (notably when piped to
a consumer that exits early, causing EPIPE). This makes the new command brittle in shell pipelines
and can turn a normal early-termination into a crash.
Code

pacquet/crates/cli/src/cli_args/prefix.rs[R86-88]

+        let prefix_dir = find_local_prefix(dir)?;
+        println!("{}", prefix_dir.display());
+        Ok(())
Evidence
The new pacquet prefix command prints using println! (panic-prone on stdout write failure),
while pnpm explicitly prevents crashes when pipes close early, and other pacquet code uses explicit
stdout writes that avoid panicking on write errors.

pacquet/crates/cli/src/cli_args/prefix.rs[81-89]
pnpm11/pnpm/src/main.ts[52-59]
pacquet/crates/cli/src/cli_args/cat_index.rs[68-74]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `pacquet prefix` implementation uses `println!` to write the machine-readable prefix path. In Rust, the standard print macros can panic on stdout write errors (e.g. broken pipe when output is piped into `head`), causing the command to crash instead of exiting cleanly.
### Issue Context
pnpm’s TypeScript CLI explicitly guards against EPIPE on stdout to keep machine-readable commands safe in pipelines. pacquet has other call sites that write to stdout using `writeln!`/`flush` and ignore write errors, avoiding panic-on-print.
### Fix Focus Areas
- pacquet/crates/cli/src/cli_args/prefix.rs[81-89]
### Suggested fix approach
Replace `println!("{}", prefix_dir.display())` with an explicit stdout write that does **not** panic on error. One simple pattern consistent with other pacquet code:
- use `let mut stdout = std::io::stdout();`
- `let _ = writeln!(stdout, "{}", prefix_dir.display());`
- optionally `let _ = stdout.flush();`
If you’d prefer stricter handling, detect `BrokenPipe` and return `Ok(())` while still surfacing other IO errors.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Prefix stdout contamination ✓ Resolved 🐞 Bug ☼ Reliability
Description
The new prefix command is meant to emit a single machine-readable path, but it is not included in
COMMANDS_WITH_STDERR_REPORTER, so reporter output (e.g. --force warnings) can go to stdout and
corrupt pnpm prefix output in scripts.
Code

pnpm11/pnpm/src/cmd/prefix.ts[R39-70]

+export async function handler (
+  opts: {
+    dir: string
+    global?: boolean
+    globalPrefix?: string
+    prefix?: string
+  }
+): Promise<string> {
+  if (opts.global) {
+    if (opts.globalPrefix) {
+      return opts.globalPrefix
+    }
+    if (opts.prefix) {
+      return opts.prefix
+    }
+    let prefix = process.env.PREFIX
+    if (!prefix) {
+      if (process.platform === 'win32') {
+        prefix = process.env.APPDATA ? path.join(process.env.APPDATA, 'npm') : path.dirname(process.execPath)
+      } else {
+        const binDir = path.dirname(process.execPath)
+        if (path.basename(binDir) === 'bin') {
+          prefix = path.dirname(binDir)
+        } else {
+          prefix = binDir
+        }
+      }
+    }
+    return prefix
+  }
+  return opts.dir
+}
Evidence
COMMANDS_WITH_STDERR_REPORTER exists specifically to keep stdout machine-readable. --force
triggers a warning via the logger, and the reporter honors config.useStderr; since prefix is not
in the set, those messages can go to stdout alongside the prefix result.

pnpm11/pnpm/src/main.ts[39-45]
pnpm11/pnpm/src/main.ts[344-349]
pnpm11/pnpm/src/reporter/index.ts[18-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pnpm prefix` returns a single path string that users commonly capture in scripts. However, pnpm routes reporter output to stdout by default, and `prefix` is not in the allowlist that forces reporter output to stderr. This can mix warnings/progress into stdout and break consumers.
### Issue Context
The code comment in `main.ts` explicitly calls out commands that must keep stdout clean; `prefix` fits that contract.
### Fix Focus Areas
- pnpm11/pnpm/src/main.ts[39-45]
### Suggested fix
- Add `'prefix'` to `COMMANDS_WITH_STDERR_REPORTER`.
- (Optional, if you want consistent behavior) consider whether `'root'` should also be in this set, but keep the PR scope focused if desired.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
7. Prefix masks IO errors ✓ Resolved 🐞 Bug ☼ Reliability
Description
pacquet prefix checks for marker files via Path::exists(), which returns false on I/O errors
(e.g. EACCES), so the walk-up can silently skip an unreadable directory and return a different
prefix instead of surfacing the underlying error. This diverges from pnpm’s
findLocalPrefix/findPrefixUp behavior (which uses readdirSync and handles/propagates errors
explicitly) and also incurs extra per-ancestor filesystem stats (minor perf cost).
Code

pacquet/crates/cli/src/cli_args/prefix.rs[R58-64]

+    let targets =
+        ["node_modules", "package.json", "package.json5", "package.yaml", "pnpm-workspace.yaml"];
+    for target in &targets {
+        if name.join(target).exists() {
+            return name.to_path_buf();
+        }
+    }
Evidence
The pacquet implementation uses Path::exists() for marker detection, which collapses permission
and other I/O errors into false and can therefore change the walk-up result without signaling
failure. pnpm’s reference implementation (which pacquet says it is porting) uses readdirSync and a
try/catch with explicit error handling, so pacquet’s current behavior is not semantically
equivalent.

pacquet/crates/cli/src/cli_args/prefix.rs[53-64]
pnpm11/config/reader/src/loadNpmrcFiles.ts[600-653]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pacquet/crates/cli/src/cli_args/prefix.rs` implements `find_prefix_up` by probing `name.join(target).exists()` for several marker files.
In Rust, `Path::exists()` **suppresses all I/O errors** (it returns `false` on errors like permission denied), which can make `pacquet prefix` silently choose a different ancestor than intended when a directory is unreadable.
### Issue Context
This function is explicitly documented as a port of pnpm’s `findLocalPrefix` logic; pnpm’s implementation uses a directory read (`readdirSync`) and explicitly handles errors (throwing for certain cases).
### Fix Focus Areas
- pacquet/crates/cli/src/cli_args/prefix.rs[53-74]
Suggested approach:
- Replace the `exists()` probes with a single `std::fs::read_dir(name)` (or equivalent), building a small set/flags of encountered entry names.
- Match pnpm’s error semantics as closely as possible:
- If the *starting* directory read fails with `NotFound`, return `original`.
- If the *starting* directory read fails with other errors, propagate an error.
- If an ancestor directory read fails, stop and return `original` (or otherwise mirror pnpm’s behavior).
- As a side benefit, this reduces syscalls from 5×`metadata` per ancestor to ~1×`readdir` per ancestor (minor but cleaner and closer to pnpm).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

8. dbg! noise in tests 🐞 Bug ⚙ Maintainability
Description
The new pacquet integration tests call dbg!(&output), which prints to stderr and adds noisy,
non-asserted output to test runs. This makes CI logs harder to read and can bury real failures in
large output streams.
Code

pacquet/crates/cli/tests/prefix.rs[R21-23]

+    let output = pacquet.with_args(["prefix"]).output().expect("run pacquet prefix");
+    dbg!(&output);
+    assert!(output.status.success(), "pacquet prefix should succeed");
Evidence
The cited lines show two dbg!(&output) statements in the newly-added test file; dbg! writes to
stderr and will pollute test output.

pacquet/crates/cli/tests/prefix.rs[15-29]
pacquet/crates/cli/tests/prefix.rs[77-89]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pacquet/crates/cli/tests/prefix.rs` includes `dbg!(&output)` calls that print debug output to stderr during normal test execution.
### Issue Context
These were likely added during development but are not needed for assertions and add noise to CI logs.
### Fix Focus Areas
- Remove the `dbg!(&output);` lines.
- pacquet/crates/cli/tests/prefix.rs[21-23]
- pacquet/crates/cli/tests/prefix.rs[81-83]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Recursive prefix walk ✓ Resolved 🐞 Bug ☼ Reliability
Description
pacquet’s find_prefix_up walks ancestors via recursion, which is avoidable and less robust than
an iterative loop (unnecessary stack growth for deep paths).
Code

pacquet/crates/cli/src/cli_args/prefix.rs[R53-74]

+fn find_prefix_up(name: &Path, original: &Path) -> PathBuf {
+    if name.parent().is_none() {
+        return original.to_path_buf();
+    }
+
+    let targets =
+        ["node_modules", "package.json", "package.json5", "package.yaml", "pnpm-workspace.yaml"];
+    for target in &targets {
+        if name.join(target).exists() {
+            return name.to_path_buf();
+        }
+    }
+
+    if let Some(parent) = name.parent() {
+        if parent == name {
+            return original.to_path_buf();
+        }
+        find_prefix_up(parent, original)
+    } else {
+        original.to_path_buf()
+    }
+}
Evidence
The current implementation clearly recurses on find_prefix_up(parent, original), adding one stack
frame per directory level until root/marker detection returns.

pacquet/crates/cli/src/cli_args/prefix.rs[53-74]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`find_prefix_up` is recursive and calls itself once per parent directory. While depth is usually small, an iterative implementation is more robust and avoids stack growth.
### Issue Context
This is a CLI-only path (not an install hot path), so it’s not urgent, but it’s cheap to harden.
### Fix Focus Areas
- pacquet/crates/cli/src/cli_args/prefix.rs[53-74]
### Suggested fix
- Rewrite `find_prefix_up` as a `loop { ... }` that updates `name = parent` until root/marker found, returning `original` as today on termination conditions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
pacquet/crates/cli/tests/prefix.rs (1)

15-114: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add one regression test for the node_modules walk-up branch.

The implementation has a dedicated node_modules special case, but this suite only exercises the manifest/workspace walk-up paths. A real CLI test that runs from <prefix>/node_modules would keep the most pnpm-specific branch from regressing silently. As per path instructions, REVIEW_GUIDE.md says to “require regression coverage through the real CLI wiring.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pacquet/crates/cli/tests/prefix.rs` around lines 15 - 114, Add a regression
test that exercises the `node_modules` special-case in the real CLI path, since
the current `prefix` tests only cover manifest/workspace walk-up behavior.
Extend the `prefix.rs` suite with a case that runs `pacquet prefix` from inside
a directory under `<prefix>/node_modules` and asserts the resolved prefix
matches the parent package root, using the existing `CommandTempCwd`,
`Command::cargo_bin("pacquet")`, and `prefix` CLI wiring so the `node_modules`
branch cannot regress silently.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pacquet/crates/cli/src/cli_args/prefix.rs`:
- Around line 17-29: Implement `--global` in `PrefixError`/`pacquet prefix`
instead of rejecting it: replace the `GlobalUnsupported` path in `prefix.rs`
with pnpm-compatible global-prefix resolution in the `prefix` command flow,
using the existing prefix resolution logic rather than hard-failing on the flag.
Make sure the `prefix` output and any error/diagnostic behavior in the relevant
command handlers match pnpm exactly for `--global`, including the user-facing
message and exit behavior.

In `@pnpm11/pnpm/src/cmd/prefix.ts`:
- Around line 47-67: The `prefix` command is re-deriving the `-g` prefix from
`process.env` and `process.execPath` instead of using the resolved config, which
can drift from `@pnpm/config` behavior. Update the `prefix` handler in
`prefix.ts` so `opts.global` uses the global prefix already resolved in command
options/config, and keep `prefix` as a thin formatter rather than duplicating
fallback logic. Make sure the global-prefix resolution is threaded in from the
config layer and referenced consistently via the existing `opts` fields.

In `@pnpm11/pnpm/test/prefix.ts`:
- Around line 36-41: The pnpm prefix -g test is too weak because it only checks
that stdout is non-empty, so unrelated output would still pass. Update the test
in prefix.ts to assert the exact expected prefix value produced by
execPnpmSync(['prefix', '-g']) after tempDir(), using the same command’s
observable contract rather than a truthy check. Keep the existing status
assertion and replace the loose stdout expectation with a precise comparison
against the controlled expected global prefix output.

---

Nitpick comments:
In `@pacquet/crates/cli/tests/prefix.rs`:
- Around line 15-114: Add a regression test that exercises the `node_modules`
special-case in the real CLI path, since the current `prefix` tests only cover
manifest/workspace walk-up behavior. Extend the `prefix.rs` suite with a case
that runs `pacquet prefix` from inside a directory under `<prefix>/node_modules`
and asserts the resolved prefix matches the parent package root, using the
existing `CommandTempCwd`, `Command::cargo_bin("pacquet")`, and `prefix` CLI
wiring so the `node_modules` branch cannot regress silently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2810c8a4-d37a-49df-ba6a-dd86243a7af4

📥 Commits

Reviewing files that changed from the base of the PR and between a6f04d4 and 6a45f76.

📒 Files selected for processing (11)
  • .changeset/implement-prefix-command.md
  • pacquet/crates/cli/src/cli_args.rs
  • pacquet/crates/cli/src/cli_args/cli_command.rs
  • pacquet/crates/cli/src/cli_args/dispatch.rs
  • pacquet/crates/cli/src/cli_args/dispatch_query.rs
  • pacquet/crates/cli/src/cli_args/prefix.rs
  • pacquet/crates/cli/tests/prefix.rs
  • pnpm11/pnpm/src/cmd/index.ts
  • pnpm11/pnpm/src/cmd/notImplemented.ts
  • pnpm11/pnpm/src/cmd/prefix.ts
  • pnpm11/pnpm/test/prefix.ts
💤 Files with no reviewable changes (1)
  • pnpm11/pnpm/src/cmd/notImplemented.ts

Comment thread pacquet/crates/cli/src/cli_args/prefix.rs
Comment thread pnpm11/pnpm/src/cmd/prefix.ts Outdated
Comment thread pnpm11/pnpm/test/prefix.ts Outdated
Comment thread pnpm11/pnpm/src/cmd/prefix.ts Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 6a45f76

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

Commit: 010554dd91c2

Each scenario reports direct installs and pnpr installs. Bencher consumes pacquet@HEAD and pnpr@HEAD.

Scenario: Isolated linker: fresh restore, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 4.155 ± 0.127 4.041 4.384 2.02 ± 0.13
pacquet@main 4.112 ± 0.120 3.987 4.398 2.00 ± 0.13
pnpr@HEAD 2.125 ± 0.147 1.945 2.351 1.03 ± 0.09
pnpr@main 2.054 ± 0.122 1.922 2.294 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 4.155147593380001,
      "stddev": 0.12660284380827855,
      "median": 4.10997828358,
      "user": 4.009320860000001,
      "system": 3.4062212599999997,
      "min": 4.04094761958,
      "max": 4.38401656158,
      "times": [
        4.37356972458,
        4.09442699058,
        4.14703304658,
        4.38401656158,
        4.09878752358,
        4.05673852358,
        4.04492027558,
        4.12116904358,
        4.04094761958,
        4.1898666245800005
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 4.11238452368,
      "stddev": 0.11950761580488214,
      "median": 4.09489491858,
      "user": 4.00231756,
      "system": 3.39963396,
      "min": 3.9871845925800002,
      "max": 4.39813266458,
      "times": [
        4.06279224558,
        4.18060632858,
        4.39813266458,
        4.17572890658,
        4.00847957958,
        4.10065809858,
        3.9871845925800002,
        4.10081872358,
        4.02031235858,
        4.08913173858
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 2.12520266548,
      "stddev": 0.14673183174726145,
      "median": 2.06492507958,
      "user": 2.7204789599999994,
      "system": 2.85733136,
      "min": 1.9449078145799998,
      "max": 2.3505013745800003,
      "times": [
        2.31994494958,
        2.05342238358,
        2.23019243258,
        2.3505013745800003,
        2.02230351758,
        2.2421233655800004,
        2.03254651158,
        1.97965652958,
        1.9449078145799998,
        2.07642777558
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 2.05357068658,
      "stddev": 0.12157210023725921,
      "median": 2.01385268458,
      "user": 2.7346437599999995,
      "system": 2.8350010600000006,
      "min": 1.9222723395799999,
      "max": 2.29388930458,
      "times": [
        2.0172100635800003,
        1.93343391558,
        1.99163416558,
        1.9222723395799999,
        2.0797744325800003,
        2.29388930458,
        2.18820958458,
        1.95523983958,
        2.01049530558,
        2.14354791458
      ]
    }
  ]
}

Scenario: Isolated linker: fresh restore, hot cache + hot store

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 627.1 ± 11.0 611.9 647.0 1.00
pacquet@main 654.3 ± 13.5 637.1 676.0 1.04 ± 0.03
pnpr@HEAD 700.3 ± 69.0 665.1 893.5 1.12 ± 0.11
pnpr@main 707.2 ± 65.1 661.6 886.0 1.13 ± 0.11
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.62710095108,
      "stddev": 0.01102516540637441,
      "median": 0.62540529038,
      "user": 0.3688121,
      "system": 1.30288166,
      "min": 0.61186641438,
      "max": 0.6469672723800001,
      "times": [
        0.6469672723800001,
        0.64184677138,
        0.62752682638,
        0.63342509438,
        0.6255606263800001,
        0.6221073633800001,
        0.6149528133800001,
        0.62150637438,
        0.61186641438,
        0.6252499543800001
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.65432813208,
      "stddev": 0.01351444887993611,
      "median": 0.65072069788,
      "user": 0.3640370999999999,
      "system": 1.33658616,
      "min": 0.63706125538,
      "max": 0.67600346138,
      "times": [
        0.6445911923800001,
        0.63706125538,
        0.64135933538,
        0.67600346138,
        0.67453239438,
        0.65018050538,
        0.65126089038,
        0.66299016038,
        0.65962678838,
        0.64567533738
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.7002897370800001,
      "stddev": 0.06896150881200593,
      "median": 0.67666291438,
      "user": 0.3746016,
      "system": 1.35827476,
      "min": 0.66505367638,
      "max": 0.89349866838,
      "times": [
        0.66505367638,
        0.66510752738,
        0.67746381838,
        0.67478746438,
        0.6885020873800001,
        0.6954783183800001,
        0.69943026338,
        0.6677135363800001,
        0.67586201038,
        0.89349866838
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.70720761138,
      "stddev": 0.06513572459610457,
      "median": 0.6844270073800001,
      "user": 0.37297349999999996,
      "system": 1.3550851600000002,
      "min": 0.66161845038,
      "max": 0.8860461053800001,
      "times": [
        0.68496738538,
        0.67766775938,
        0.69566851638,
        0.70080332838,
        0.68253785138,
        0.67386805238,
        0.68388662938,
        0.66161845038,
        0.8860461053800001,
        0.72501203538
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 4.200 ± 0.033 4.140 4.252 1.93 ± 0.12
pacquet@main 4.191 ± 0.033 4.147 4.256 1.92 ± 0.12
pnpr@HEAD 2.180 ± 0.140 2.021 2.434 1.00
pnpr@main 2.218 ± 0.149 2.021 2.510 1.02 ± 0.09
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 4.19972782522,
      "stddev": 0.03264914893728307,
      "median": 4.200000237319999,
      "user": 3.7972316200000003,
      "system": 3.25812642,
      "min": 4.1402457973199995,
      "max": 4.252193516319999,
      "times": [
        4.20076527132,
        4.19923520332,
        4.16815532332,
        4.1402457973199995,
        4.195933773319999,
        4.203648384319999,
        4.18944572632,
        4.20138089232,
        4.252193516319999,
        4.24627436432
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 4.190856560319999,
      "stddev": 0.03314045589026519,
      "median": 4.19606036032,
      "user": 3.79695902,
      "system": 3.26110172,
      "min": 4.14735795132,
      "max": 4.25623584132,
      "times": [
        4.20862243432,
        4.14778166932,
        4.16395022532,
        4.14735795132,
        4.25623584132,
        4.20172150132,
        4.202835899319999,
        4.21110405432,
        4.19039921932,
        4.17855680732
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 2.18004485542,
      "stddev": 0.14005549587081192,
      "median": 2.14298145332,
      "user": 2.5562190199999995,
      "system": 2.77711362,
      "min": 2.02102351932,
      "max": 2.43404729032,
      "times": [
        2.0994991023200003,
        2.09108275432,
        2.24002996432,
        2.1122030293200003,
        2.17375987732,
        2.0290865993200002,
        2.37921146732,
        2.02102351932,
        2.43404729032,
        2.22050495032
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 2.2179313576200004,
      "stddev": 0.14913019849843162,
      "median": 2.2157672143200005,
      "user": 2.5720018199999997,
      "system": 2.7852485199999997,
      "min": 2.02079201932,
      "max": 2.51013708232,
      "times": [
        2.51013708232,
        2.12090770532,
        2.2112655983200002,
        2.28469202132,
        2.3092695663200002,
        2.22026883032,
        2.02079201932,
        2.09787318632,
        2.34121006532,
        2.06289750132
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, hot cache + hot store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.360 ± 0.019 1.326 1.384 2.06 ± 0.12
pacquet@main 1.369 ± 0.024 1.345 1.429 2.07 ± 0.13
pnpr@HEAD 0.661 ± 0.013 0.643 0.680 1.00 ± 0.06
pnpr@main 0.660 ± 0.038 0.643 0.768 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 1.35960821814,
      "stddev": 0.01905742695707547,
      "median": 1.35958564974,
      "user": 1.28735236,
      "system": 1.7179404999999999,
      "min": 1.32640174174,
      "max": 1.3839222777400002,
      "times": [
        1.33975415774,
        1.34235270574,
        1.37528261174,
        1.3743502167400001,
        1.36292736074,
        1.3556894137400002,
        1.3839222777400002,
        1.3791577567400002,
        1.32640174174,
        1.35624393874
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 1.36874294044,
      "stddev": 0.023583225385931047,
      "median": 1.36437949924,
      "user": 1.31872046,
      "system": 1.6960855,
      "min": 1.3445083137400002,
      "max": 1.42914412674,
      "times": [
        1.34631344274,
        1.37346579274,
        1.42914412674,
        1.36410349374,
        1.3646555047400002,
        1.3763289117400002,
        1.36370680674,
        1.35770595174,
        1.3445083137400002,
        1.36749705974
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.6608524870400001,
      "stddev": 0.012759017422659176,
      "median": 0.66184897724,
      "user": 0.33032336000000007,
      "system": 1.2861784,
      "min": 0.64305248074,
      "max": 0.68019068374,
      "times": [
        0.6451222197400001,
        0.6732567177400001,
        0.65233860974,
        0.64305248074,
        0.66727782374,
        0.68019068374,
        0.65853819674,
        0.66515975774,
        0.65098754374,
        0.67260083674
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.65968264214,
      "stddev": 0.03815214399151571,
      "median": 0.64719120074,
      "user": 0.33276516,
      "system": 1.263074,
      "min": 0.6428999187400001,
      "max": 0.76765527674,
      "times": [
        0.6507590467400001,
        0.64700207974,
        0.65187682874,
        0.6428999187400001,
        0.65562333374,
        0.6451341647400001,
        0.64551474274,
        0.64298070774,
        0.76765527674,
        0.64738032174
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + hot store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 3.041 ± 0.060 2.983 3.166 4.57 ± 0.12
pacquet@main 3.027 ± 0.057 2.975 3.177 4.55 ± 0.11
pnpr@HEAD 0.666 ± 0.011 0.655 0.691 1.00
pnpr@main 0.670 ± 0.014 0.657 0.701 1.01 ± 0.03
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 3.04142247666,
      "stddev": 0.060237957177601525,
      "median": 3.01937678656,
      "user": 1.76179676,
      "system": 1.9712096199999998,
      "min": 2.98271074606,
      "max": 3.16633876306,
      "times": [
        3.16633876306,
        3.00726253406,
        3.02134383006,
        3.01740974306,
        2.98271074606,
        3.00951512506,
        3.03088191406,
        3.13571286806,
        3.03952438006,
        3.00352486306
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 3.02690361146,
      "stddev": 0.057451845517073746,
      "median": 3.02632143106,
      "user": 1.7613915599999999,
      "system": 2.0121737200000003,
      "min": 2.97535355906,
      "max": 3.17709443706,
      "times": [
        3.03173270306,
        3.02314071106,
        2.99740998206,
        3.03252374606,
        2.97535355906,
        3.02950215106,
        2.98345667206,
        3.17709443706,
        3.03228500106,
        2.9865371520600004
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.6655732482600001,
      "stddev": 0.010651808495969455,
      "median": 0.66350980406,
      "user": 0.32552306000000003,
      "system": 1.3033361200000002,
      "min": 0.6549276210600001,
      "max": 0.6906892610600001,
      "times": [
        0.6667007630600001,
        0.6642646870600001,
        0.6731570670600001,
        0.6549276210600001,
        0.6699567690600001,
        0.65586600106,
        0.6906892610600001,
        0.6580568020600001,
        0.6627549210600001,
        0.6593585900600001
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.66957889086,
      "stddev": 0.013694220197446417,
      "median": 0.6679920080600001,
      "user": 0.33087116,
      "system": 1.2769123200000003,
      "min": 0.6567306890600001,
      "max": 0.7011818350600001,
      "times": [
        0.6783096200600001,
        0.6693875720600001,
        0.6569830050600001,
        0.6779984350600001,
        0.6683483590600001,
        0.6622509640600001,
        0.6567306890600001,
        0.66763565706,
        0.7011818350600001,
        0.6569627720600001
      ]
    }
  ]
}

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12728
Testbedpacquet
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
🚷 view threshold
4,199.73 ms
(-11.04%)Baseline: 4,720.70 ms
5,664.83 ms
(74.14%)
isolated-linker.fresh-install.cold-cache.hot-store📈 view plot
🚷 view threshold
3,041.42 ms
(-1.15%)Baseline: 3,076.68 ms
3,692.02 ms
(82.38%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
1,359.61 ms
(-0.72%)Baseline: 1,369.44 ms
1,643.33 ms
(82.73%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
4,155.15 ms
(-12.72%)Baseline: 4,760.95 ms
5,713.14 ms
(72.73%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
627.10 ms
(-2.66%)Baseline: 644.25 ms
773.10 ms
(81.12%)
🐰 View full continuous benchmarking report in Bencher

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12728
Testbedpnpr

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds flag.

Click to view all benchmark results
BenchmarkLatencymilliseconds (ms)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
⚠️ NO THRESHOLD
2,180.04 ms
isolated-linker.fresh-install.cold-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
665.57 ms
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
660.85 ms
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
⚠️ NO THRESHOLD
2,125.20 ms
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
700.29 ms
🐰 View full continuous benchmarking report in Bencher

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pacquet/crates/cli/src/cli_args/prefix.rs`:
- Around line 57-80: The prefix search in the path-walking logic should not
return `original` as soon as `current.parent().is_none()` is reached; instead,
probe the filesystem root with the same `targets` check first. Update the loop
in the prefix resolution routine in `prefix.rs` so the root directory is
examined for markers like `package.json` or `node_modules` before falling back
to `original.to_path_buf()`, using the existing `current`, `targets`, and
`try_exists` flow.

In `@pnpm11/pnpm/src/cmd/prefix.ts`:
- Around line 46-47: The global branch in prefix.ts currently returns an empty
string when opts.globalPkgDir is missing, which makes pnpm prefix -g succeed
silently. Update the getPrefix logic for opts.global to throw a user-facing
PnpmError instead of returning '' when the global package dir cannot be
resolved, so the command fails clearly and surfaces the config problem rather
than emitting a blank line.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f3c15ec4-51ff-48fc-b86c-ce32b901174f

📥 Commits

Reviewing files that changed from the base of the PR and between 6a45f76 and 6980a49.

📒 Files selected for processing (5)
  • pacquet/crates/cli/src/cli_args/prefix.rs
  • pacquet/crates/cli/tests/prefix.rs
  • pnpm11/pnpm/src/cmd/prefix.ts
  • pnpm11/pnpm/src/main.ts
  • pnpm11/pnpm/test/prefix.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • pnpm11/pnpm/test/prefix.ts

Comment thread pacquet/crates/cli/src/cli_args/prefix.rs
Comment thread pnpm11/pnpm/src/cmd/prefix.ts Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 6980a49

Comment thread pnpm11/pnpm/src/cmd/prefix.ts
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 6980a49

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@kairosci
kairosci force-pushed the feat/prefix-command branch from ec51157 to e3ce5fb Compare June 29, 2026 18:43
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit ec51157

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit e3ce5fb

1 similar comment
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit e3ce5fb

@codecov-commenter

codecov-commenter commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.48780% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.19%. Comparing base (cc04eb1) to head (010554d).

Files with missing lines Patch % Lines
pacquet/crates/cli/src/cli_args/prefix.rs 77.77% 8 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #12728   +/-   ##
=======================================
  Coverage   85.19%   85.19%           
=======================================
  Files         407      408    +1     
  Lines       61945    61986   +41     
=======================================
+ Hits        52776    52812   +36     
- Misses       9169     9174    +5     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit b7d2645

1 similar comment
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit b7d2645

@github-actions github-actions Bot added the reviewed: coderabbit CodeRabbit submitted an approving review label Jun 29, 2026
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

1 similar comment
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

Comment thread pnpm11/deps/inspection/commands/src/bugs/index.ts Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 834eecc

@kairosci
kairosci force-pushed the feat/prefix-command branch from 834eecc to 57f9430 Compare June 29, 2026 21:13
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 57f9430

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@kairosci
kairosci force-pushed the feat/prefix-command branch from 57f9430 to 010554d Compare June 29, 2026 21:17
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 010554d

1 similar comment
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 010554d

@zkochan
zkochan added this pull request to the merge queue Jul 2, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 2, 2026
@zkochan
zkochan merged commit 8491f8e into pnpm:main Jul 2, 2026
34 checks passed
@kairosci
kairosci deleted the feat/prefix-command branch July 19, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reviewed: coderabbit CodeRabbit submitted an approving review state: automerge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants