Skip to content

feat(pacquet): implement the update command#12102

Merged
zkochan merged 3 commits into
mainfrom
pq-update
Jun 1, 2026
Merged

feat(pacquet): implement the update command#12102
zkochan merged 3 commits into
mainfrom
pq-update

Conversation

@zkochan

@zkochan zkochan commented Jun 1, 2026

Copy link
Copy Markdown
Member

Ports pnpm's update (aliases up / upgrade) to pacquet, onto its always-fresh-resolve install path.

Behavior

  • Compatible bump (no --latest): the matched names have their lockfile pins withheld from the preferred-versions seed (new UpdateSeedPolicy), so the resolver re-picks the highest version satisfying the manifest range. package.json is left untouched — this is exactly what distinguishes update from install (which keeps the pin because it still satisfies the range). Mirrors pnpm's update: 'compatible'.
  • --latest: each matched direct dependency's latest dist-tag is fetched and written into package.json (^<version>, or exact under --save-exact), then the install resolves the new range. Mirrors pnpm's updateToLatest + updateSpec.
  • Selectors: bare-name / glob patterns (@scope/bar-*) with depth > 0 and no --latest match every locked package name at any depth (like pnpm's updateMatching(infoFromLockfile.name, …)); versioned (foo@2) or --latest selectors match direct deps only; --latest combined with a spec is rejected (ERR_PNPM_LATEST_WITH_SPEC).
  • Flags: -L/--latest, -E/--save-exact, -P/-D/--no-optional (faithful makeIncludeDependenciesFromCLI, including the quirk that --no-optional only drops optionals alongside --prod/--dev), --depth, --lockfile-only, -i/--interactive.
  • Interactive uses dialoguer (added to [workspace.dependencies]) plus an inline outdated check (current from the lockfile, target from one packument fetch).

The resolver's UpdateBehavior tri-state and the npm resolver's include_latest_tag already existed — this PR drives them from a new CLI command and pacquet_package_manager::Update operation.

Deferred (error out rather than misbehave) → tracked in #12101

  • --global / -g (global-dir + @pnpm/global.commands not ported)
  • --workspace (workspace-protocol version linking not ported)
  • Known gap: under recursive -r, sibling-importer package.json files aren't rewritten on --latest (only the root manifest is); resolution still spans all importers.

Tests

New e2e suite pacquet/crates/cli/tests/update.rs: compatible bump within range, --latest manifest rewrite, --save-exact, scoped selector, up alias, and LATEST_WITH_SPEC rejection. Full Rust suite green; cargo check / clippy -D warnings / cargo doc -D warnings / dylint (perfectionist) / fmt --check / typos all clean.

No changeset (pacquet-only change). This is the pnpm → pacquet porting direction, so no TypeScript-side mirror is needed.


Written by an agent (Claude Code, claude-opus-4-8).

Summary by CodeRabbit

  • New Features

    • Added pacquet update (aliases: up, upgrade) with flags: --interactive, --latest, --save-exact, --no-save, --prod, --dev, --no-optional, --depth
    • --interactive prompts to select outdated direct deps
    • add/install now preserve existing lockfile pins by default during updates
  • Tests

    • New unit and integration tests covering update parsing, selection, and CLI behaviors

Port pnpm's `update` (aliases `up`/`upgrade`) onto pacquet's
always-fresh-resolve install path.

- Compatible bump: withhold matched names' lockfile pins from the
  preferred-versions seed (new `UpdateSeedPolicy`) so they re-resolve
  to highest-in-range; `package.json` is left untouched. This is what
  distinguishes `update` from `install`.
- `--latest`: fetch each matched direct dep's `latest` tag and rewrite
  the manifest range (`^v`, or exact under `--save-exact`), like `add`.
- Selectors: bare-name/glob patterns (`depth>0`, no `--latest`) match
  every locked package name at any depth; versioned (`foo@2`) or
  `--latest` selectors match direct deps only; `--latest` + spec is
  rejected with `ERR_PNPM_LATEST_WITH_SPEC`.
- CLI flags: `-L/--latest`, `-E/--save-exact`, `-P/-D/--no-optional`
  (faithful `makeIncludeDependenciesFromCLI`), `--depth`,
  `--lockfile-only`, `-i/--interactive` (dialoguer + inline outdated
  check).
- `--global` and `--workspace` error out for now: the global-dir and
  workspace-version-linking subsystems are not ported yet.

The resolver's `UpdateBehavior` tri-state and the npm resolver's
`include_latest_tag` already existed; this change drives them from a
CLI command.

Written by an agent (Claude Code, claude-opus-4-8).
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a full pacquet update command (incl. up/upgrade aliases), interactive outdated-selection via dialoguer, selector parsing, and an UpdateSeedPolicy to control which lockfile pins seed fresh resolution; wires policy through Install paths and adds unit/integration tests.

Changes

pacquet update implementation

Layer / File(s) Summary
UpdateSeedPolicy infrastructure and install wiring
Cargo.toml, pacquet/crates/cli/Cargo.toml, pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs, pacquet/crates/package-manager/src/install.rs, pacquet/crates/package-manager/src/add.rs, pacquet/crates/cli/src/cli_args/install.rs, pacquet/crates/package-manager/src/install/tests.rs, pnpr/crates/pnpr/src/install_accelerator/resolve.rs
Adds dialoguer workspace dependency. Introduces UpdateSeedPolicy (KeepAll, DropAll, DropOnly) and threads it into Install and InstallWithFreshLockfile, gating optimistic repeat-install fast-paths and computing seed snapshots; sets KeepAll at add/install/pnpr callsites and updates tests.
CLI command structure and registration
pacquet/crates/cli/src/cli_args.rs
Exports update and update_interactive modules; adds Update(UpdateArgs) to CliCommand with aliases up/upgrade; wires dispatcher to run update via existing reporter types.
Update CLI arguments and dependency options
pacquet/crates/cli/src/cli_args/update.rs
Adds UpdateDependencyOptions with include_direct() for --prod/--dev/--no-optional; defines UpdateArgs with packages, --latest, --save-exact, --no-save/--lockfile-only, --depth, --interactive, and platform selectors; includes unit tests for dependency-group logic.
UpdateArgs::run dispatching and interactive selection
pacquet/crates/cli/src/cli_args/update.rs, pacquet/crates/cli/src/cli_args/update_interactive.rs
Implements UpdateArgs::run (arg validation, supported-arch derivation, lockfile path), optional select_packages interactive flow that computes outdated direct deps by comparing lockfile versions to manifest-range or latest targets and prompts via dialoguer MultiSelect.
Core update runner and selector parsing
pacquet/crates/package-manager/src/lib.rs, pacquet/crates/package-manager/src/update.rs
Adds Update runner and Update::run: parse selectors (pnpm @ rules, negation), enforce --latest incompatibility with versioned specs, snapshot direct deps, compute UpdateSeedPolicy (drop-all/drop-only), optionally fetch registry latest and rewrite manifest in-memory, run Install with policy, and conditionally persist manifest; includes helpers and tests.
Comprehensive integration tests
pacquet/crates/cli/tests/update.rs
Integration tests covering update behaviors: within-range bumps, --latest rewrites, --save-exact, selectors (scoped/negation), --no-save lockfile-only updates, --depth errors, aliases (up), and spec rejection with --latest.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • pnpm/pnpm#11945: Also modifies optimistic repeat-install fast-path gating; related to update_seed_policy integration.
  • pnpm/pnpm#12077: pnpr lockfile-only resolution uses UpdateSeedPolicy::KeepAll similarly to this PR.
  • pnpm/pnpm#11784: Changes that interact with preferred-version seeding used by InstallWithFreshLockfile.

Suggested reviewers

  • KSXGitHub

Poem

🐰 I nibble at manifests, chase seeds of new fate,
I ask which deps are old, which versions await,
With prompts and with flags, I hop, choose, and cheer,
Rewrites and fresh resolves bring updates near,
A small rabbit’s joy when lockfiles are straight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat(pacquet): implement the update command' accurately and specifically describes the main change: the implementation of a new update command for the pacquet package manager.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pq-update

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 and usage tips.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.00      6.1±0.04ms   712.6 KB/sec    1.00      6.1±0.10ms   714.7 KB/sec

@codecov-commenter

codecov-commenter commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 64.00000% with 135 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.04%. Comparing base (577a90f) to head (55735de).

Files with missing lines Patch % Lines
...quet/crates/cli/src/cli_args/update_interactive.rs 0.00% 81 Missing ⚠️
pacquet/crates/package-manager/src/update.rs 81.95% 35 Missing ⚠️
pacquet/crates/cli/src/cli_args/update.rs 76.05% 17 Missing ⚠️
pacquet/crates/cli/src/cli_args.rs 66.66% 1 Missing ⚠️
...package-manager/src/install_with_fresh_lockfile.rs 93.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #12102      +/-   ##
==========================================
- Coverage   87.30%   87.04%   -0.27%     
==========================================
  Files         249      252       +3     
  Lines       27769    28142     +373     
==========================================
+ Hits        24245    24495     +250     
- Misses       3524     3647     +123     

☔ View full report in Codecov by Sentry.
📢 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.

…sing

Reviewing pnpm's `update` test suite surfaced gaps:

- Fix `parse_update_param` to match pnpm's `parseUpdateParam`: search
  for the version `@` starting at index 2 for `!`-negated patterns (1
  otherwise), so `!@scope/pkg-*` is no longer wrongly split into
  pattern `!` + version `scope/pkg-*`. Negation selectors now work.
- Add `--no-save`: the range rewrites still drive resolution (lockfile
  updates) but `package.json` is not persisted. Mirrors pnpm's
  `updatePackageManifest: opts.save !== false`.
- Add `ERR_PNPM_NO_PACKAGE_IN_DEPENDENCIES`: a selector that matches no
  direct dependency under `--depth 0` (without `--latest`) now errors,
  matching pnpm; `--latest` with an unmatched selector is a no-op.

Ports the negation-pattern, `--no-save`, and no-package-in-deps tests
from pnpm's `installing/commands/test/update/update.ts`.

Written by an agent (Claude Code, claude-opus-4-8).
@zkochan zkochan marked this pull request as ready for review June 1, 2026 12:38
Copilot AI review requested due to automatic review settings June 1, 2026 12:38
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Implement pacquet update command with compatible bump and latest version support

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Implement pacquet update command with up/upgrade aliases
• Support compatible bump (re-resolve to highest in-range) and --latest flag
• Add interactive mode with outdated dependency selection via dialoguer
• Support package selectors (bare names, globs, versioned) with depth filtering
• Implement CLI flags: -L/--latest, -E/--save-exact, -P/-D/--no-optional, --depth,
  --lockfile-only, -i/--interactive
Diagram
flowchart LR
  CLI["CLI: update/up/upgrade"]
  UpdateArgs["UpdateArgs parsing"]
  UpdateOp["Update operation"]
  Selector["Selector parsing<br/>bare/glob/versioned"]
  Latest["--latest flag<br/>fetch latest tag"]
  Compatible["Compatible bump<br/>UpdateSeedPolicy"]
  Install["Install with<br/>fresh resolve"]
  Manifest["Rewrite package.json<br/>if --save"]
  
  CLI --> UpdateArgs
  UpdateArgs --> UpdateOp
  UpdateOp --> Selector
  Selector --> Latest
  Selector --> Compatible
  Latest --> Manifest
  Compatible --> Install
  Manifest --> Install

Loading

Grey Divider

File Changes

1. pacquet/crates/cli/src/cli_args.rs ✨ Enhancement +10/-0

Add Update command to CLI enum

pacquet/crates/cli/src/cli_args.rs


2. pacquet/crates/cli/src/cli_args/update.rs ✨ Enhancement +189/-0

Implement UpdateArgs with dependency options

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


3. pacquet/crates/cli/src/cli_args/update/tests.rs 🧪 Tests +47/-0

Test dependency group inclusion logic

pacquet/crates/cli/src/cli_args/update/tests.rs


View more (13)
4. pacquet/crates/cli/src/cli_args/update_interactive.rs ✨ Enhancement +151/-0

Interactive outdated dependency selection

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


5. pacquet/crates/cli/tests/update.rs 🧪 Tests +224/-0

End-to-end tests for update command

pacquet/crates/cli/tests/update.rs


6. pacquet/crates/package-manager/src/update.rs ✨ Enhancement +406/-0

Core update operation with selector parsing

pacquet/crates/package-manager/src/update.rs


7. pacquet/crates/package-manager/src/update/tests.rs 🧪 Tests +52/-0

Unit tests for selector parsing logic

pacquet/crates/package-manager/src/update/tests.rs


8. pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs ✨ Enhancement +57/-1

Add UpdateSeedPolicy to control lockfile pin seeding

pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs


9. pacquet/crates/package-manager/src/install.rs ✨ Enhancement +21/-2

Thread UpdateSeedPolicy through install pipeline

pacquet/crates/package-manager/src/install.rs


10. pacquet/crates/package-manager/src/install/tests.rs 🧪 Tests +61/-0

Update install tests with UpdateSeedPolicy

pacquet/crates/package-manager/src/install/tests.rs


11. pacquet/crates/package-manager/src/add.rs ✨ Enhancement +5/-1

Set UpdateSeedPolicy::KeepAll for add command

pacquet/crates/package-manager/src/add.rs


12. pacquet/crates/package-manager/src/lib.rs ✨ Enhancement +2/-0

Export update module and UpdateSeedPolicy

pacquet/crates/package-manager/src/lib.rs


13. pnpr/crates/pnpr/src/install_accelerator/resolve.rs ✨ Enhancement +1/-0

Set UpdateSeedPolicy for pnpr resolve

pnpr/crates/pnpr/src/install_accelerator/resolve.rs


14. Cargo.toml Dependencies +1/-0

Add dialoguer dependency for interactive mode

Cargo.toml


15. pacquet/crates/cli/Cargo.toml Dependencies +2/-0

Add dialoguer and node-semver dependencies

pacquet/crates/cli/Cargo.toml


16. pacquet/crates/cli/src/cli_args/install.rs Additional files +3/-1

...

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


Grey Divider

Qodo Logo

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.457 ± 0.228 2.100 2.829 1.04 ± 0.18
pacquet@main 2.358 ± 0.349 2.031 3.158 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.4566408774200004,
      "stddev": 0.2275829329168382,
      "median": 2.5218089810200004,
      "user": 2.10212818,
      "system": 2.49490444,
      "min": 2.09966515202,
      "max": 2.82932251902,
      "times": [
        2.82932251902,
        2.6000103180200003,
        2.58340993902,
        2.2678382690200003,
        2.6067853430200003,
        2.4602080230200003,
        2.6084587000200004,
        2.27673856402,
        2.09966515202,
        2.23397194702
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.35753664512,
      "stddev": 0.3489274948616896,
      "median": 2.24000815802,
      "user": 2.0984539799999995,
      "system": 2.47609354,
      "min": 2.03081571402,
      "max": 3.1580120390200004,
      "times": [
        3.1580120390200004,
        2.06085648102,
        2.53113283802,
        2.36768578102,
        2.1082387280200003,
        2.03081571402,
        2.26750410602,
        2.1633057350200002,
        2.67530281902,
        2.2125122100200003
      ]
    }
  ]
}

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

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 806.3 ± 114.1 728.3 1103.9 1.07 ± 0.16
pacquet@main 750.5 ± 23.7 734.1 809.9 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.8062802473399999,
      "stddev": 0.11413541015969599,
      "median": 0.76622457294,
      "user": 0.2857632,
      "system": 1.0327461199999999,
      "min": 0.72830877794,
      "max": 1.10387462994,
      "times": [
        0.88408071394,
        0.72830877794,
        0.79174978994,
        0.75116673394,
        0.79639671294,
        0.74545707994,
        0.72931888894,
        0.75263400294,
        1.10387462994,
        0.77981514294
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.7505368386400002,
      "stddev": 0.02367250598092555,
      "median": 0.74256530594,
      "user": 0.27311589999999997,
      "system": 1.04466612,
      "min": 0.73414326594,
      "max": 0.80989705394,
      "times": [
        0.77321919394,
        0.80989705394,
        0.73414326594,
        0.7429471359400001,
        0.73629707694,
        0.74751090494,
        0.74218347594,
        0.74454786494,
        0.73490263794,
        0.73971977594
      ]
    }
  ]
}

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.454 ± 0.165 2.260 2.742 1.04 ± 0.09
pacquet@main 2.350 ± 0.132 2.241 2.594 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.4542280744600005,
      "stddev": 0.16530176023290175,
      "median": 2.39945773156,
      "user": 3.03911338,
      "system": 2.407408639999999,
      "min": 2.26002281006,
      "max": 2.7420432770600005,
      "times": [
        2.4629734930600002,
        2.26002281006,
        2.3246303470600003,
        2.39689334606,
        2.36583799406,
        2.4020221170600005,
        2.7420432770600005,
        2.31543201806,
        2.56849986906,
        2.70392547306
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.34975311766,
      "stddev": 0.13221839937672794,
      "median": 2.30795019006,
      "user": 3.01900108,
      "system": 2.3977972399999996,
      "min": 2.24084593806,
      "max": 2.59430741406,
      "times": [
        2.30549216706,
        2.35287892906,
        2.31040821306,
        2.58943219906,
        2.2793184400600004,
        2.59430741406,
        2.31912332506,
        2.24899032806,
        2.24084593806,
        2.25673422306
      ]
    }
  ]
}

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.413 ± 0.055 1.346 1.494 1.00
pacquet@main 1.494 ± 0.106 1.355 1.724 1.06 ± 0.09
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 1.4131425543199998,
      "stddev": 0.05458893394674749,
      "median": 1.41495722752,
      "user": 1.3574363,
      "system": 1.4672560000000001,
      "min": 1.34638586602,
      "max": 1.49416346802,
      "times": [
        1.34638586602,
        1.4147625400200001,
        1.44365742402,
        1.45029734802,
        1.38834800002,
        1.49416346802,
        1.41515191502,
        1.34836915702,
        1.3493344200200001,
        1.48095540502
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 1.4944150061200001,
      "stddev": 0.10626653054988991,
      "median": 1.48209901802,
      "user": 1.3635203000000002,
      "system": 1.4626891,
      "min": 1.35512796502,
      "max": 1.72371037702,
      "times": [
        1.37273677602,
        1.5164135250200002,
        1.42958888902,
        1.35512796502,
        1.72371037702,
        1.56014902702,
        1.49224517102,
        1.47195286502,
        1.55998059102,
        1.46224487502
      ]
    }
  ]
}

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12102
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
2,454.23 ms
(+2.62%)Baseline: 2,391.57 ms
2,869.88 ms
(85.52%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
1,413.14 ms
(-7.02%)Baseline: 1,519.81 ms
1,823.78 ms
(77.48%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
2,456.64 ms
(+15.94%)Baseline: 2,118.98 ms
2,542.77 ms
(96.61%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
806.28 ms
(+19.30%)Baseline: 675.87 ms
811.04 ms
(99.41%)
🐰 View full continuous benchmarking report in Bencher

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR ports pnpm’s update/up/upgrade behavior into pacquet by adding a dedicated Update operation and wiring it through the CLI, including support for compatible in-range bumps (via lockfile-seed withholding) and --latest manifest rewrites.

Changes:

  • Add pacquet update command (with up / upgrade aliases), selectors, --latest, --save-exact, --no-save, --depth, --lockfile-only, and interactive selection.
  • Introduce UpdateSeedPolicy and thread it through the fresh-resolve install path to control which lockfile pins seed preferred versions.
  • Add unit + e2e coverage for selector parsing, CLI include-group behavior, and end-to-end update semantics.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pnpr/crates/pnpr/src/install_accelerator/resolve.rs Passes update_seed_policy to installs initiated via pnpr.
pacquet/crates/package-manager/src/update.rs Implements the Update operation (selector parsing, seed policy selection, manifest rewrites, install invocation).
pacquet/crates/package-manager/src/update/tests.rs Unit tests for update selector parsing.
pacquet/crates/package-manager/src/lib.rs Exposes the new update module publicly.
pacquet/crates/package-manager/src/install.rs Threads UpdateSeedPolicy through install and bypasses optimistic repeat-install when updating.
pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs Defines UpdateSeedPolicy and applies it to lockfile preferred-version seeding.
pacquet/crates/package-manager/src/install/tests.rs Updates install tests to provide update_seed_policy.
pacquet/crates/package-manager/src/add.rs Ensures add uses UpdateSeedPolicy::KeepAll.
pacquet/crates/cli/tests/update.rs Adds e2e tests for pacquet update behaviors and aliases.
pacquet/crates/cli/src/cli_args/update.rs Adds CLI args for update and group-selection logic.
pacquet/crates/cli/src/cli_args/update/tests.rs Unit tests for dependency-group selection logic.
pacquet/crates/cli/src/cli_args/update_interactive.rs Implements --interactive selection using dialoguer.
pacquet/crates/cli/src/cli_args/install.rs Ensures install CLI sets update_seed_policy: KeepAll.
pacquet/crates/cli/src/cli_args.rs Registers the Update subcommand and its aliases.
pacquet/crates/cli/Cargo.toml Adds dialoguer and node-semver dependencies for interactive update.
Cargo.toml Adds workspace dependency on dialoguer.
Cargo.lock Locks new Rust dependencies introduced by interactive update.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pacquet/crates/cli/src/cli_args/update.rs
Comment thread pacquet/crates/cli/src/cli_args/update.rs
Comment thread pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
… filter

`UpdateSeedPolicy::DropOnly`'s snapshot filter allocated a `String` for
every lockfile snapshot key. Parse the (small) update-target set to
`PkgName` once and compare against `key.name` directly. Addresses a
review comment on #12102.

Written by an agent (Claude Code, claude-opus-4-8).

@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.

🧹 Nitpick comments (1)
pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs (1)

717-720: 💤 Low value

Consider logging when PkgName::parse fails.

Using .ok() silently drops names that fail to parse, meaning those packages won't be filtered from the seed (opposite of intended behavior). While unlikely given upstream validation, a debug log would help diagnose issues if a selector unexpectedly doesn't trigger an update.

💡 Optional: Add debug logging for parse failures
                     let drop: std::collections::HashSet<pacquet_lockfile::PkgName> = names
                         .iter()
-                        .filter_map(|name| pacquet_lockfile::PkgName::parse(name.as_str()).ok())
+                        .filter_map(|name| {
+                            match pacquet_lockfile::PkgName::parse(name.as_str()) {
+                                Ok(pkg_name) => Some(pkg_name),
+                                Err(err) => {
+                                    tracing::debug!(
+                                        target: "pacquet::install",
+                                        name = %name,
+                                        ?err,
+                                        "update selector did not parse as PkgName; ignoring",
+                                    );
+                                    None
+                                }
+                            }
+                        })
                         .collect();
🤖 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/package-manager/src/install_with_fresh_lockfile.rs` around
lines 717 - 720, The current collection into drop silently ignores parse errors
from pacquet_lockfile::PkgName::parse (used in the
names.iter().filter_map(...).collect()), so add a debug log when PkgName::parse
fails: replace the filter_map usage with an explicit match/if let that on Err(e)
emits a debug-level log (including the original name and error) and on Ok(name)
includes it in the HashSet; keep the rest of the logic unchanged so only parse
failures are recorded for diagnostics.
🤖 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.

Nitpick comments:
In `@pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs`:
- Around line 717-720: The current collection into drop silently ignores parse
errors from pacquet_lockfile::PkgName::parse (used in the
names.iter().filter_map(...).collect()), so add a debug log when PkgName::parse
fails: replace the filter_map usage with an explicit match/if let that on Err(e)
emits a debug-level log (including the original name and error) and on Ok(name)
includes it in the HashSet; keep the rest of the logic unchanged so only parse
failures are recorded for diagnostics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7d411f4a-0797-48c2-8fd3-dbbb5db0f91e

📥 Commits

Reviewing files that changed from the base of the PR and between 55735de and 17b1a24.

📒 Files selected for processing (1)
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: ubuntu-latest / Node.js 24 / Test
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Run benchmark on ubuntu-latest
🧰 Additional context used
📓 Path-based instructions (1)
pacquet/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/*.rs: Log emissions are part of matching pnpm — when porting a function that fires pnpm:<channel> events through globalLogger, logger.debug(...), or streamParser.write(...), mirror the call site, payload, and ordering so @pnpm/cli.default-reporter parses pacquet's NDJSON the same way
Declare a newtype wrapper for branded string types instead of collapsing the brand into a plain String or &str in Rust
If upstream TypeScript always validates before construction of a branded string, validate in the Rust wrapper too via TryFrom<String> and/or FromStr and do not provide an infallible public constructor
If upstream TypeScript never validates a branded string, just brand for type-safety in Rust by exposing an infallible From<String> constructor
If upstream TypeScript occasionally constructs a branded string without validation, expose from_str_unchecked in Rust as an escape hatch alongside the validating constructor
Match upstream serde behavior for branded strings crossing JSON, YAML, or INI boundaries by using #[serde(try_from = "String")] for deserialization and #[serde(into = "String")] for serialization
Derive simple conversions for branded strings using #[derive(derive_more::From)] and #[derive(derive_more::Into)] instead of handwriting impl blocks; use manual impl only when conversion needs custom logic
Model TypeScript string literal unions (like 'auto' | 'always' | 'never') as Rust enums instead of newtype wrappers, since the set of valid values is closed
Treat TypeScript string template literal types (like `${string}@${string}`) the same as branded string types in Rust, using a newtype wrapper with validation
Follow the code style guide in CODE_STYLE_GUIDE.md — imports, modules, naming, ownership and borrowing, parameter type selection, trait bounds, pattern matching, pipe-trait, error handling, test layout, and cloning of Arc and Rc
Choose owned vs. borrowed parameters to minimize copies; widen to t...

Files:

  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
🧠 Learnings (3)
📚 Learning: 2026-05-20T19:40:55.051Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11774
File: pacquet/crates/resolving-deps-resolver/src/resolve_peers.rs:0-0
Timestamp: 2026-05-20T19:40:55.051Z
Learning: In the pacquet Rust code, ensure the semver implementation uses the `node-semver` crate (not `nodejs-semver`). `node-semver`’s public API does not include a `satisfies_with_prerelease`-style method; prerelease-tolerant matching should be implemented inline by first calling `Range::satisfies`, and when it rejects a prerelease version, retry matching against a stripped `MAJOR.MINOR.PATCH` base of the prerelease version.

Applied to files:

  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
🔇 Additional comments (2)
pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs (2)

188-200: LGTM!

Also applies to: 171-171


721-726: LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants