Skip to content

refactor(pacquet/cli): split cli_args.rs#12690

Merged
zkochan merged 4 commits into
mainfrom
claude/refactor-cli-args-file-mxmj0s
Jun 27, 2026
Merged

refactor(pacquet/cli): split cli_args.rs#12690
zkochan merged 4 commits into
mainfrom
claude/refactor-cli-args-file-mxmj0s

Conversation

@KSXGitHub

@KSXGitHub KSXGitHub commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

pacquet/crates/cli/src/cli_args.rs had grown to ~1500 lines and mixed
several unrelated concerns in one file, which made it a frequent source of
merge conflicts. This PR splits it into focused submodules under
cli_args/, leaving cli_args.rs as a thin module index, and then further
breaks up the command-dispatch match.

1. Split cli_args.rs into focused submodules

The pub mod <subcommand>; declarations stay in cli_args.rs (they define
the module paths for the existing cli_args/<cmd>.rs files), but everything
else moves out:

File Contents
cli_args.rs module index + CliArgs re-export
cli_args/cli_command.rs the CliArgs struct and CliCommand enum
cli_args/dispatch.rs impl CliArgs — the install fast-path, completion handling, and the route dispatcher
cli_args/pipelines.rs the reporter-generic install / deploy / dedupe / prune pipelines
cli_args/package_manager.rs packageManager / devEngines parsing
cli_args/reporter.rs ReporterType and its emit / configure helpers

2. Split the dispatch match

dispatch.rs now holds a RunCtx context (the canonicalized dir, the
manifest path, the reporter, the --recursive flag, and the lazily-loaded
config / state closures) and a thin route match that wires each variant
to a handler. The per-command bodies move into three modules grouped by what
the command does:

File Commands
cli_args/dispatch_install.rs mutate the install graph: add, update, remove, install, deploy, dedupe, prune, fetch, import, link, unlink, rebuild, patch / patch-commit / patch-remove, dlx, create, runtime, approve-builds
cli_args/dispatch_query.rs read-only queries: outdated, audit, list, ll, why, whoami, dist-tag, ping, pack, root, config, pack-app, docs, store, cache, cat-file, cat-index, ignored-builds, find-hash
cli_args/dispatch_script.rs package.json script / lifecycle: init, test, run, exec, start, stop, restart, set-script

dispatch.rs drops from ~800 lines to ~280; the match itself is now a
~50-line router.

3. Two pnpm-parity fixes surfaced by review

Both were pre-existing in the original cli_args.rs (not refactor
regressions), found while reviewing the split:

  • i install alias — pnpm exposes commandNames = ['install', 'i'];
    pacquet lacked it. Added #[clap(visible_alias = "i")] so pacquet i
    dispatches to install.
  • scoped packageManager parsingparse_package_manager now mirrors
    pnpm's parser (split on the first @, skipping a leading scope @),
    instead of split_once('@') which mis-parsed @scope/pnpm@1.0.0. The
    first @ (not the last) is deliberate so a URL reference containing @
    stays intact.

Notes

  • The refactor itself is mechanical / behavior-preserving. The only
    substantive edits there are visibility widenings (to pub(crate) /
    pub(super)), the import collapsing required by the
    perfectionist::import-granularity dylint lint (one use per crate root),
    and one signature change: ApproveBuildsArgs::prepare now takes its
    config / state closures as &(dyn Fn(...) + Sync) so the async handler
    futures that capture those closures stay Send.
  • Verified for pacquet-cli: cargo check, cargo clippy -- --deny warnings, and cargo fmt --check are clean, and the lib tests
    (356, incl. the two new parity tests) pass. Import granularity was verified
    with rustfmt --config imports_granularity=Crate (and the CI Dylint job is
    green).

Squash Commit Body

refactor(cli): split cli_args.rs into focused submodules

cli_args.rs had grown to ~1500 lines mixing the CLI argument types, the
command-dispatch match, the install/deploy/dedupe/prune pipelines, reporter
wiring, and packageManager/devEngines parsing — a frequent merge-conflict
hotspot. Split it into focused submodules (cli_command, dispatch, pipelines,
package_manager, reporter), leaving cli_args.rs as a thin module index.

Then split the ~600-line dispatch match: dispatch.rs keeps a RunCtx context
and a thin route() match, and the per-command handler bodies move into three
modules grouped by behavior — dispatch_install (mutate the install graph),
dispatch_query (read-only queries), and dispatch_script (package.json
scripts).

The refactor is behavior-preserving (visibility widenings, import collapsing
for the import-granularity dylint lint, and a `+ Sync` bound on
ApproveBuildsArgs::prepare's closures so the shared config/state closures keep
the async handler futures Send). Two pre-existing pnpm-parity gaps surfaced by
review are also fixed: the `i` alias for install, and scope-aware
packageManager parsing.

Checklist

  • The change is implemented in both the TypeScript CLI and the Rust
    pacquet/ port, or the description notes what still needs porting.

    The refactor is pacquet-internal (nothing to port). The two parity
    fixes align pacquet with the existing TypeScript pnpm behavior, so no
    TS-side change is needed.
  • Added or updated tests.

    The refactor is a mechanical move; the existing cli_args tests moved
    with the code. Added install_command_parses_i_alias and
    parse_package_manager_handles_unscoped_scoped_and_url_references for the
    two parity fixes.

Written by an agent (Claude Code, Claude Opus 4.8).

🤖 Generated with Claude Code

https://claude.ai/code/session_01WLA4Nx1wDMQCaGzhcbXKVV

Summary by CodeRabbit

  • New Features

    • Expanded CLI support with more commands and subcommands, including package, workspace, script, store, and cache operations.
    • Added improved command handling for reporter selection, completion output, and script execution.
    • Added automatic package-manager version detection and syncing behavior for supported projects.
  • Bug Fixes

    • Improved command execution flow for install-related operations, including faster no-op handling and more consistent status output.
    • Refined parsing of package manager references for better compatibility with scoped names and version formats.

The single cli_args.rs file had grown to ~1500 lines and mixed several
unrelated concerns, making it a frequent source of merge conflicts. Split
it into focused submodules under cli_args/, leaving cli_args.rs as a thin
module index:

- cli_command.rs: the CliArgs and CliCommand argument types
- dispatch.rs:    the impl CliArgs command-dispatch logic
- pipelines.rs:   the install/deploy/dedupe/prune reporter-generic pipelines
- package_manager.rs: packageManager / devEngines parsing helpers
- reporter.rs:    ReporterType and its emit/configure helpers

No behavior change; the move is mechanical. Subcommand modules that adding
a new command still touches (the enum and the dispatch match) now live in
their own files, so unrelated changes to pipelines or package-manager
parsing no longer conflict with them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLA4Nx1wDMQCaGzhcbXKVV
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dfc9c018-c879-49f2-b0b9-ac36f8baf159

📥 Commits

Reviewing files that changed from the base of the PR and between ad1e670 and d1cdedd.

📒 Files selected for processing (3)
  • pacquet/crates/cli/src/cli_args/cli_command.rs
  • pacquet/crates/cli/src/cli_args/package_manager.rs
  • pacquet/crates/cli/src/cli_args/tests.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • pacquet/crates/cli/src/cli_args/cli_command.rs
  • pacquet/crates/cli/src/cli_args/package_manager.rs

📝 Walkthrough

Walkthrough

The CLI argument surface was split into dedicated modules for command definitions, reporter setup, package-manager sync detection, pipelines, and dispatch handlers. The main run path now uses shared context, completion handling, install fast-path execution, and per-command install/query/script routing.

Changes

CLI module split and orchestration

Layer / File(s) Summary
CLI facade and command surface
pacquet/crates/cli/src/cli_args.rs, pacquet/crates/cli/src/cli_args/cli_command.rs
The CLI facade re-exports CliArgs, and the command module defines the top-level parser flags plus the full CliCommand tree.
Reporter and package-manager sync
pacquet/crates/cli/src/cli_args/reporter.rs, pacquet/crates/cli/src/cli_args/package_manager.rs, pacquet/crates/cli/src/cli_args/tests.rs
ReporterType and its helpers are defined alongside package-manager manifest parsing, version resolution, lockfile persistence checks, and related tests.
Top-level dispatch and timing
pacquet/crates/cli/src/cli_args/dispatch.rs, pacquet/crates/cli/src/cli_args/approve_builds.rs
The main run path handles completion, install fast-path execution, shared config/state loading, command routing, and timing footer emission.
Install and maintenance pipelines
pacquet/crates/cli/src/cli_args/pipelines.rs
Shared config-root and CLI config helpers underpin the install, deploy, dedupe, and prune pipelines, which sync package-manager dependencies, run config-dependency hooks, and dispatch the command-specific args.
Install, query, and script handlers
pacquet/crates/cli/src/cli_args/dispatch_install.rs, pacquet/crates/cli/src/cli_args/dispatch_query.rs, pacquet/crates/cli/src/cli_args/dispatch_script.rs
The install-family handlers route reporter-specific futures or pipelines, the readonly handlers wire output and exit codes, and the script handlers execute package.json-driven commands with recursive handling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • pnpm/pnpm#12285: Both PRs wire config-dependency handling and updateConfig hooks into the install CLI path.
  • pnpm/pnpm#12431: Both PRs touch reporter selection and install execution footer plumbing in the CLI run path.
  • pnpm/pnpm#12540: Both PRs affect pnpm source version lookup used by current_source_pnpm_version and package_manager_to_sync.

Suggested labels

product: pacquet

Suggested reviewers

  • zkochan
✨ 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 claude/refactor-cli-args-file-mxmj0s

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.

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.00      6.7±0.21ms   647.2 KB/sec    1.00      6.7±0.40ms   646.8 KB/sec

@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

🤖 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/cli_command.rs`:
- Around line 108-109: Restore the install shortcut by ensuring
CliCommand::Install has the same alias/argv handling for "i" as before, so pnpm
i maps to install again. Update the CliCommand::Install variant in
cli_command.rs or the command parsing/rewrite logic that feeds it, and add or
adjust a test covering the pnpm i invocation so the shortcut behavior stays
locked in.

In `@pacquet/crates/cli/src/cli_args/package_manager.rs`:
- Around line 106-116: The packageManager parser in parse_package_manager
currently uses split_once('@'), which incorrectly breaks scoped packages like
`@pnpm/exe`@10.0.0 and prevents pnpm-matching behavior. Update
parse_package_manager to treat the last '@' as the version separator while
preserving the existing reference normalization logic (including the ':' guard
and '+' version trimming), so scoped names are parsed into the correct name and
optional reference.

In `@pacquet/crates/cli/src/cli_args/pipelines.rs`:
- Around line 196-213: The prune boundary check in pipelines::Config validation
is only lexical, so a relative escape like modules_dir pointing through ".." can
still bypass the workspace guard. Update the containment check around
cfg.modules_dir and config_root to first resolve/normalize the prune target
against the workspace root (or canonicalize both paths) before using
starts_with, so the final comparison in the prune pipeline reflects the real
filesystem location. Keep the safety behavior aligned with the existing prune
validation and the WorkspaceSettings::apply_to flow.
🪄 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: cf15d503-53e1-463f-a966-431db773ddf8

📥 Commits

Reviewing files that changed from the base of the PR and between de3ee84 and fd32c2f.

📒 Files selected for processing (7)
  • 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/package_manager.rs
  • pacquet/crates/cli/src/cli_args/pipelines.rs
  • pacquet/crates/cli/src/cli_args/reporter.rs
  • pacquet/crates/cli/src/cli_args/tests.rs

Comment thread pacquet/crates/cli/src/cli_args/cli_command.rs
Comment thread pacquet/crates/cli/src/cli_args/package_manager.rs Outdated
Comment thread pacquet/crates/cli/src/cli_args/pipelines.rs
@codecov-commenter

codecov-commenter commented Jun 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.97802% with 164 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.96%. Comparing base (de3ee84) to head (d1cdedd).

Files with missing lines Patch % Lines
pacquet/crates/cli/src/cli_args/package_manager.rs 62.12% 50 Missing ⚠️
pacquet/crates/cli/src/cli_args/dispatch_query.rs 73.54% 41 Missing ⚠️
pacquet/crates/cli/src/cli_args/pipelines.rs 68.25% 40 Missing ⚠️
...acquet/crates/cli/src/cli_args/dispatch_install.rs 92.83% 21 Missing ⚠️
pacquet/crates/cli/src/cli_args/dispatch.rs 92.90% 10 Missing ⚠️
pacquet/crates/cli/src/cli_args/dispatch_script.rs 97.95% 1 Missing ⚠️
pacquet/crates/cli/src/cli_args/reporter.rs 91.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #12690      +/-   ##
==========================================
+ Coverage   86.92%   86.96%   +0.04%     
==========================================
  Files         376      382       +6     
  Lines       57967    58161     +194     
==========================================
+ Hits        50386    50579     +193     
- Misses       7581     7582       +1     

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

The dylint `perfectionist::import-granularity` lint (style = "crate")
requires all imports sharing a crate root to be merged into a single
`use` with nested braces. The split introduced several files with one
`use super::x::Y;` per line, which the lint rejects. Collapse the
top-level imports in the new cli_args submodules accordingly.

This only affects module-level imports; function-body `use` statements
are left as-is (the lint does not flag them).
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

Commit: d1cdedde6882

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.448 ± 0.183 4.234 4.791 1.60 ± 0.10
pacquet@main 4.357 ± 0.095 4.231 4.511 1.57 ± 0.09
pnpr@HEAD 2.772 ± 0.139 2.634 3.078 1.00
pnpr@main 2.827 ± 0.135 2.661 3.034 1.02 ± 0.07
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 4.447792795700001,
      "stddev": 0.18275526009310783,
      "median": 4.4216461875,
      "user": 3.1564263200000005,
      "system": 2.69355482,
      "min": 4.2338434845,
      "max": 4.7911472985,
      "times": [
        4.3387218275,
        4.7037978015,
        4.5060775225,
        4.7911472985,
        4.4837955705,
        4.4156382415,
        4.3253193685,
        4.2338434845,
        4.4276541335,
        4.2519327085
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 4.3573895379,
      "stddev": 0.09534042186825586,
      "median": 4.3450163565,
      "user": 3.11221572,
      "system": 2.68461512,
      "min": 4.2308315875,
      "max": 4.5113799225,
      "times": [
        4.3160126475,
        4.2308315875,
        4.4701038745,
        4.5113799225,
        4.4423555445,
        4.2865208735,
        4.2847877455,
        4.3931828335,
        4.2647002845,
        4.3740200655
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 2.7723649518,
      "stddev": 0.13926223788651496,
      "median": 2.7459913030000003,
      "user": 2.15608792,
      "system": 2.29063022,
      "min": 2.6339354155,
      "max": 3.0777243005,
      "times": [
        2.8145266815000003,
        2.7914335975,
        3.0777243005,
        2.6653991425,
        2.9197551855,
        2.6339354155,
        2.7717508895000003,
        2.6441885155000002,
        2.6847040735,
        2.7202317165
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 2.8265720696,
      "stddev": 0.13549471201373717,
      "median": 2.819267559,
      "user": 2.1518121199999998,
      "system": 2.2817162200000003,
      "min": 2.6605615465000003,
      "max": 3.0340871155,
      "times": [
        3.0041397425,
        2.7087355215,
        2.6605615465000003,
        2.8736339375,
        2.8104075265,
        2.6973736265,
        2.7018305445,
        3.0340871155,
        2.8281275915000004,
        2.9468235435
      ]
    }
  ]
}

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

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 661.7 ± 59.1 606.1 737.1 1.00
pacquet@main 688.7 ± 106.6 614.1 945.4 1.04 ± 0.19
pnpr@HEAD 674.7 ± 48.6 646.6 811.0 1.02 ± 0.12
pnpr@main 736.7 ± 82.9 658.6 886.7 1.11 ± 0.16
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.66172196354,
      "stddev": 0.059068447446870356,
      "median": 0.62269658134,
      "user": 0.31228500000000003,
      "system": 0.9968055999999998,
      "min": 0.60609254134,
      "max": 0.73707992234,
      "times": [
        0.73707992234,
        0.60609254134,
        0.72451361534,
        0.62453763034,
        0.62085553234,
        0.61578014234,
        0.72983615134,
        0.72875969234,
        0.61434805034,
        0.61541635734
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.6887148209399998,
      "stddev": 0.10662663409789597,
      "median": 0.6243599773399999,
      "user": 0.3102204,
      "system": 1.0107624,
      "min": 0.61410262434,
      "max": 0.94536169334,
      "times": [
        0.62709467434,
        0.74462745434,
        0.74544247534,
        0.73095496334,
        0.62162528034,
        0.61410262434,
        0.62098911734,
        0.61647735534,
        0.62047257134,
        0.94536169334
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 0.6746956512400001,
      "stddev": 0.048602157268984675,
      "median": 0.66214945534,
      "user": 0.3194484,
      "system": 1.0508707999999998,
      "min": 0.64664181334,
      "max": 0.81101163534,
      "times": [
        0.81101163534,
        0.66986963234,
        0.67129434934,
        0.64664181334,
        0.6554904903400001,
        0.66221325034,
        0.66208566034,
        0.65577282334,
        0.64803563934,
        0.66454121834
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 0.73674742634,
      "stddev": 0.08288742324011034,
      "median": 0.7213200578400001,
      "user": 0.313863,
      "system": 1.0716912,
      "min": 0.65863031434,
      "max": 0.8866616003400001,
      "times": [
        0.8866616003400001,
        0.82566390534,
        0.67314482034,
        0.66166310434,
        0.76949529534,
        0.78337292134,
        0.66652793534,
        0.78033507834,
        0.66197928834,
        0.65863031434
      ]
    }
  ]
}

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 4.372 ± 0.036 4.321 4.421 1.45 ± 0.04
pacquet@main 4.367 ± 0.048 4.325 4.491 1.45 ± 0.04
pnpr@HEAD 3.190 ± 0.286 2.833 3.645 1.06 ± 0.10
pnpr@main 3.011 ± 0.078 2.853 3.116 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 4.372174682159999,
      "stddev": 0.03621498662470635,
      "median": 4.373118291259999,
      "user": 3.0117563999999994,
      "system": 2.57807866,
      "min": 4.32097334876,
      "max": 4.42067719076,
      "times": [
        4.42067719076,
        4.4057405337599995,
        4.369264525759999,
        4.38537686676,
        4.333578419759999,
        4.4153380877599995,
        4.37697205676,
        4.32607851576,
        4.32097334876,
        4.367747275759999
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 4.36711845886,
      "stddev": 0.04774616200077435,
      "median": 4.35254588676,
      "user": 3.0169043999999996,
      "system": 2.5885437599999994,
      "min": 4.32497560476,
      "max": 4.490834952759999,
      "times": [
        4.490834952759999,
        4.3359203507599995,
        4.34684501576,
        4.3677389797599995,
        4.32497560476,
        4.37698405176,
        4.3553096257599995,
        4.33449009576,
        4.38830376376,
        4.34978214776
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 3.19014051926,
      "stddev": 0.28640823314099706,
      "median": 3.15395868476,
      "user": 2.3306801999999998,
      "system": 2.50195616,
      "min": 2.83335520876,
      "max": 3.64535346876,
      "times": [
        2.9028590287600005,
        3.0310783327600004,
        2.93856126376,
        3.4106970977600004,
        2.99284047176,
        3.64535346876,
        3.52902485276,
        3.27683903676,
        2.83335520876,
        3.34079643076
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 3.0107072500600003,
      "stddev": 0.07808220919743038,
      "median": 3.01484328026,
      "user": 2.2531264,
      "system": 2.4638980599999996,
      "min": 2.8526531747600004,
      "max": 3.11598033476,
      "times": [
        2.98396406776,
        3.06202461576,
        2.95517767176,
        3.03958954376,
        2.99009701676,
        2.8526531747600004,
        3.11598033476,
        2.9577104237600005,
        3.0695993027600004,
        3.08027634876
      ]
    }
  ]
}

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.217 ± 0.046 1.181 1.344 1.00
pacquet@main 1.236 ± 0.056 1.189 1.390 1.02 ± 0.06
pnpr@HEAD 1.294 ± 0.087 1.243 1.539 1.06 ± 0.08
pnpr@main 1.303 ± 0.082 1.267 1.534 1.07 ± 0.08
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 1.2173982998800001,
      "stddev": 0.046434694261724765,
      "median": 1.2084169721800002,
      "user": 1.0478673400000003,
      "system": 1.3428523599999997,
      "min": 1.1808719541800001,
      "max": 1.34356061718,
      "times": [
        1.1894855561800002,
        1.19495789418,
        1.2058517911800002,
        1.34356061718,
        1.1918730691800001,
        1.22258994818,
        1.21993519618,
        1.1808719541800001,
        1.2138748191800002,
        1.21098215318
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 1.2360448218800002,
      "stddev": 0.05567251701808611,
      "median": 1.21891279518,
      "user": 1.05147264,
      "system": 1.35857646,
      "min": 1.18936041118,
      "max": 1.39003698518,
      "times": [
        1.21351817618,
        1.21603725718,
        1.39003698518,
        1.2406332561800002,
        1.2187585871800002,
        1.2182780531800002,
        1.2305520111800001,
        1.2242064781800002,
        1.18936041118,
        1.2190670031800002
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 1.29377457118,
      "stddev": 0.08683528164826192,
      "median": 1.26880941868,
      "user": 0.47109724000000003,
      "system": 1.23323196,
      "min": 1.24321835618,
      "max": 1.53892552218,
      "times": [
        1.2769735611800002,
        1.26401759118,
        1.24321835618,
        1.53892552218,
        1.2851129671800001,
        1.26241796418,
        1.26965755218,
        1.26910280918,
        1.26851602818,
        1.25980336018
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 1.3027630579799998,
      "stddev": 0.08178019920853473,
      "median": 1.2762448276800002,
      "user": 0.4703504399999999,
      "system": 1.23528336,
      "min": 1.2674990511800002,
      "max": 1.53404241918,
      "times": [
        1.2674990511800002,
        1.2759295021800001,
        1.27034117818,
        1.53404241918,
        1.29655454018,
        1.28443685418,
        1.2676618161800002,
        1.2765601531800002,
        1.28428594918,
        1.27031911618
      ]
    }
  ]
}

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

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.733 ± 0.096 2.672 2.985 2.15 ± 0.08
pacquet@main 2.788 ± 0.106 2.683 3.041 2.20 ± 0.09
pnpr@HEAD 1.270 ± 0.013 1.236 1.283 1.00
pnpr@main 1.292 ± 0.096 1.235 1.558 1.02 ± 0.08
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.73257122908,
      "stddev": 0.09612468063140177,
      "median": 2.69897953078,
      "user": 1.42091592,
      "system": 1.5559568,
      "min": 2.67205262178,
      "max": 2.98468350878,
      "times": [
        2.67205262178,
        2.70893233178,
        2.71415048678,
        2.68902672978,
        2.68236379778,
        2.70973631378,
        2.80181139178,
        2.68798497878,
        2.98468350878,
        2.6749701297799997
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.7883534810799993,
      "stddev": 0.10582113157462848,
      "median": 2.75880803828,
      "user": 1.41869622,
      "system": 1.5706883999999999,
      "min": 2.68343498078,
      "max": 3.04116522278,
      "times": [
        2.75327429478,
        2.74231415778,
        2.68741887178,
        2.7643417817800002,
        2.83474692478,
        2.71252260278,
        2.68343498078,
        2.83395655878,
        3.04116522278,
        2.8303594147799997
      ]
    },
    {
      "command": "pnpr@HEAD",
      "mean": 1.26957674558,
      "stddev": 0.013369905847460993,
      "median": 1.2712781187799997,
      "user": 0.47410682,
      "system": 1.2271338999999999,
      "min": 1.2364300317799999,
      "max": 1.28345058678,
      "times": [
        1.2692539087799999,
        1.28345058678,
        1.2761994857799999,
        1.2678293327799999,
        1.2733023287799998,
        1.26560749278,
        1.27790496378,
        1.2642467817799998,
        1.2364300317799999,
        1.28154254278
      ]
    },
    {
      "command": "pnpr@main",
      "mean": 1.29202259678,
      "stddev": 0.09646948115966539,
      "median": 1.2574064647799998,
      "user": 0.48208912000000004,
      "system": 1.2066268,
      "min": 1.23469731678,
      "max": 1.55800953978,
      "times": [
        1.2787816967799999,
        1.24474720078,
        1.2710480517799998,
        1.55800953978,
        1.31971029578,
        1.26201427878,
        1.25279865078,
        1.23469731678,
        1.2488660757799999,
        1.24955286078
      ]
    }
  ]
}

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12690
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,372.17 ms
(-8.11%)Baseline: 4,758.04 ms
5,709.65 ms
(76.58%)
isolated-linker.fresh-install.cold-cache.hot-store📈 view plot
🚷 view threshold
2,732.57 ms
(-10.41%)Baseline: 3,050.23 ms
3,660.28 ms
(74.65%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
1,217.40 ms
(-10.39%)Baseline: 1,358.57 ms
1,630.28 ms
(74.67%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
4,447.79 ms
(-7.43%)Baseline: 4,805.00 ms
5,766.00 ms
(77.14%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
661.72 ms
(+0.91%)Baseline: 655.72 ms
786.87 ms
(84.10%)
🐰 View full continuous benchmarking report in Bencher

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12690
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
3,190.14 ms
isolated-linker.fresh-install.cold-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
1,269.58 ms
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
1,293.77 ms
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
⚠️ NO THRESHOLD
2,772.36 ms
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
⚠️ NO THRESHOLD
674.70 ms
🐰 View full continuous benchmarking report in Bencher

`dispatch.rs` was dominated by one ~600-line `match` over every
`CliCommand` variant. Extract a `RunCtx` context (the canonicalized dir,
the manifest path, the reporter, the `--recursive` flag, and the lazily
loaded `config` / `state` closures as `&dyn Fn`) and move each command's
body into one of three handler modules grouped by what the command does:

- dispatch_install.rs: commands that mutate the install graph (add,
  update, remove, install, deploy, dedupe, prune, fetch, import, link,
  unlink, rebuild, patch*, dlx, create, runtime, approve-builds)
- dispatch_query.rs:   read-only queries (outdated, audit, list, why,
  whoami, dist-tag, ping, pack, root, config, pack-app, docs, store,
  cache, cat-file, cat-index, ignored-builds, find-hash)
- dispatch_script.rs:  package.json script / lifecycle commands (init,
  test, run, exec, start, stop, restart, set-script)

`dispatch.rs` keeps only `RunCtx`, the three `CliArgs` methods, and a thin
`route` match that wires each variant to its handler. No behavior change.

The `config` / `state` closures are shared with each handler as
`&dyn Fn(...) + Sync`; the `+ Sync` is required so the async handler
futures that capture them stay `Send`, and it is mirrored onto
`ApproveBuildsArgs::prepare`, which takes the same closures.
@KSXGitHub KSXGitHub marked this pull request as ready for review June 27, 2026 07:43
@KSXGitHub KSXGitHub requested a review from zkochan as a code owner June 27, 2026 07:43
@KSXGitHub KSXGitHub marked this pull request as draft June 27, 2026 07:45
@github-actions github-actions Bot added the reviewed: coderabbit CodeRabbit submitted an approving review label Jun 27, 2026
…ger parsing

Two pnpm-parity fixes surfaced by review of the cli_args split (both
pre-existing on the original cli_args.rs, not refactor regressions):

- Expose `i` as a visible alias of `install`, matching pnpm's
  `commandNames = ['install', 'i']`, so `pacquet i` dispatches to install.

- Parse the `packageManager` field the way pnpm does: split on the first
  `@`, except skip a leading scope `@` and split on the next one. The
  previous `split_once('@')` mis-parsed scoped names like
  `@scope/pnpm@1.0.0` into an empty name. The first `@` (not the last) is
  used deliberately so a reference that is a URL containing `@` (e.g.
  credentials) stays intact — mirroring pnpm's parsePackageManager.

Adds tests for the `i` alias and for parse_package_manager (unscoped,
scoped, `+`-hash, missing separator, and URL-with-`@` cases).
@KSXGitHub KSXGitHub marked this pull request as ready for review June 27, 2026 10:33
@zkochan zkochan merged commit 4e18e44 into main Jun 27, 2026
32 of 33 checks passed
@zkochan zkochan deleted the claude/refactor-cli-args-file-mxmj0s branch June 27, 2026 10:36
KSXGitHub pushed a commit that referenced this pull request Jun 27, 2026
Brings in 3 commits from main: the cli_args.rs split (#12690,
CLI-only), plus the bytes and tower-http dependency-version bumps. Only
Cargo.lock conflicted; resolved by taking main's lockfile and re-adding this
branch's network-web-auth subtree (crossterm, qrcode, open) via cargo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWejcxTU8n144a1KK4nRj8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

product: pacquet reviewed: coderabbit CodeRabbit submitted an approving review state: automerge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants