feat(cli): port the bin command to pacquet#12703
Conversation
Port pnpm's `bin` command, which prints the directory where executables are installed. The local invocation prints `<dir>/node_modules/.bin`: the `node_modules/.bin` leaf is hardcoded, so a configured `modules-dir` is ignored, and the path is anchored on the already-canonicalized `--dir`, mirroring pnpm's config reader. `--global` prints the resolved global bin directory (`global-bin-dir ?? <pnpm-home>/bin`) and reuses the existing `ERR_PNPM_NO_GLOBAL_BIN_DIR` diagnostic when it cannot be resolved. Unlike pnpm, the read-only `--global` path does not run `checkGlobalBinDir` (its PATH/writability validation) or create the directory. pacquet limits that to its mutating global handlers, matching the sibling `outdated --global`; porting the validation to the config layer for all read-only `-g` commands is left as a follow-up. Ported from pnpm's bin.ts and config reader.
|
💖 Thanks for opening this pull request! 💖 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds a new Changespacquet bin subcommand
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested labels
Suggested reviewers
✨ 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 Qodofeat(cli): add Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
1. Non-hermetic bin tests
|
|
Code review by qodo was updated up to the latest commit 0e98d96 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
pacquet/crates/cli/tests/bin.rs (1)
56-88: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd the unresolved-global-bin regression.
The suite locks in the happy path for
bin -g, but not theERR_PNPM_NO_GLOBAL_BIN_DIRbranch on Line 46 ofpacquet/crates/cli/src/cli_args/bin.rs. One more CLI integration test that removes the home/global-bin inputs would keep that user-visible error contract from drifting. As per path instructions, “add regression/e2e coverage that proves the changed behavior.”🤖 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/bin.rs` around lines 56 - 88, Add a CLI integration regression for the unresolved global bin case handled by BinArgs::run in the bin command, covering the ERR_PNPM_NO_GLOBAL_BIN_DIR branch. Extend the tests in bin.rs with a scenario that clears the global-bin inputs (for example unset PNPM_HOME and the global-bin-dir config env vars, while keeping HOME/XDG_CONFIG_HOME pinned) and invoke pacquet bin -g. Assert the command fails with the expected user-visible error so the error contract stays locked in alongside bin_global_prints_the_configured_global_bin_dir.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/bin.rs`:
- Around line 35-46: `bin -g` is bypassing pnpm’s shared global-bin resolution
behavior and returning `config.global_bin` directly, which can change the
visible error/output for a newly ported command. Update `bin::run` to use the
same global-bin lookup/validation path as the mutating global handlers, so
`check_global_bin_dir`-equivalent logic runs before `bin -g` prints a path. Keep
the fix centered around `run` and the
`config.global_bin`/`GlobalError::NoGlobalBinDir` flow so the command matches
pnpm’s `checkGlobalBinDir` behavior.
---
Nitpick comments:
In `@pacquet/crates/cli/tests/bin.rs`:
- Around line 56-88: Add a CLI integration regression for the unresolved global
bin case handled by BinArgs::run in the bin command, covering the
ERR_PNPM_NO_GLOBAL_BIN_DIR branch. Extend the tests in bin.rs with a scenario
that clears the global-bin inputs (for example unset PNPM_HOME and the
global-bin-dir config env vars, while keeping HOME/XDG_CONFIG_HOME pinned) and
invoke pacquet bin -g. Assert the command fails with the expected user-visible
error so the error contract stays locked in alongside
bin_global_prints_the_configured_global_bin_dir.
🪄 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: d43df582-a930-4378-8c0e-8bde0102544c
📒 Files selected for processing (6)
pacquet/crates/cli/src/cli_args.rspacquet/crates/cli/src/cli_args/bin.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/tests/bin.rs
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12703 +/- ##
==========================================
+ Coverage 85.25% 85.32% +0.06%
==========================================
Files 396 405 +9
Lines 60060 61623 +1563
==========================================
+ Hits 51207 52577 +1370
- Misses 8853 9046 +193 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Match pnpm's config reader for `--global`: create the global bin directory and run `check_global_bin_dir` (PATH membership + writability, since `globalDirShouldAllowWrite` is true for every command except `root`) before printing, instead of printing `config.global_bin` unvalidated. `bin -g` now errors with `ERR_PNPM_GLOBAL_BIN_DIR_NOT_IN_PATH` / `ERR_PNPM_NO_PATH_ENV` / `ERR_PNPM_PNPM_DIR_NOT_WRITABLE` exactly as pnpm does, which also removes the read-only `--global` divergence from the original port. Add Unix-gated tests for the on-PATH success and not-in-PATH failure paths.
|
Code review by qodo was updated up to the latest commit e5d6222 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
pacquet/crates/cli/src/cli_args/bin.rs (1)
55-55: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftThread
PATHin; don't read it directly here.This env-sensitive branch reaches into
std::envinstead of takingPATHfrom theSys/Hostseam, which is the pacquet convention for process-state dependent logic. Passing it in keeps this command testable the same way as the rest of the CLI plumbing. As per path instructions, "library code should generally not callstd::envdirectly—thread it through parameters or the DI seam (important givenbin -gdepends on env like PATH/home/pnpm-home)."🤖 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/src/cli_args/bin.rs` at line 55, The `bin` CLI path currently reads `PATH` directly via `std::env` inside the `check_global_bin_dir` call, which bypasses the `Sys`/`Host` seam. Update the `bin` command wiring in `cli_args/bin.rs` to accept `PATH` from the existing process-state dependency injection layer and pass that value into `check_global_bin_dir` instead of calling `std::env::var` there. Keep the fix localized to the `bin` argument handling and the `check_global_bin_dir` invocation so the command remains testable and consistent with the rest of the CLI plumbing.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/bin.rs`:
- Around line 49-54: The `create_dir_all` failure path in `bin.rs` is emitting a
generic `miette!` message instead of pnpm’s stable coded diagnostic. Update the
error mapping in the `bin -g` setup path to return the same pnpm-style error
code/message for unwritable home/bin directories, using the existing
`create_dir_all` branch and `bin` handling as the entry point. Ensure the
user-facing diagnostic matches pnpm exactly rather than a raw local message.
---
Nitpick comments:
In `@pacquet/crates/cli/src/cli_args/bin.rs`:
- Line 55: The `bin` CLI path currently reads `PATH` directly via `std::env`
inside the `check_global_bin_dir` call, which bypasses the `Sys`/`Host` seam.
Update the `bin` command wiring in `cli_args/bin.rs` to accept `PATH` from the
existing process-state dependency injection layer and pass that value into
`check_global_bin_dir` instead of calling `std::env::var` there. Keep the fix
localized to the `bin` argument handling and the `check_global_bin_dir`
invocation so the command remains testable and consistent with the rest of the
CLI plumbing.
🪄 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: 66876a4d-5fbb-4f18-a379-fd9ec7616888
📒 Files selected for processing (2)
pacquet/crates/cli/src/cli_args/bin.rspacquet/crates/cli/tests/bin.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- pacquet/crates/cli/tests/bin.rs
|
Code review by qodo was updated up to the latest commit e5d6222 |
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.6244192260000005,
"stddev": 0.1632418997900026,
"median": 4.5817925399,
"user": 3.8496015999999997,
"system": 3.37614758,
"min": 4.457340080900001,
"max": 4.9589745579,
"times": [
4.9589745579,
4.4598500619,
4.684537710900001,
4.775089745900001,
4.565753043900001,
4.597832035900001,
4.5349610569,
4.457340080900001,
4.475160955900001,
4.734693009900001
]
},
{
"command": "pacquet@main",
"mean": 4.6324662349,
"stddev": 0.12668596816913794,
"median": 4.5798370059,
"user": 3.9063811999999993,
"system": 3.4183351799999997,
"min": 4.455753035900001,
"max": 4.866166138900001,
"times": [
4.803392107900001,
4.866166138900001,
4.5580820709,
4.642798267900001,
4.5731351979,
4.455753035900001,
4.5726219709,
4.586538813900001,
4.5478412739000005,
4.7183334709
]
},
{
"command": "pnpr@HEAD",
"mean": 3.1159693474999997,
"stddev": 0.1711368244495741,
"median": 3.0786588644,
"user": 2.8331536999999996,
"system": 3.02630778,
"min": 2.9293841909,
"max": 3.3766722709,
"times": [
3.2475598669,
3.2864188819,
2.9389345529,
3.2815632349,
2.9609959859,
3.0057907769,
3.3766722709,
3.1515269518999998,
2.9808467619,
2.9293841909
]
},
{
"command": "pnpr@main",
"mean": 3.0542685628,
"stddev": 0.11812687325157778,
"median": 3.0837257819,
"user": 2.8162016999999997,
"system": 3.04804668,
"min": 2.8733198879,
"max": 3.2359434489,
"times": [
3.1086142289,
3.1430870549,
3.0302131419,
2.8733198879,
3.1474924799,
3.0823682199,
3.0850833439,
2.9406651419,
2.8958986799,
3.2359434489
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6594830742000001,
"stddev": 0.009348632548609247,
"median": 0.6599387217000001,
"user": 0.41397456000000005,
"system": 1.3339206,
"min": 0.6434793372000001,
"max": 0.6734277422,
"times": [
0.6734277422,
0.6700343622,
0.6571484082000001,
0.6646401712000001,
0.6583100592000001,
0.6642442552000001,
0.6541480422,
0.6478309802000001,
0.6615673842,
0.6434793372000001
]
},
{
"command": "pacquet@main",
"mean": 0.7061202181,
"stddev": 0.049375509358668114,
"median": 0.6982585162000001,
"user": 0.41113685999999994,
"system": 1.3594964999999999,
"min": 0.6696645012000001,
"max": 0.8402836932000001,
"times": [
0.6775783282000001,
0.7039033602000001,
0.6717811672,
0.6984842332000001,
0.7001027202000001,
0.7147372642,
0.8402836932000001,
0.6866341142000001,
0.6980327992,
0.6696645012000001
]
},
{
"command": "pnpr@HEAD",
"mean": 0.7160159576,
"stddev": 0.03420449789973863,
"median": 0.7129914132000001,
"user": 0.41961116,
"system": 1.3508265000000002,
"min": 0.6867644012,
"max": 0.8057103912000001,
"times": [
0.7201119182000001,
0.6877873922000001,
0.7117217332000001,
0.6867644012,
0.7194624092,
0.7175821742,
0.7072441892000001,
0.6895138742000001,
0.8057103912000001,
0.7142610932000001
]
},
{
"command": "pnpr@main",
"mean": 0.6994623231,
"stddev": 0.030095808083333807,
"median": 0.6944825142000001,
"user": 0.39621686,
"system": 1.3376103,
"min": 0.6662602802,
"max": 0.7450293142000001,
"times": [
0.7282141432000001,
0.6698606872,
0.7101974762000001,
0.6662602802,
0.6920487002000001,
0.6744277862000001,
0.7450293142000001,
0.6969163282,
0.7403708332000001,
0.6712976822000001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.8678361186800005,
"stddev": 0.038981599158947874,
"median": 4.86980388848,
"user": 3.991254,
"system": 3.3749446,
"min": 4.801036309480001,
"max": 4.9307410034800006,
"times": [
4.9307410034800006,
4.87748042848,
4.882888883480001,
4.86431572648,
4.87088360448,
4.801036309480001,
4.9128102374800005,
4.816012676480001,
4.853468144480001,
4.86872417248
]
},
{
"command": "pacquet@main",
"mean": 4.81237314128,
"stddev": 0.040051240960100945,
"median": 4.815659327480001,
"user": 3.9598172,
"system": 3.3442836999999996,
"min": 4.75137314048,
"max": 4.863751496480001,
"times": [
4.7763516074800005,
4.807301739480001,
4.863751496480001,
4.756806222480001,
4.860245513480001,
4.75137314048,
4.83813912348,
4.83844391448,
4.813622657480001,
4.81769599748
]
},
{
"command": "pnpr@HEAD",
"mean": 2.82020939848,
"stddev": 0.09372181899393389,
"median": 2.80760268698,
"user": 2.6320497000000005,
"system": 2.8592299999999997,
"min": 2.67416878548,
"max": 3.0295678054799997,
"times": [
2.82060684948,
2.77766458448,
2.80992044948,
2.77001555448,
2.8280400814799997,
3.0295678054799997,
2.9070476944799997,
2.67416878548,
2.80528492448,
2.77977725548
]
},
{
"command": "pnpr@main",
"mean": 2.8596629254799995,
"stddev": 0.14087319625459097,
"median": 2.81528348948,
"user": 2.6345053999999997,
"system": 2.8003747999999997,
"min": 2.67586839848,
"max": 3.04558626148,
"times": [
2.83248030448,
3.04558626148,
3.03723277248,
2.79808667448,
2.7172693994799997,
2.73011181248,
2.79364098948,
3.0168542124799997,
2.9494984294799997,
2.67586839848
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.3887261919200002,
"stddev": 0.007084986580673581,
"median": 1.39066138152,
"user": 1.36956104,
"system": 1.7033499200000002,
"min": 1.3792077705199999,
"max": 1.39952779152,
"times": [
1.3792077705199999,
1.38009634852,
1.39269514152,
1.39043038252,
1.38186647452,
1.38400278652,
1.39708296852,
1.39145987452,
1.39089238052,
1.39952779152
]
},
{
"command": "pacquet@main",
"mean": 1.3962445322199997,
"stddev": 0.024145397454521243,
"median": 1.3845634705199998,
"user": 1.34771024,
"system": 1.72771592,
"min": 1.37370536252,
"max": 1.4522097385200001,
"times": [
1.4522097385200001,
1.41341626052,
1.37869423952,
1.40314741652,
1.41031463752,
1.38402923552,
1.37818107152,
1.37370536252,
1.38509770552,
1.38364965452
]
},
{
"command": "pnpr@HEAD",
"mean": 0.66471595662,
"stddev": 0.008513519402470441,
"median": 0.6644030785199999,
"user": 0.3604703399999999,
"system": 1.28936132,
"min": 0.65172278152,
"max": 0.68438663752,
"times": [
0.66044195752,
0.66452237052,
0.65172278152,
0.65850576252,
0.66493201252,
0.66651461452,
0.66428378652,
0.68438663752,
0.66184441452,
0.67000522852
]
},
{
"command": "pnpr@main",
"mean": 0.6698545571200001,
"stddev": 0.010918064631357033,
"median": 0.6686461720200001,
"user": 0.33782063999999995,
"system": 1.29533582,
"min": 0.65087596852,
"max": 0.68844141252,
"times": [
0.68449339252,
0.67121396352,
0.65087596852,
0.66305293252,
0.66722316952,
0.66844252552,
0.68844141252,
0.66884981852,
0.66135087952,
0.67460150852
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 3.05491679962,
"stddev": 0.03852659336867342,
"median": 3.04574349082,
"user": 1.81932262,
"system": 1.9788687599999997,
"min": 3.01587210332,
"max": 3.14447448632,
"times": [
3.03449473632,
3.08112945032,
3.02677149432,
3.01807079732,
3.01587210332,
3.0719620293200003,
3.04502007032,
3.14447448632,
3.06490591732,
3.04646691132
]
},
{
"command": "pacquet@main",
"mean": 3.10090176712,
"stddev": 0.06518632868884316,
"median": 3.08782682782,
"user": 1.8125300199999999,
"system": 2.0212715599999997,
"min": 3.01717129832,
"max": 3.21051245032,
"times": [
3.01717129832,
3.06456165632,
3.0245637863200003,
3.08165181932,
3.06628593832,
3.09400183632,
3.15126210432,
3.10954648632,
3.21051245032,
3.18946029532
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6878605028200001,
"stddev": 0.00891981197060841,
"median": 0.6874942508200002,
"user": 0.37372492,
"system": 1.32468086,
"min": 0.6754186283200001,
"max": 0.7045826313200001,
"times": [
0.6971793073200001,
0.7045826313200001,
0.6817226483200001,
0.6877953993200001,
0.6754186283200001,
0.69421252032,
0.6846371453200001,
0.6882868493200001,
0.6871931023200001,
0.6775767963200001
]
},
{
"command": "pnpr@main",
"mean": 0.67851414112,
"stddev": 0.02117015289466461,
"median": 0.6703563698200001,
"user": 0.35646601999999994,
"system": 1.29689896,
"min": 0.66156945432,
"max": 0.71966284832,
"times": [
0.66521117732,
0.6648998183200001,
0.66402101532,
0.66156945432,
0.6748348073200001,
0.66587793232,
0.7146254763200001,
0.6768464643200001,
0.71966284832,
0.6775924173200001
]
}
]
} |
|
| Branch | pr/12703 |
| 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,867.84 ms(+2.22%)Baseline: 4,762.30 ms | 5,714.75 ms (85.18%) |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot 🚷 view threshold | 3,054.92 ms(-0.06%)Baseline: 3,056.85 ms | 3,668.22 ms (83.28%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,388.73 ms(+2.26%)Baseline: 1,358.09 ms | 1,629.71 ms (85.21%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 4,624.42 ms(-4.53%)Baseline: 4,843.80 ms | 5,812.56 ms (79.56%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 659.48 ms(+2.20%)Baseline: 645.27 ms | 774.33 ms (85.17%) |
|
| Branch | pr/12703 |
| 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,820.21 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 687.86 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 664.72 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 3,115.97 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 716.02 ms |
Bind the path display to a `let` before the `create_dir_all` failure `miette!` instead of passing `bin.display()` directly. perfectionist's `macro_argument_binding` Dylint rejects an impure expression passed straight to a third-party macro (it exempts std `format!` / `println!`), matching the existing pattern in `link.rs`. Dylint runs only in CI (the pre-push hook skips it when `cargo-dylint` is absent), so this was missed locally.
|
Code review by qodo was updated up to the latest commit dd3bb4c |
1 similar comment
|
Code review by qodo was updated up to the latest commit dd3bb4c |
zkochan
left a comment
There was a problem hiding this comment.
I see too many comments in the code. I doubt this adheres our styleguides.
Cut the duplicated and what-restating comments in the `bin` command and its tests. The local/global resolution rationale and the modules-dir-ignored note were repeated across the struct doc, the `run` doc, and an inline comment; keep the non-obvious why once on the struct, and one short note on why `should_allow_write` is true. In the tests, drop comments that restate the asserts (trailing newline, dir creation) and condense the doc comments to the non-obvious why (modules-dir hardcoding, the Unix gate, the Windows skip).
|
Thanks, good call. I trimmed the comments down to the non-obvious why and removed the ones that restated the code or the test assertions, so the same rationale is no longer repeated across the struct doc, the run doc, and the inline comment. Updated in 681d93b. |
|
Code review by qodo was updated up to the latest commit 681d93b |
1 similar comment
|
Code review by qodo was updated up to the latest commit 681d93b |
Brings in the filter->recursive promotion (#12726), the `bin` (#12703) and `repo` (#12702) commands, and a pnpr cold-store perf fix (#12709). Resolved one conflict in cli_args/dispatch_query.rs: the publish and bin handlers and their imports were both added at the same spot, so both were kept. Adapted recursive publish to #12726's new shared-function signatures: `discover_workspace_projects` now returns `(projects, patterns)` and `select_recursive_projects` takes an `AutoExcludeRoot`. Publish passes `AutoExcludeRoot::Disabled` because it is not in pnpm's run/exec/add/test root-auto-exclusion set; its private/unnamed eligibility check drops the workspace root instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KtBQzmLLDU3RcGzzCMopPB
Summary
Ports the
bincommand from the TypeScript pnpm CLI to the Rustpacquetport, mirroringpnpm11/pnpm/src/cmd/bin.tsand the bin-dir resolution inpnpm11/config/reader/src/index.ts.pacquet binprints the directory where executables are installed:<dir>/node_modules/.bin— thenode_modules/.binleaf is hardcoded (a configuredmodules-diris ignored) and the path is anchored on the already-canonicalized--dir, matching pnpm'sconfig.dir(the cwd, not the workspace root). Includes a differential test against the realpnpmfrom a workspace subdirectory.--global/-g, the resolved global bin directory (global-bin-dir ?? <pnpm-home>/bin). Mirroring pnpm's config reader, it creates the directory and validates it withcheckGlobalBinDir(PATH membership + writability, sinceglobalDirShouldAllowWriteis true for every command exceptroot) before printing, erroring withERR_PNPM_GLOBAL_BIN_DIR_NOT_IN_PATH/ERR_PNPM_NO_PATH_ENV/ERR_PNPM_PNPM_DIR_NOT_WRITABLE, orERR_PNPM_NO_GLOBAL_BIN_DIRwhen no pnpm home resolves.Related to #11633.
Squash Commit Body
Checklist
pacquet/port, or the description notes what still needs porting.bin; this ports it to pacquet. No TypeScript changes needed.pnpm changeset) if this PR changes any published package. Keep it short and written for pnpm users — it becomes a release note.node_modules/.bin, ignores a custom modules-dir,-gprints the validated global bin dir,-gerrors when not onPATH, and a differential test against the realpnpmfrom a workspace subdir. The two-gtests are Unix-gated (likeglobal.rs) because thePATHvalidation is platform-specific.Summary by CodeRabbit
binCLI subcommand that prints the resolvednode_modules/.bin(local) or global executables directory.--global/-gto output the global executables directory and create it if needed.PATH, with clearer failure behavior when not reachable.pnpm bin).