feat(cli): implement prefix command#12728
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds the ChangesPrefix command implementation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
PR Summary by QodoImplement
AI Description
Diagram
High-Level Assessment
Files changed (11)
|
Code Review by Qodo
1. issues alias overridden
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
pacquet/crates/cli/tests/prefix.rs (1)
15-114: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd one regression test for the
node_moduleswalk-up branch.The implementation has a dedicated
node_modulesspecial case, but this suite only exercises the manifest/workspace walk-up paths. A real CLI test that runs from<prefix>/node_moduleswould 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
📒 Files selected for processing (11)
.changeset/implement-prefix-command.mdpacquet/crates/cli/src/cli_args.rspacquet/crates/cli/src/cli_args/cli_command.rspacquet/crates/cli/src/cli_args/dispatch.rspacquet/crates/cli/src/cli_args/dispatch_query.rspacquet/crates/cli/src/cli_args/prefix.rspacquet/crates/cli/tests/prefix.rspnpm11/pnpm/src/cmd/index.tspnpm11/pnpm/src/cmd/notImplemented.tspnpm11/pnpm/src/cmd/prefix.tspnpm11/pnpm/test/prefix.ts
💤 Files with no reviewable changes (1)
- pnpm11/pnpm/src/cmd/notImplemented.ts
|
Code review by qodo was updated up to the latest commit 6a45f76 |
Integrated-Benchmark Report (Linux)Commit: Each scenario reports direct installs and pnpr installs. Bencher consumes pacquet@HEAD and pnpr@HEAD. Scenario: Isolated linker: fresh restore, cold cache + cold store
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
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
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
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
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
]
}
]
} |
|
| Branch | pr/12728 |
| Testbed | pacquet |
Click to view all benchmark results
| Benchmark | Latency | Benchmark 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%) |
|
| Branch | pr/12728 |
| Testbed | pnpr |
⚠️ 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-thresholdsflag.
Click to view all benchmark results
| Benchmark | Latency | milliseconds (ms) |
|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot | 2,180.04 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 665.57 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 660.85 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 2,125.20 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 700.29 ms |
There was a problem hiding this comment.
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
📒 Files selected for processing (5)
pacquet/crates/cli/src/cli_args/prefix.rspacquet/crates/cli/tests/prefix.rspnpm11/pnpm/src/cmd/prefix.tspnpm11/pnpm/src/main.tspnpm11/pnpm/test/prefix.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- pnpm11/pnpm/test/prefix.ts
|
Code review by qodo was updated up to the latest commit 6980a49 |
|
Code review by qodo was updated up to the latest commit 6980a49 |
ec51157 to
e3ce5fb
Compare
|
Code review by qodo was updated up to the latest commit ec51157 |
|
Code review by qodo was updated up to the latest commit e3ce5fb |
1 similar comment
|
Code review by qodo was updated up to the latest commit e3ce5fb |
Codecov Report❌ Patch coverage is
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. 🚀 New features to boost your workflow:
|
|
Code review by qodo was updated up to the latest commit b7d2645 |
1 similar comment
|
Code review by qodo was updated up to the latest commit b7d2645 |
1 similar comment
…e pnpm parity test
…pmError, writability check
|
Code review by qodo was updated up to the latest commit 834eecc |
834eecc to
57f9430
Compare
|
Code review by qodo was updated up to the latest commit 57f9430 |
57f9430 to
010554d
Compare
|
Code review by qodo was updated up to the latest commit 010554d |
1 similar comment
|
Code review by qodo was updated up to the latest commit 010554d |
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
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
prefixcommand to thepacquetCLI that prints the resolved local prefix directory.node_modules).pnpm prefixcommand;pnpm prefix -g/--globaloutputs the global prefix directory.pacquet prefixdoes not support--global.prefixoutput routing and tightened permission handling for the global bin directory.pacquet prefix(including--globalfailure) and Jest coverage forpnpm prefix(local + global).