Skip to content

feat: implement numan use for side-by-side Nu version management - #67

Merged
tonythethompson merged 37 commits into
masterfrom
feature/numan-use
Aug 2, 2026
Merged

feat: implement numan use for side-by-side Nu version management#67
tonythethompson merged 37 commits into
masterfrom
feature/numan-use

Conversation

@tonythethompson

@tonythethompson tonythethompson commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements numan use for side-by-side Nu version management. The legacy-migration story (single-binary → versioned layout, journaled transition, doctor reconcile) lives on its own PR (#PR_MIGRATE_ID) so this PR stays small and reviewable.

Changes

Tracking: src/nu/version_manager.rs

Active version tracking + versioned-layout helpers for side-by-side Nu installs. The active Nu selector is stored at <root>/nu_state/active-version.json and the per-version binaries live at <root>/tools/nushell/<v>/nu. Helpers include listing installed versions, normalizing version strings, resolving the active binary (on-tree or off-tree via the marker), and clearing the active marker (cleared atomically before removing the versioned tree so the marker cannot dangle at a missing binary).

This file no longer carries the legacy single-binary migration fs in this PR — that work lands in #PR_MIGRATE_ID and the call site in cmd/use_cmd.rs is dormant until then.

Updated: src/nu/bootstrap.rs

  • install_from_archive() writes to <root>/tools/nushell/<version>/ (immutable, one dir per version).
  • managed_nu_binary() delegates to version_manager for active version lookup.
  • numan setup nu <x.y.z> persists the freshly installed version as the active version atomically (active-marker write sequenced after archive extraction).
  • Non-interactive numan setup nu is fail-closed without --yes, mirroring setup.rs; refuses to mutate state silently.
  • Hoisted the post-install PATH-mutation consent through a caller_consented_destructive flag on NuSetupOptions so setup.rs and bootstrap.rs only prompt once for the combined wipe + PATH-add.

New: numan use command (src/cmd/use_cmd.rs)

  • numan use <version> — switch to a specific installed version.
  • numan use latest — switch to the newest installed version.
  • numan use list — show installed versions + the active marker + per-version binary paths.
  • Validates the requested version is installed before flipping the active marker; errors with a hint to run setup nu <version> if it isn't. Does not auto-download.
  • Acquires the root mutation lock (so concurrent numan setup nu and numan use cannot interleave) and creates a PreMutation snapshot before any marker write.
  • The off-tree active-marker bridge: when numan setup nu use <path> switches to a user-supplied Nu outside the versioned layout, the marker records the resolved binary path so subsequent numan use list and find_nu_executable_with_root can resolve that version even if no on-tree install exists.

New: destructive-confirm + --yes gate in src/cmd/setup.rs

For numan setup nu use <path> / numan use existing, where the destructive step is wiping <root>/tools/nushell/ (every managed Nu version + active-version marker) before adding the resolved Nu's parent to PATH:

  • One merged prompt explicitly states both effects and includes the literal "there is no undo".
  • The prompt is gated on !args.yes && std::io::stdin().is_terminal() — non-interactive sessions refuse to commit destructive mutations without explicit --yes. An audit-grade eprintln! records whether the gate was an explicit --yes or an interactive confirmation.
  • Hoisted via caller_consented_destructive so setup.rs only prompts once for the combined wipe + PATH-add.

New: require_tty_or_yes (src/util/confirm.rs)

Hard-fail audit helper used by destructive numan setup flows. Refuses to run mutations in a non-TTY session without --yes; audit-grade eprintln! records the gate outcome. Other destructive prompts (remove_managed_nu, the loader overwrite) can adopt the same rubric in a follow-up.

docs/plans/consolidated-multi-repo-roadmap.md

  • Removes the "reserved stub" / "post-1.0" line for numan use.
  • Documents that numan use <version> errors (with a hint to run setup nu <version>) if the version isn't installed — it never auto-downloads.
  • Documents the active-marker as a JSON file at <root>/nu_state/active-version.json, atomic clear-before-tree-remove, and that PATH persistence is owned by numan setup nu while numan use only flips the active marker under the mutation lock with a PreMutation snapshot.

Testing

  • 442 unit tests pass on this branch (down from 468 once migration pieces are stripped; the 26 migration tests live on the migration branch).
  • cargo clippy -- -D warnings clean.
  • cargo fmt --check clean.
  • New end-to-end test coverage for the confirm-gate UX in src/cmd/setup.rs (DI seams via ExecuteUseOpts<'a>).

Architecture Notes

This implements the non-auto-installing shape of numan use: switching the active marker is fast and silent, but downloading a new Nu stays in numan setup nu --version <version>. If the requested version is not installed, the command errors with a hint to run setup nu first. This keeps numan use predictable for users (no surprise downloads) and matches the north-star product decision: Nu-compat onboarding flows through setup nu + try rather than use.

Future Work

  • Per-version activation sets in lockfile (Phase 5+)
  • Show which plugins are active/inactive after switch (Phase 5+ UX)
  • Activation reconciliation (post-1.0)
  • numan alias for Numan-level aliases (post-1.0)

tonythethompson and others added 7 commits July 31, 2026 22:20
…nfirm utility

Breaking change: --remove, --use-path, --use-existing are replaced by subcommands (remove, path, use <path>). Hidden backward-compat flags emit deprecation warnings (remove in v0.3.0).

- Add src/util/confirm.rs: confirm_or_auto / confirm_or_bail (non-TTY auto-confirms)

- Replace 16 inline TTY-check sites across 8 files

- Normalize all --yes help text to 'Skip confirmation prompts'

- Redesign NuSetupArgs with Option<NuAction> subcommand + positional VERSION

- Add NuSetupArgs constructors for internal callers (doctor, nu_pin_offer)

- Add 8 CLI-parse tests + update all affected test assertions

- Update README, CHANGELOG, docs/numan-doctor.md, AGENTS.md
- Prevent PATH subcommand from deleting active managed Nu
- Guard loader overwrites with ownership verification
- Reject incompatible legacy Nu setup flags
Fix remaining --version references in resolve.rs, tighten doctor_test assertion, add skip_path guard to legacy compat path, add negative tests for version+subcommand and legacy use_existing+skip_path.
Change 'your PATH Nu is not touched' to 'your existing Nu is not replaced' since setup nu does modify PATH by default.
Introduce a new `numan use <version>` command path (`cli`, `main`, and `cmd::use_cmd`) as a post-1.0 placeholder that currently fails with a clear guidance message to use `numan setup nu <version>`. Update roadmap/docs to reflect the consolidated plan filename, add post-1.0 side-by-side Nu management notes, and register the new command module in AGENTS metadata.
Fixed 4 file(s) based on 5 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
- Add src/nu/version_manager.rs for active version tracking
  - Active version stored in <root>/nu_state/active-version.json
  - Installed versions discovered from <root>/tools/nushell/<version>/
  - Helpers: read/write active version, list installed, check if installed
  - Migration logic for legacy single-binary installs

- Update bootstrap.rs to install to versioned subdirectories
  - install_from_archive() now writes to <root>/tools/nushell/<version>/
  - managed_nu_binary() delegates to version_manager for active version
  - Automatically sets newly installed version as active

- Implement numan use command
  - numan use <version> — switch to specific installed version
  - numan use latest — switch to newest installed version
  - numan use list — show all installed versions with active marker
  - Validates version is installed before switching
  - Provides helpful hints when version not found
  - Auto-migrates legacy single-binary installs on first run

- Remove Commands::Use from root init exclusion (now needs root)

All 425 tests pass, clippy clean, fmt applied.
Copilot AI review requested due to automatic review settings August 1, 2026 15:34

@sourcery-ai sourcery-ai Bot 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.

Sorry @tonythethompson, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

The PR adds side-by-side Nu installations, active-version persistence, legacy migration, and numan use list/latest/switch operations. Bootstrap resolves active versioned binaries, installs into version-specific paths, skips satisfied setups, and uses shared confirmation handling.

Changes

Nushell version management

Layer / File(s) Summary
Version storage, discovery, and migration
src/nu/mod.rs, src/nu/version_manager.rs
Adds active-version persistence, versioned binary paths, installation discovery, semver handling, and legacy binary migration.
Bootstrap installation and confirmation integration
src/nu/bootstrap.rs
Installs version-specific binaries, records active releases, resolves legacy paths, skips satisfied setups, and centralizes confirmation handling.
Active-version selection command
src/cmd/use_cmd.rs
Adds numan use support for listing versions, selecting the latest version, switching versions, and validating unavailable versions. Includes command tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: cursoragent

🚥 Pre-merge checks | ✅ 8
✅ Passed checks (8 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 60.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.
Pipeline Stage Enum Ordering ✅ Passed No SessionWorkflowStage enum or its members occur in tracked source; only unrelated AutoloadStage and LifecycleStage enums exist, so this ordering check is not applicable.
Gpu/Cpu Runtime Boundary ✅ Passed The PR changes only Rust files; no inference, managed requirements, Python main.py, or C# files are present or modified, so no runtime boundary check applies.
Managed Host Restart Safety ✅ Passed The PR diff contains no managed-host, containerized readiness, lease, busy-state, or host stop/restart code; the named components are absent, so this check is not applicable.
Title check ✅ Passed The title clearly and concisely describes the main change: adding numan use for side-by-side Nu version management.
Description check ✅ Passed The description directly explains the numan use command, version tracking, installation layout, confirmation gates, testing, and related changes.
✨ 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 feature/numan-use
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feature/numan-use

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

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add numan use and Nu version manager for side-by-side Nu installs

✨ Enhancement 📝 Documentation 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add numan use to switch/list latest installed managed Nu versions.
• Install managed Nu into versioned directories and persist active version marker.
• Centralize confirmation prompts; update setup nu subcommands, docs, and tests.
Diagram

graph TD
cli["CLI (numan)"] --> setup["setup.rs (setup nu)"] --> boot["nu/bootstrap.rs"] --> vm["nu/version_manager.rs"] --> state[("nu_state/active-version.json")]
cli --> usecmd["use_cmd.rs (numan use)"] --> vm
vm --> installs[("tools/nushell/<version>/nu")]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use a symlink `tools/nushell/active` instead of JSON marker
  • ➕ No JSON parsing; active binary resolution becomes a fixed filesystem path
  • ➕ Human-inspectable without opening state files
  • ➖ Windows symlink creation can require elevated privileges / policies
  • ➖ Harder to keep robust across filesystems and backup/restore scenarios
2. Keep `tools/nushell/nu` as a shim that forwards to a versioned binary
  • ➕ Minimizes downstream code changes that expect a single managed binary path
  • ➕ Can support richer activation logic later (logging, validation, migration)
  • ➖ Adds a wrapper executable/script that must be built/distributed correctly
  • ➖ Can complicate integrity checks and debugging (extra indirection)
3. Store active version in a broader config file (TOML) instead of `nu_state/`
  • ➕ Centralizes user-tunable settings and makes state/config separation explicit
  • ➕ Easier to extend with future features (aliases, per-version policies)
  • ➖ Requires defining/maintaining config schema and migration semantics now
  • ➖ Mixes operational state (active selection) into user config

Recommendation: The current JSON marker approach is a good fit for cross-platform reliability and low coupling: it keeps activation as pure state (no shims/symlinks) while enabling versioned installs. If future Phase 2 introduces richer per-version activation sets, consider whether the marker should expand (e.g., include install path hash/metadata) or move behind a stable config/state abstraction, but there’s no need to change now.

Files changed (31) +1336 / -300

Enhancement (8) +860 / -122
cli.rsAdd 'use' command to top-level CLI +2/-0

Add 'use' command to top-level CLI

• Extends the 'Commands' enum with a 'Use' variant wired to 'cmd::use_cmd::UseArgs'. This exposes 'numan use' in the CLI surface area.

src/cli.rs

mod.rsRegister 'use_cmd' module +1/-0

Register 'use_cmd' module

• Adds 'pub mod use_cmd;' so the new 'numan use' implementation is compiled and available to the CLI.

src/cmd/mod.rs

setup.rsRedesign 'setup nu' as subcommands + harden loader overwrites +303/-62

Redesign 'setup nu' as subcommands + harden loader overwrites

• Replaces action flags with 'NuAction' subcommands ('remove|path|use <path>') and adds constructors for internal callers. Implements hidden backward-compat flags with deprecation warnings, adds 'setup nu remove' behavior, and centralizes confirmation prompts; also adds ownership verification before overwriting 'loader.nu'.

src/cmd/setup.rs

use_cmd.rsImplement 'numan use' for active managed Nu switching +199/-0

Implement 'numan use' for active managed Nu switching

• Adds 'numan use' with 'list', 'latest', and explicit version switching, validating that requested versions are installed. Invokes legacy migration on entry and produces actionable error hints when versions are missing; includes unit tests for the new behavior.

src/cmd/use_cmd.rs

main.rsWire 'Commands::Use' to command execution +1/-0

Wire 'Commands::Use' to command execution

• Adds runtime dispatch so 'numan use' executes 'cmd::use_cmd::execute'. Completes the CLI plumbing for the new command.

src/main.rs

bootstrap.rsInstall Nu into versioned directories and resolve active binary +36/-60

Install Nu into versioned directories and resolve active binary

• Updates 'install_from_archive' to write into '<root>/tools/nushell/<version>/' and sets the active version marker. Changes 'managed_nu_binary()' to consult the version manager first, and refactors all interactive confirmations to the shared confirm utility.

src/nu/bootstrap.rs

mod.rsExpose Nu version manager module +1/-0

Expose Nu version manager module

• Adds 'pub mod version_manager;' to the Nu module tree so the new version tracking logic is available to bootstrap and commands.

src/nu/mod.rs

version_manager.rsAdd active-version marker + installed version discovery + legacy migration +317/-0

Add active-version marker + installed version discovery + legacy migration

• Introduces a module to read/write '<root>/nu_state/active-version.json', list installed versions by scanning version directories, and compute the active Nu binary path. Includes legacy single-binary migration by probing 'nu --version' and moving the binary into a versioned directory, plus unit tests.

src/nu/version_manager.rs

Refactor (9) +88 / -102
activate.rsAdopt shared confirmation utility for activation consent +10/-31

Adopt shared confirmation utility for activation consent

• Replaces inline TTY/'--yes' confirmation prompting with 'util::confirm::confirm_or_bail'. Updates tests to reflect the new policy: non-TTY sessions auto-confirm rather than failing.

src/cmd/activate.rs

deactivate.rsAdopt shared confirmation utility for deactivation consent +7/-23

Adopt shared confirmation utility for deactivation consent

• Replaces inline confirmation logic with 'util::confirm::confirm_or_bail' for consistent behavior with activation. Updates tests to validate non-TTY auto-confirm behavior.

src/cmd/deactivate.rs

doctor.rsUse new 'NuSetupArgs' constructors for doctor repairs +3/-21

Use new 'NuSetupArgs' constructors for doctor repairs

• Updates doctor repair execution to call 'NuSetupArgs::use_existing(...)' and 'NuSetupArgs::install(...)' rather than manually constructing legacy flag fields. Aligns help text to the new confirmation semantics.

src/cmd/doctor.rs

nu_pin_offer.rsSwitch pin offer path to new 'NuSetupArgs::install' constructor +1/-7

Switch pin offer path to new 'NuSetupArgs::install' constructor

• Refactors pinned installation invocation to use the new helper constructor, aligning with the redesigned 'setup nu' argument model.

src/cmd/nu_pin_offer.rs

snapshot.rsAdopt shared confirmation utility for snapshot rollback +2/-13

Adopt shared confirmation utility for snapshot rollback

• Replaces snapshot rollback’s bespoke non-TTY confirmation logic with 'util::confirm::confirm_or_bail', aligning confirmation behavior with the rest of the CLI.

src/cmd/snapshot.rs

try_cmd.rsNormalize confirmation help text and update setup hint messaging +3/-3

Normalize confirmation help text and update setup hint messaging

• Updates '--yes' help text and adjusts user-facing messaging to say the existing Nu is not replaced. Updates tests to match the new 'setup nu <version>' hint format.

src/cmd/try_cmd.rs

confirm.rsCentralize confirmation prompting with non-TTY auto-confirm policy +57/-0

Centralize confirmation prompting with non-TTY auto-confirm policy

• Adds 'confirm_or_auto' and 'confirm_or_bail' utilities to unify confirmation behavior across commands. Implements auto-confirm for non-interactive sessions with a stderr notice, plus minimal unit tests.

src/util/confirm.rs

hints.rsUpdate hint helpers for new 'setup nu' syntax +4/-4

Update hint helpers for new 'setup nu' syntax

• Updates helpers to emit 'numan setup nu <version>' and 'numan setup nu use <path>' commands instead of legacy flags. Keeps hint constants stable for call sites.

src/util/hints.rs

mod.rsExport confirm utility module +1/-0

Export confirm utility module

• Adds 'pub mod confirm;' to make the shared confirmation helpers available throughout the codebase.

src/util/mod.rs

Tests (3) +136 / -49
activate_test.rsUpdate activation integration test for non-TTY auto-confirm +8/-11

Update activation integration test for non-TTY auto-confirm

• Replaces the prior expectation that activation fails in non-TTY without '--yes' with a new expectation that it proceeds via auto-confirm. Keeps the TTY-guarded skip for interactive runs.

tests/activate_test.rs

doctor_test.rsAdjust doctor tests for 'NuAction::Use' and new fix strings +10/-4

Adjust doctor tests for 'NuAction::Use' and new fix strings

• Updates the test stub to assert doctor passes off-PATH Nu via 'NuAction::Use { path }' rather than deprecated flags. Updates fix-string assertions to match 'setup nu use' output.

tests/doctor_test.rs

setup_nu_test.rsAdd CLI-parse tests for 'setup nu' subcommands and compat flags +118/-34

Add CLI-parse tests for 'setup nu' subcommands and compat flags

• Refactors existing tests to use 'NuSetupArgs' constructors and adds a suite of clap parsing tests for bare install, pinned version, subcommands, and hidden legacy flags. Adds negative tests for invalid argument combinations (e.g., version alongside a subcommand).

tests/setup_nu_test.rs

Documentation (10) +250 / -27
AGENTS.mdUpdate command map for 'setup nu' redesign and 'use' reservation +6/-5

Update command map for 'setup nu' redesign and 'use' reservation

• Updates the documented CLI/module inventory to reflect 'setup nu' subcommands and the addition of 'use_cmd.rs'. Adjusts wording in learned workspace facts to reference the new pinned-version syntax.

AGENTS.md

CHANGELOG.mdDocument breaking 'setup nu' CLI redesign and non-TTY auto-confirm +15/-0

Document breaking 'setup nu' CLI redesign and non-TTY auto-confirm

• Adds Unreleased entries describing the switch from action flags to 'setup nu' subcommands, including the deprecation plan for hidden legacy flags. Documents the new confirmation policy where non-TTY sessions auto-confirm and print a notice.

CHANGELOG.md

CLAUDE.mdAdd agent/tooling workflow guidance for Claude Code +156/-0

Add agent/tooling workflow guidance for Claude Code

• Introduces a new contributor/agent instructions document focusing on tool selection and safe editing workflow. Clarifies preferred semantic tooling and operational safety expectations.

CLAUDE.md

README.mdRefresh CLI docs for 'setup nu' subcommands and pinned version syntax +6/-2

Refresh CLI docs for 'setup nu' subcommands and pinned version syntax

• Updates documentation and command tables to use 'numan setup nu <VERSION>' instead of '--version'. Adds explicit rows for 'setup nu remove|path|use <path>' and documents '--force/--skip-path/--yes' flags for 'setup nu'.

README.md

numan-doctor.mdUpdate doctor guidance for new 'setup nu' subcommands +4/-4

Update doctor guidance for new 'setup nu' subcommands

• Replaces references to legacy '--use-existing' and '--yes' guidance with 'setup nu use <path>' and the new confirmation policy. Keeps the doctor repair/check ordering intact while updating command examples.

docs/numan-doctor.md

2026-07-29-remaining-roadmap.mdFix consolidated plan link target +1/-1

Fix consolidated plan link target

• Updates a roadmap document link to point at the renamed consolidated plan file. No functional/runtime impact.

docs/plans/2026-07-29-remaining-roadmap.md

consolidated-multi-repo-roadmap.mdExpand post-1.0 plan notes for side-by-side Nu version management +50/-3

Expand post-1.0 plan notes for side-by-side Nu version management

• Adds a detailed post-1.0 feature section describing the intended shape of side-by-side Nu version management ('numan use') and related activation/alias concepts. Also removes an outdated status-date header from the top.

docs/plans/consolidated-multi-repo-roadmap.md

handoff-promote-0114-plugins.mdUpdate roadmap path reference +1/-1

Update roadmap path reference

• Adjusts a Windows path reference to the renamed consolidated plan file. Purely documentation/ops guidance change.

docs/plans/handoff-promote-0114-plugins.md

nupm.rsNormalize '--yes' help text for import +1/-1

Normalize '--yes' help text for import

• Updates CLI help text for '--yes' to match the project-wide phrasing (“Skip confirmation prompts”). No behavioral change.

src/cmd/nupm.rs

resolve.rsUpdate Nu mismatch remediation to new setup syntax and messaging +10/-10

Update Nu mismatch remediation to new setup syntax and messaging

• Updates remediation text to recommend 'numan setup nu <VERSION>' and clarifies that existing Nu is not replaced. Adjusts associated tests to match updated messaging.

src/core/resolve.rs

Other (1) +2 / -0
loader.nuMark loader.nu as generated/managed by Numan +2/-0

Mark loader.nu as generated/managed by Numan

• Adds header lines indicating the loader file is generated/managed and declares an autoload schema version. Keeps the vendored loader attribution unchanged.

assets/nushell-loader/loader.nu

This comment was marked as resolved.

@qodo-code-review

qodo-code-review Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (2) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 17 rules
✅ REVIEW.md

Grey Divider


Action required

1. Version path traversal risk ✓ Resolved 🐞 Bug ⛨ Security
Description
Version strings are used directly as path components for install directories and active-binary
resolution, so values like "../…" or absolute paths can escape $NUMAN_ROOT/tools/nushell/ and
later cause Numan to reference or execute an unintended binary. This breaks the managed-root
boundary and can overwrite files outside the root during installation.
Code

src/nu/version_manager.rs[R63-71]

+pub fn version_install_dir(root: &Path, version: &str) -> PathBuf {
+    versioned_nu_dir(root).join(version)
+}
+
+/// Binary path for a specific Nu version.
+pub fn version_binary(root: &Path, version: &str) -> PathBuf {
+    let binary_name = if cfg!(windows) { "nu.exe" } else { "nu" };
+    version_install_dir(root, version).join(binary_name)
+}
Relevance

●●● Strong

Security boundary issue; validating version strings before path-join is straightforward and usually
accepted.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The PR adds a versioned install layout where version_install_dir() blindly appends the provided
version to <root>/tools/nushell/, and callers use that to create directories/copy binaries and
later resolve the active binary path. This allows a version string to influence filesystem locations
outside the intended root.

src/nu/version_manager.rs[57-86]
src/nu/version_manager.rs[62-71]
src/nu/version_manager.rs[121-124]
src/nu/bootstrap.rs[197-241]
src/nu/bootstrap.rs[289-309]
src/cmd/use_cmd.rs[72-100]

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

### Issue description
Nu version identifiers are currently used directly as filesystem path components (e.g., `<root>/tools/nushell/<version>/nu`). Without validation, a crafted version containing path separators, `..`, or an absolute path can escape the intended directory and later be treated as the active Nu binary.

### Issue Context
- `version_install_dir()` joins `version` into the managed directory.
- `install_from_archive()` uses `version_install_dir()` / `version_binary()` to create directories and copy binaries.
- `numan use <version>` can write arbitrary strings into the active marker, and downstream code resolves that into a binary path.

### Fix Focus Areas
- src/nu/version_manager.rs[57-124]
- src/nu/bootstrap.rs[197-241]
- src/cmd/use_cmd.rs[72-100]

### Implementation guidance
- Introduce a strict parser/validator for Nu versions (recommended: accept `vX.Y.Z` and normalize to `X.Y.Z`; otherwise error).
- Reject any version containing path separators (`/` or `\\`), `..`, or absolute paths.
- After building the install path, defensively ensure it is still under `versioned_nu_dir(root)` (e.g., via canonicalization when possible).
- Ensure `write_active_version()` only persists normalized/validated versions, and `numan use` rejects invalid versions early.

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



Remediation recommended

2. Migration errors are dropped ✓ Resolved 🐞 Bug ☼ Reliability
Description
numan use runs legacy-install migration but discards any error, so failures (e.g., legacy `nu
--version` execution or parse failures) are silently ignored and users can get misleading “No Nu
versions installed” outcomes. This loses critical diagnostics during upgrade from the legacy layout.
Code

src/cmd/use_cmd.rs[R21-25]

+pub fn execute(args: &UseArgs, root: &Path) -> Result<()> {
+    // Attempt migration of legacy single-binary install before any operation.
+    // This is a no-op if migration has already occurred or no legacy install exists.
+    let _ = version_manager::migrate_legacy_install(root);
+
Relevance

●●● Strong

Ignoring migration errors loses diagnostics; small reliability fix to propagate/log errors is
typically accepted.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The call site explicitly discards the migration result, while the migration implementation has
multiple error paths (including explicit bail!) that would otherwise explain why legacy state
could not be migrated.

src/cmd/use_cmd.rs[21-30]
src/nu/version_manager.rs[138-179]
src/nu/version_manager.rs[181-209]

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

### Issue description
`use_cmd::execute()` ignores the `Result` from `migrate_legacy_install()`, hiding real failures in migration and causing confusing follow-on behavior.

### Issue Context
`migrate_legacy_install()` can fail when:
- executing the legacy binary,
- parsing its version output,
- creating the target directory,
- renaming the binary.

### Fix Focus Areas
- src/cmd/use_cmd.rs[21-30]
- src/nu/version_manager.rs[132-210]

### Implementation guidance
- Replace `let _ = migrate_legacy_install(root);` with `migrate_legacy_install(root).with_context(...)?;`.
- If you truly want best-effort migration, log a warning to stderr on error and continue, but do not silently discard it.

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


3. Marker invalid handling mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
read_active_version() claims it returns None when the marker is invalid, but it actually returns
an error on JSON parse failure, which can break numan use list/latest if the marker is corrupted.
This is a contract mismatch between documentation and behavior.
Code

src/nu/version_manager.rs[R25-37]

+/// Read the active Nu version from the marker file.
+///
+/// Returns `None` if no active version is set (marker doesn't exist or is invalid).
+pub fn read_active_version(root: &Path) -> Result<Option<ActiveVersion>> {
+    let path = active_version_path(root);
+    if !path.exists() {
+        return Ok(None);
+    }
+    let content = std::fs::read_to_string(&path)
+        .with_context(|| format!("Failed to read active version from '{}'", path.display()))?;
+    let active: ActiveVersion = serde_json::from_str(&content)
+        .with_context(|| format!("Failed to parse active version from '{}'", path.display()))?;
+    Ok(Some(active))
Relevance

●●● Strong

Doc/behavior contract mismatch; deterministic change to return Ok(None) on invalid marker is
low-risk.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The function’s doc comment promises None for invalid content, but the implementation wraps and
returns the parse error instead of returning Ok(None).

src/nu/version_manager.rs[25-38]

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

### Issue description
`read_active_version()` documents “Returns None if marker is invalid” but propagates JSON parse errors instead.

### Issue Context
This file is user-local state (`<root>/nu_state/active-version.json`) and can be corrupted; list/switch commands should either degrade gracefully or document that corruption is fatal.

### Fix Focus Areas
- src/nu/version_manager.rs[25-38]

### Implementation guidance
Choose one:
- If invalid should be non-fatal (matches current docs): catch `serde_json::from_str` errors and return `Ok(None)` (optionally `eprintln!` a warning).
- If invalid should be fatal: update the doc comment and ensure callers surface a clear remediation message (e.g., delete the marker / rerun setup).

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


4. legacy_binary.parent().unwrap() may panic ✓ Resolved 📘 Rule violation ☼ Reliability
Description
migrate_legacy_install() uses unwrap() on legacy_binary.parent(), which can panic and crash
callers if the path ever lacks a parent. Library code must return errors instead of panicking.
Code

src/nu/version_manager.rs[207]

+    let _ = std::fs::remove_dir(legacy_binary.parent().unwrap());
Relevance

●●● Strong

Unwrap panic in library code violates project rule; replacing with error handling is a small, clear
fix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The checklist forbids panic triggers like unwrap() in non-test library code. The PR adds an
unwrap() call in migrate_legacy_install() when removing the legacy directory.

Rule 2436700: Library code must not panic; it must return errors instead
src/nu/version_manager.rs[206-208]

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

## Issue description
`src/nu/version_manager.rs` uses `legacy_binary.parent().unwrap()` in non-test code, which can panic.

## Issue Context
Compliance requires library code to avoid panic triggers like `unwrap()`/`expect()` and return errors instead.

## Fix Focus Areas
- src/nu/version_manager.rs[206-208]

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


View more (1)
5. setup nu path persists PATH 📘 Rule violation ≡ Correctness
Description
The new setup nu path/setup nu use flows call register_existing_nu(), which persists PATH
changes (e.g., PowerShell user PATH update on Windows), writing outside $NUMAN_ROOT. Install-time
code must not modify locations outside $NUMAN_ROOT.
Code

src/cmd/setup.rs[R231-239]

+    remove_managed_nu_if_present(root)?;
    let options = NuSetupOptions {
-        yes: args.yes,
-        force: args.force,
-        skip_path: args.skip_path,
-        version: args.version.clone(),
+        yes,
+        force: false,
+        skip_path: false,
+        version: None,
    };
-    if let Some(existing) = &args.use_existing {
-        bootstrap::register_existing_nu(existing, &options)?;
+    bootstrap::register_existing_nu(Path::new(&path_nu), &options)?;
+    Ok(())
Relevance

●● Moderate

Repo already accepts PATH-persisting register_existing_nu, but “don’t write outside root” policy
conflicts.

PR-#35
PR-#66

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The checklist forbids install-time code from writing outside $NUMAN_ROOT. The PR adds new code
paths that invoke register_existing_nu(), and that function persists PATH changes (including a
Windows PowerShell call that updates the user Path env var).

Rule 2436679: Install code must not invoke Nu integration APIs
src/cmd/setup.rs[231-251]
src/nu/bootstrap.rs[441-472]
src/nu/bootstrap.rs[496-512]

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

## Issue description
`setup nu path`/`setup nu use` invoke `register_existing_nu()`, which persists PATH updates (outside `$NUMAN_ROOT`).

## Issue Context
Compliance requires install-time code paths to avoid writes/modifications outside `$NUMAN_ROOT` (e.g., user PATH, home dir, system directories).

## Fix Focus Areas
- src/cmd/setup.rs[231-251]
- src/nu/bootstrap.rs[441-472]
- src/nu/bootstrap.rs[496-512]

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



Informational

6. Use guidance uses deprecated flag ✓ Resolved 🐞 Bug ≡ Correctness
Description
numan use still tells users to run numan setup nu --version ..., but this PR redesigns setup to
accept a positional version (numan setup nu <VERSION>). These messages appear on the common
empty-install and version-not-installed paths, so users will be instructed to run a non-existent
flag.
Code

src/cmd/use_cmd.rs[R38-41]

+    if versions.is_empty() {
+        println!("No Nu versions installed.");
+        println!("Run 'numan setup nu' or 'numan setup nu --version <version>' to install.");
+        return Ok(());
Relevance

●●● Strong

Trivial user-facing hint mismatch; PR #66 moved CLI to positional version so message should be
updated.

PR-#66

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The use command messages reference --version, while the shared hints helper now formats `numan
setup nu <ver>`, demonstrating the intended new CLI shape.

src/cmd/use_cmd.rs[38-41]
src/cmd/use_cmd.rs[72-93]
src/util/hints.rs[21-27]

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

### Issue description
`use_cmd` emits outdated setup guidance using `--version`, which no longer matches the new `setup nu` CLI.

### Issue Context
The PR changes setup syntax (see `hints::setup_nu_version()`), but `use_cmd` hard-codes old strings.

### Fix Focus Areas
- src/cmd/use_cmd.rs[38-41]
- src/cmd/use_cmd.rs[64-68]
- src/cmd/use_cmd.rs[77-93]
- src/util/hints.rs[21-27]

### Implementation guidance
- Replace hard-coded `setup nu --version ...` strings with `hints::setup_nu_version(version)` (or update to `numan setup nu <version>`).
- Ensure the empty-install and not-installed branches print the correct new syntax.

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


7. Hidden legacy flags compatibility shim 📘 Rule violation ⚙ Maintainability
Description
NuSetupArgs introduces hidden legacy flags (--remove, --use-path, --use-existing) and
translation logic, adding an unrequested compatibility shim. This increases maintenance burden and
contradicts the requirement to exclude compatibility shims when not explicitly requested.
Code

src/cmd/setup.rs[R162-180]

+    // COMPAT: remove in v0.3.0 — translate hidden legacy flags to subcommands
+    if args.remove {
+        eprintln!("warning: --remove is deprecated, use 'numan setup nu remove' instead");
+        return remove_managed_nu(root, args.yes);
+    }
+    if args.use_path {
+        eprintln!("warning: --use-path is deprecated, use 'numan setup nu path' instead");
+        return execute_use_path(args.yes, root);
+    }
+    if let Some(existing) = &args.use_existing {
+        eprintln!("warning: --use-existing is deprecated, use 'numan setup nu use <path>' instead");
+        if args.skip_path {
+            bail!(
+                "numan setup nu use cannot be combined with --skip-path. \
+                 Off-PATH registration must persist the binary directory to PATH."
+            );
+        }
+        return execute_use_existing(existing, args.yes, root);
+    }
Relevance

● Weak

PR #66 intentionally added hidden legacy flags + translation; team merged it despite shim concerns.

PR-#66

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The checklist prohibits introducing compatibility shims unless explicitly requested. The PR adds
hidden legacy flags and explicit translation branches marked COMPAT to preserve old behavior.

Rule 2452627: Exclude unrequested feature flags and compatibility shims from change sets
src/cmd/setup.rs[53-60]
src/cmd/setup.rs[162-180]

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

## Issue description
The PR adds backward-compatibility shims (hidden legacy flags and translation branches) for `numan setup nu`.

## Issue Context
Compliance requires excluding unrequested feature flags and compatibility shims from change sets unless explicitly required by a linked ticket/spec.

## Fix Focus Areas
- src/cmd/setup.rs[53-60]
- src/cmd/setup.rs[162-180]

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


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

qodo-code-review[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@greptile-apps

greptile-apps Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR implements numan use for side-by-side Nu version management: a new version_manager module tracks the active version in <root>/nu_state/active-version.json, install_from_archive writes into a per-version layout (tools/nushell/<version>/nu), and the numan use list|latest|<version> command flips the active marker under the mutation lock with a pre-mutation snapshot.

  • src/nu/version_manager.rs (new): active-version JSON marker CRUD, path-traversal-safe normalize_version, list_installed_versions with off-tree augmentation from the active marker, and resolve_installed_version that falls through to the off-tree binary_path when the on-tree binary is absent.
  • src/cmd/setup.rs + src/nu/bootstrap.rs: destructive-step consent is consolidated into one merged prompt covering both managed-tree deletion and PATH mutation; require_tty_or_yes guards prevent silent destructive mutations in non-interactive sessions; the active-version marker is written after archive extraction and cleared before remove_dir_all.
  • src/nu/paths.rs: find_nu_executable_with_root gains a 3-step marker hint table (on-tree versioned binary → off-tree recorded path → full version scan) so all installed versions are discoverable without a PATH lookup.

Confidence Score: 4/5

Mostly safe to merge; one defect in the install fast-path leaves the active-version marker out of sync with the session PATH when a pinned version is already on disk.

The already-installed early return in execute_nu_setup_with_installer prepends the already-installed version directory to the current session PATH but does not call write_active_version, leaving the marker pointing at the previously active version. A user who runs numan setup nu 0.113.1 --yes when 0.113.1 is already installed but 0.112.0 is active will see 'already installed (unchanged)', have 0.113.1 on their session PATH, yet have numan use list show 0.112.0 as active.

Files Needing Attention: src/nu/bootstrap.rs — the execute_nu_setup_with_installer already-installed early-return path needs a write_active_version call for the pinned-version case before the return Ok(dest.clone()) at line 730.

Important Files Changed

Filename Overview
src/nu/bootstrap.rs Refactored to write versioned layout, persist active-version marker post-install, and hoist PATH-consent flag. Contains a P1 bug: the --yes already-installed early-return skips the active-marker write, leaving marker/PATH out of sync for the pinned-version case.
src/nu/version_manager.rs New file implementing the side-by-side version management core: active-version JSON marker read/write/clear, versioned binary path helpers, normalized-version validation (path-traversal safe), and list/resolve APIs with off-tree fallback. Well-tested; note previously-flagged migration re-entry guard issue.
src/cmd/use_cmd.rs New numan use command implementing list/latest/switch subcommands with mutation lock and PreMutation snapshot; previously-flagged hint text still references deprecated --version flag in error messages.
src/cmd/setup.rs Adds consolidated destructive-step confirm gate (merged deletion + PATH prompt) for execute_use_path/execute_use_existing, require_tty_or_yes guard before managed-tree removal, ExecuteUseOpts DI seam for tests, and clears active-version marker before remove_dir_all.
src/util/confirm.rs Adds require_tty_or_yes / require_tty_or_yes_with_tty hard-fail guard for destructive non-interactive sessions, and extracts TTY injection seam (confirm_or_auto_with_tty) for deterministic test coverage.
src/nu/paths.rs Extends find_nu_executable_with_root with a 3-step active-marker hint table (on-tree versioned → off-tree recorded path → full version scan fallback), updates error hint to new subcommand syntax, and changes validate_nushell_binary to return the version string.
src/util/hints.rs Adds POSIX shell_quote helper to make setup_nu_use_existing copy-pasteable for paths with spaces or shell metacharacters; well-tested. Note: single-quote wrapping is not valid on Windows cmd/PowerShell.

Reviews (14): Last reviewed commit: "Merge branch 'master' into feature/numan..." | Re-trigger Greptile

greptile-apps[bot]

This comment was marked as resolved.

- Validate and normalize Nu versions
- Propagate legacy migration errors
- Handle invalid active markers gracefully
- Avoid parent path panic
- Update Nu setup guidance
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo Fixer

✅ Committed (5) · ☑ Fixed (5)

Grey Divider

Commits pushed directly to this PR — no separate fix PR opened.

Process — 5 fixed
  • ☑ Fixed: Version path traversal risk
  • ☑ Fixed: Migration errors are dropped
  • ☑ Fixed: Marker invalid handling mismatch
  • ☑ Fixed: legacy_binary.parent().unwrap() may panic
  • ☑ Fixed: Use guidance uses deprecated flag
  • ⏭ Skipped (2)

coderabbitai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

tonythethompson and others added 2 commits August 1, 2026 20:11
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cmd/setup.rs (1)

374-384: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Require explicit confirmation for non-TTY configuration changes.

The shared confirmation path now auto-confirms when standard input is not a terminal. A piped or CI invocation can overwrite loader or configuration files without --yes. src/cmd/snapshot.rs still rejects non-TTY sessions, so the mutation contract is inconsistent.

Keep the non-TTY guard for loader and configuration writes, or require an explicit yes value.

Also applies to: 421-426

🤖 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 `@src/cmd/setup.rs` around lines 374 - 384, Update the overwrite-confirmation
logic in the setup flow around assert_managed_file_owned and confirm_or_bail,
including the corresponding configuration-write block, to reject non-TTY
sessions unless args.yes is explicitly set. Preserve interactive confirmation
for TTY sessions and the existing cancellation behavior, matching the non-TTY
guard used by the snapshot command.

Source: MCP tools

♻️ Duplicate comments (7)
src/cmd/use_cmd.rs (2)

1-33: ⚠️ Potential issue | 🟠 Major

Mutating Nushell entry points bypass the required lock boundary.

Both paths can rename, remove, register, or write state without an exclusive lock and pre-write snapshot.

  • src/cmd/use_cmd.rs#L1-L33: acquire the mutation lock and snapshot before migration and command dispatch.
  • src/cmd/setup.rs#L161-L207: route doctor and normal setup calls through one lock-aware entry point without double-locking.

As per coding guidelines: “Every new mutating code path must acquire the mutation lock and snapshot the lockfile before writes.”

🤖 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 `@src/cmd/use_cmd.rs` around lines 1 - 33, The mutating use command currently
performs migration and dispatch without the required exclusive lock and
pre-write snapshot. Update use_cmd::execute to acquire the mutation lock and
snapshot before migrate_legacy_install or any command dispatch. In
src/cmd/setup.rs lines 161-207, route doctor and normal setup through a single
lock-aware entry point and avoid acquiring the lock twice.

Source: Coding guidelines


35-104: ⚠️ Potential issue | 🟠 Major

Refresh cached Nu state after changing the active version.

execute_latest and execute_switch write active-version.json but do not refresh NuPaths or activation records. Subsequent activation commands can still identify the previous Nu binary until numan init --refresh.

Refresh after write_active_version succeeds, using a lock-aware entry point.

🤖 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 `@src/cmd/use_cmd.rs` around lines 35 - 104, After each successful
version_manager::write_active_version call in execute_latest and execute_switch,
refresh the cached NuPaths and activation records through the existing
lock-aware refresh entry point. Ensure the refresh runs only after the
active-version write succeeds and preserve the current success messages and
error propagation.

Source: MCP tools

src/nu/version_manager.rs (3)

153-234: ⚠️ Potential issue | 🟠 Major

Do not run an unbounded Nu probe during migration.

Command::output() waits for process exit and pipe closure. A hung legacy binary can block numan use indefinitely. Prefer the existing VERSION metadata. If a process probe remains necessary, make it explicit and enforce a wall-clock timeout.

As per coding guidelines: “External-call hazards ... blocking calls without timeouts on request threads.”

🤖 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 `@src/nu/version_manager.rs` around lines 153 - 234, Update
migrate_legacy_install to avoid the unbounded Command::output probe when
determining the legacy binary version, preferring the existing VERSION metadata.
If probing the binary remains necessary, execute it through an explicit timeout
mechanism that enforces a wall-clock limit and handles timeout or termination
before continuing migration.

Source: Coding guidelines


261-341: 🧹 Nitpick | 🔵 Trivial

Add seam-based tests for migrate_legacy_install.

The tests cover marker persistence, discovery, and output parsing, but not migration rename, marker-write failure, skip behavior, or process failure. Inject the version detector and use a fake instead of spawning real nu.

As per coding guidelines: “Unit tests must use injectable seams ...; do not spawn real nu in unit tests” and “Behavior changes require corresponding tests.”

🤖 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 `@src/nu/version_manager.rs` around lines 261 - 341, Add seam-based unit tests
for migrate_legacy_install covering successful directory migration and marker
persistence, skipping when migration is unnecessary, marker-write failures, and
detector/process failures. Refactor the migration flow to accept or reuse an
injectable version-detection seam, then use a fake detector in tests so no real
nu process is spawned; assert each expected result and filesystem state.

Source: Coding guidelines


1-58: ⚠️ Potential issue | 🟠 Major

Keep broken active-version state visible.

The read path treats invalid markers as absent. The active-binary path also treats a selected version with a missing binary as absent. These states are not equivalent. src/nu/bootstrap.rs can then select the legacy binary while nu_state/active-version.json names another version.

Return None only when the marker file is missing. Return a contextual error for malformed or dangling state, and let numan doctor report it.

Also applies to: 78-105

🤖 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 `@src/nu/version_manager.rs` around lines 1 - 58, Update read_active_version so
it returns None only when active-version.json is missing; propagate malformed
JSON as a contextual error instead of treating it as absent. In the
active-binary resolution path, distinguish a selected version whose binary is
missing from no active marker and return a contextual dangling-state error.
Preserve these errors so numan doctor can report the broken active-version state
rather than falling back to the legacy binary.

Source: MCP tools

src/cmd/setup.rs (2)

242-253: ⚠️ Potential issue | 🔴 Critical

Complete replacement validation before destructive removal.

resolved.is_file() checks only the file type. It does not validate executable permissions or Nushell format. Line 253 removes the managed installation before register_existing_nu performs full validation. A regular invalid file can delete the working installation and then return an error.

Reuse the full Nushell validator before removal. Apply the same ordering to execute_use_path.

🤖 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 `@src/cmd/setup.rs` around lines 242 - 253, Update execute_use_existing and
execute_use_path to run the full Nushell binary validation, including format and
executable checks, before calling remove_managed_nu_if_present. Reuse the
existing validator that register_existing_nu uses, while preserving the current
path resolution and error context, so invalid binaries cannot trigger
destructive removal.

242-253: ⚠️ Potential issue | 🟠 Major

Keep active-version state consistent with managed-tree removal.

The removal helpers operate on the directory that now contains all side-by-side Nu versions. Deleting it removes every installed version but leaves nu_state/active-version.json unchanged. Future resolution can then reference a missing binary and fall back to a different binary.

Preserve the versioned tree for replacement flows, or clear the active marker atomically under the mutation lock and report the destructive scope.

Also applies to: 264-311

🤖 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 `@src/cmd/setup.rs` around lines 242 - 253, Update execute_use_existing and the
related removal flows so deleting the managed version tree cannot leave
nu_state/active-version.json pointing to a missing binary. Preserve the
versioned tree during replacement operations, or clear the active-version marker
atomically while holding the mutation lock; ensure destructive removal reports
that all installed versions are affected.
🤖 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.

Outside diff comments:
In `@src/cmd/setup.rs`:
- Around line 374-384: Update the overwrite-confirmation logic in the setup flow
around assert_managed_file_owned and confirm_or_bail, including the
corresponding configuration-write block, to reject non-TTY sessions unless
args.yes is explicitly set. Preserve interactive confirmation for TTY sessions
and the existing cancellation behavior, matching the non-TTY guard used by the
snapshot command.

---

Duplicate comments:
In `@src/cmd/setup.rs`:
- Around line 242-253: Update execute_use_existing and execute_use_path to run
the full Nushell binary validation, including format and executable checks,
before calling remove_managed_nu_if_present. Reuse the existing validator that
register_existing_nu uses, while preserving the current path resolution and
error context, so invalid binaries cannot trigger destructive removal.
- Around line 242-253: Update execute_use_existing and the related removal flows
so deleting the managed version tree cannot leave nu_state/active-version.json
pointing to a missing binary. Preserve the versioned tree during replacement
operations, or clear the active-version marker atomically while holding the
mutation lock; ensure destructive removal reports that all installed versions
are affected.

In `@src/cmd/use_cmd.rs`:
- Around line 1-33: The mutating use command currently performs migration and
dispatch without the required exclusive lock and pre-write snapshot. Update
use_cmd::execute to acquire the mutation lock and snapshot before
migrate_legacy_install or any command dispatch. In src/cmd/setup.rs lines
161-207, route doctor and normal setup through a single lock-aware entry point
and avoid acquiring the lock twice.
- Around line 35-104: After each successful
version_manager::write_active_version call in execute_latest and execute_switch,
refresh the cached NuPaths and activation records through the existing
lock-aware refresh entry point. Ensure the refresh runs only after the
active-version write succeeds and preserve the current success messages and
error propagation.

In `@src/nu/version_manager.rs`:
- Around line 153-234: Update migrate_legacy_install to avoid the unbounded
Command::output probe when determining the legacy binary version, preferring the
existing VERSION metadata. If probing the binary remains necessary, execute it
through an explicit timeout mechanism that enforces a wall-clock limit and
handles timeout or termination before continuing migration.
- Around line 261-341: Add seam-based unit tests for migrate_legacy_install
covering successful directory migration and marker persistence, skipping when
migration is unnecessary, marker-write failures, and detector/process failures.
Refactor the migration flow to accept or reuse an injectable version-detection
seam, then use a fake detector in tests so no real nu process is spawned; assert
each expected result and filesystem state.
- Around line 1-58: Update read_active_version so it returns None only when
active-version.json is missing; propagate malformed JSON as a contextual error
instead of treating it as absent. In the active-binary resolution path,
distinguish a selected version whose binary is missing from no active marker and
return a contextual dangling-state error. Preserve these errors so numan doctor
can report the broken active-version state rather than falling back to the
legacy binary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 71831383-ada2-41e5-a354-326cf612e513

📥 Commits

Reviewing files that changed from the base of the PR and between 213e874 and 2591036.

📒 Files selected for processing (5)
  • src/cli.rs
  • src/cmd/setup.rs
  • src/cmd/snapshot.rs
  • src/cmd/use_cmd.rs
  • src/nu/version_manager.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Greptile Review
  • GitHub Check: Analyze (rust)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use Rust 2021 edition conventions and match existing naming, module layout, and documentation level in edited files.
Use anyhow::Result with .context(...) in application code; use thiserror for library error types that callers match on.
Use clap derive macros for CLI definitions.
Use serde with serde_json or toml for serialization.
Function parameters must use &Path, not &PathBuf.
Error paths must return anyhow::Result with context, and library code must not panic.
Unit tests must use injectable seams such as FakeCandidateRunner and registrars; do not spawn real nu in unit tests.
Real-Nu acceptance tests should be marked #[ignore] and run separately when activation or nupm-import behavior changes.
Every new mutating code path must acquire the mutation lock and snapshot the lockfile before writes.
Mutating commands, including install, remove, update, gc, and nupm import, must call acquire_mutation_lock(root).
Lockfiles, journals, and state files must be written atomically using write_json_atomic.
numan install must write only to $NUMAN_ROOT and must not invoke Nu or register plugins/autoloads.
Only activate and deactivate may modify Nu integration state.
Treat the lockfile as the authoritative source of truth; derived projections such as autoload state are not authoritative.
Install payloads must use versioned, content-addressed paths and must never be overwritten in place.
Never overwrite foreign autoload files; respect OWNERSHIP_MARKER.
Pass plugin paths through environment variables only; do not use runtime interpolation in Nu program strings.
The nupm boundary must remain read-only toward NUPM_HOME, must not execute build.nu, and must not perform bidirectional synchronization.

**/*.rs: Use Rust 2021 edition conventions; use anyhow::Result for application errors, thiserror for library errors, add error context with .context(...) or ?, and never panic in library code.
Use &Path rather tha...

Files:

  • src/cli.rs
  • src/cmd/snapshot.rs
  • src/nu/version_manager.rs
  • src/cmd/use_cmd.rs
  • src/cmd/setup.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{rs,toml}: All changes must pass formatting and linting: cargo fmt and cargo clippy -- -D warnings.
Behavior changes require corresponding tests, including relevant failure paths.

Files:

  • src/cli.rs
  • src/cmd/snapshot.rs
  • src/nu/version_manager.rs
  • src/cmd/use_cmd.rs
  • src/cmd/setup.rs
**/*.{rs,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update documentation when changing structure, conventions, or user-visible behavior, using AGENTS.md, docs/, or command help as appropriate.

Files:

  • src/cli.rs
  • src/cmd/snapshot.rs
  • src/nu/version_manager.rs
  • src/cmd/use_cmd.rs
  • src/cmd/setup.rs
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Redact secrets when reporting relevant logs or lockfile excerpts in issues.

**/*: Run CI-equivalent validation: cargo test, cargo clippy -- -D warnings, and cargo fmt --check; ignored real-Nu acceptance tests require Nu 0.113 on PATH.
Do not force-push to master; use imperative commit messages under 72 characters, feature/fix branch naming, and squash merges for features.

Files:

  • src/cli.rs
  • src/cmd/snapshot.rs
  • src/nu/version_manager.rs
  • src/cmd/use_cmd.rs
  • src/cmd/setup.rs
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Pass Nu paths and names through environment variables (NUMAN_PLUGIN_BINARY, NUMAN_PLUGIN_CONFIG, NUMAN_PLUGIN_NAME); keep the Nu program string compile-time constant with no runtime interpolation.

Files:

  • src/cli.rs
  • src/cmd/snapshot.rs
  • src/nu/version_manager.rs
  • src/cmd/use_cmd.rs
  • src/cmd/setup.rs
src/cli.rs

📄 CodeRabbit inference engine (AGENTS.md)

Define the CLI with Clap derive macros and use lowercase numan for the binary command name.

Files:

  • src/cli.rs
**/*.{js,jsx,ts,tsx,py,java,go,rs,rb,php,c,cpp,h,hpp,cs,swift,kotlin,scala}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{js,jsx,ts,tsx,py,java,go,rs,rb,php,c,cpp,h,hpp,cs,swift,kotlin,scala}: For code-file work, use Serena's semantic, symbol-aware tools as the primary tools. Use built-in Read, Glob, Grep, and Edit only when Serena has failed, the file is not parseable, a cross-file regex discovery is required, a small read is more appropriate, or the full file is absolutely necessary.
Before editing a code file, first run get_symbols_overview unless it was already run in the session, then use find_symbol with include_body=true for the symbols being changed, and edit with Serena symbol-aware operations such as replace_symbol_body, insert_before_symbol, insert_after_symbol, or replace_content.
Use Serena mappings for code navigation and editing: get_symbols_overview for structure, find_symbol for symbol bodies or name searches, find_referencing_symbols for callers, declaration and implementation tools for definitions, symbol replacement/insertion tools for edits, and hierarchy tools for type relationships.
Write comments only when the rationale is non-obvious, such as a hidden constraint, workaround, or subtle invariant; do not narrate behavior or reference the current task or pull request.

Files:

  • src/cli.rs
  • src/cmd/snapshot.rs
  • src/nu/version_manager.rs
  • src/cmd/use_cmd.rs
  • src/cmd/setup.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Understand the existing implementation before changing it, and make the smallest change that satisfies the request.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Do not add unrelated scope, speculative abstractions, unnecessary error handling, feature flags, or backward-compatibility shims unless requested.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Prefer editing existing files over creating new ones, and never create Markdown or README files unless explicitly requested.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: For exploratory questions, respond in two or three sentences with a recommendation and the main tradeoff; do not implement until the user agrees.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: For UI or frontend changes that cannot be tested in a browser, explicitly state that browser testing was not performed rather than claiming success.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Watch for security issues such as injection, XSS, SQL injection, path traversal, and secret leaks, and fix them when discovered.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Pause and confirm before destructive, hard-to-reverse, externally visible, or third-party-upload actions, including deleting files or branches, dropping tables, killing processes, force-pushing, modifying CI/CD, posting externally, or uploading content.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: When encountering an obstacle, investigate the root cause; do not bypass it with `--no-verify`, `--force`, or deletion of the blocking item.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Investigate unfamiliar files, branches, or configuration before deleting them because they may contain the user's in-progress work.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: A user's approval applies only to the requested action and does not grant perpetual approval for later actions.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Only commit when the user asks; never proactively create commits.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Never update Git configuration or skip hooks with `--no-verify` or `--no-gpg-sign` unless explicitly requested.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Prefer new commits over amending. If a pre-commit hook fails, fix the issue, re-stage, and create a new commit instead of amending.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Stage files by explicit name; do not use `git add -A` or `git add .` because they may include secrets or large binaries.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Use a HEREDOC for commit messages and include the trailer `Co-Authored-By: Claude Opus 4.7 (1M context) <noreplyanthropic.com>`.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Do not push unless asked. Never force-push to `main` or `master`; warn if explicitly asked.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: For pull requests, use `gh` via Bash and inspect the complete diff against the base branch before drafting the title and body.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Before the first tool call, state in one sentence what will be done; provide short updates at key moments, avoid narrating internal deliberation, and end with a one- or two-sentence summary.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Use GitHub-flavored Markdown, reference code locations as `path:line`, avoid emojis unless requested, and match response shape to task complexity.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: Run independent tool calls in parallel; run dependent calls sequentially after resolving their inputs without placeholders or guesses.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:12:22.839Z
Learning: When ambiguity materially changes the work, ask one focused question; otherwise choose a reasonable interpretation and state it.
🔍 Remote MCP DeepWiki, GitHub Copilot

Relevant review context

  • Scope mismatch: The supplied summary describes implemented side-by-side version management, but PR #66’s actual diff adds only a reserved numan use stub that always errors and suggests setup nu; it does not add active-version persistence, version discovery, migration, or switching logic. [src/cmd/use_cmd.rs]
  • DeepWiki identifies Nu identity as cached in NuPaths and activation state as stored in lockfile/autoload records. Switching binaries requires numan init --refresh to update those identities; stale journals, plugin activation records, and autoload state must be rejected or refreshed.
  • The PR changes confirmation behavior globally: non-TTY sessions now auto-confirm activation, deactivation, setup, loader configuration, and snapshot rollback instead of requiring --yes. This is a significant behavioral/security change and is documented as breaking.
  • The setup nu use <path> and setup nu path paths remove the managed Nushell directory before registering the replacement. The specific-path flow performs removal before the replacement path is validated, so an invalid path can destroy the existing managed installation.
  • All reported PR checks passed, including Ubuntu/Windows/macOS tests, real-Nu acceptance, Clippy, MSRV, CodeQL, Rust analysis, and formatting.
🔇 Additional comments (5)
src/nu/version_manager.rs (2)

107-151: Verify that installed-version sorting uses a total order.

If invalid directory names reach a comparator that mixes semantic-version order with lexical fallback, values such as 0.10.0, 0.2.0, and 0.15.0x can produce a non-transitive order. Filter invalid names before sorting, or partition valid and invalid names. Add a regression test.


66-77: LGTM!

src/cmd/snapshot.rs (1)

1-2: LGTM!

Also applies to: 243-246

src/cli.rs (1)

64-65: LGTM!

src/cmd/use_cmd.rs (1)

106-116: LGTM!

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/nu/bootstrap.rs (1)

699-713: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reject non-interactive setup when --yes is absent.

Line 704 delegates to confirm_or_bail. In non-TTY execution, that helper auto-confirms. The flow then downloads Nu and can call persist_user_path at Lines 718-719 without explicit consent.

When options.yes is false and stdin is not interactive, return the cancellation error. Preserve automatic execution only for callers that explicitly set --yes.

Based on learnings: “Pause and confirm before destructive, hard-to-reverse, externally visible” actions. External tool context states that confirm_or_bail auto-confirms in non-TTY execution.

🤖 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 `@src/nu/bootstrap.rs` around lines 699 - 713, Update the setup flow around the
final `confirm_or_bail` call so non-interactive execution returns the existing
cancellation error when `options.yes` is false; only bypass confirmation for
callers explicitly setting `options.yes`. Preserve the interactive confirmation
behavior and the earlier reinstall prompt, while preventing download and
subsequent `persist_user_path` execution without consent.

Sources: Learnings, MCP tools

♻️ Duplicate comments (2)
src/cmd/use_cmd.rs (1)

23-31: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Snapshot the lockfile before migration writes.

Line 26 acquires the mutation lock, but Line 30 can rename the legacy binary and write active-version state before any lockfile snapshot occurs. Add the established snapshot step immediately after lock acquisition and before migration or dispatch.

As per coding guidelines: “Every new mutating code path must acquire the mutation lock and snapshot the lockfile before writes.”

🤖 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 `@src/cmd/use_cmd.rs` around lines 23 - 31, Add the established lockfile
snapshot operation immediately after acquiring the mutation lock in execute,
before migrate_legacy_install or any subsequent dispatch. Preserve the existing
mutation lock and migration error context while ensuring all writes occur after
both protections are established.

Source: Coding guidelines

src/nu/bootstrap.rs (1)

237-240: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not activate Nu during archive installation.

Line 240 writes the active-version marker as an archive extraction side effect. This silently selects a Nu version and leaves Nu identity-bound state stale until refresh.

Return the installed version to the caller. Run the activation lifecycle only after explicit user consent.

As per coding guidelines: “Only activate and deactivate may modify Nu integration state.” Based on learnings: “Do not silently switch the user's Nushell version; managed Nu pinning must be offer-based and require user consent.”

🤖 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 `@src/nu/bootstrap.rs` around lines 237 - 240, Remove the write_active_version
call from the archive installation flow in the Nu bootstrap function, and return
the installed version after writing the VERSION file. Move activation to the
explicit user-consent lifecycle, ensuring only activate/deactivate modify Nu
integration state and archive extraction never silently selects a version.

Sources: Coding guidelines, Learnings, MCP tools

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

Outside diff comments:
In `@src/nu/bootstrap.rs`:
- Around line 699-713: Update the setup flow around the final `confirm_or_bail`
call so non-interactive execution returns the existing cancellation error when
`options.yes` is false; only bypass confirmation for callers explicitly setting
`options.yes`. Preserve the interactive confirmation behavior and the earlier
reinstall prompt, while preventing download and subsequent `persist_user_path`
execution without consent.

---

Duplicate comments:
In `@src/cmd/use_cmd.rs`:
- Around line 23-31: Add the established lockfile snapshot operation immediately
after acquiring the mutation lock in execute, before migrate_legacy_install or
any subsequent dispatch. Preserve the existing mutation lock and migration error
context while ensuring all writes occur after both protections are established.

In `@src/nu/bootstrap.rs`:
- Around line 237-240: Remove the write_active_version call from the archive
installation flow in the Nu bootstrap function, and return the installed version
after writing the VERSION file. Move activation to the explicit user-consent
lifecycle, ensuring only activate/deactivate modify Nu integration state and
archive extraction never silently selects a version.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 22ea51d3-a219-4069-8c2c-a2edf74dbc42

📥 Commits

Reviewing files that changed from the base of the PR and between 2591036 and d9b8e0f.

📒 Files selected for processing (2)
  • src/cmd/use_cmd.rs
  • src/nu/bootstrap.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Trackdubllc/Trackdub (manual)
  • tonythethompson/QuickShell (manual)
  • tonythethompson/numan (manual)
  • tonythethompson/dependency-chain-substrate (manual)
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Greptile Review
  • GitHub Check: Analyze (rust)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use Rust 2021 edition conventions and match existing naming, module layout, and documentation level in edited files.
Use anyhow::Result with .context(...) in application code; use thiserror for library error types that callers match on.
Use clap derive macros for CLI definitions.
Use serde with serde_json or toml for serialization.
Function parameters must use &Path, not &PathBuf.
Error paths must return anyhow::Result with context, and library code must not panic.
Unit tests must use injectable seams such as FakeCandidateRunner and registrars; do not spawn real nu in unit tests.
Real-Nu acceptance tests should be marked #[ignore] and run separately when activation or nupm-import behavior changes.
Every new mutating code path must acquire the mutation lock and snapshot the lockfile before writes.
Mutating commands, including install, remove, update, gc, and nupm import, must call acquire_mutation_lock(root).
Lockfiles, journals, and state files must be written atomically using write_json_atomic.
numan install must write only to $NUMAN_ROOT and must not invoke Nu or register plugins/autoloads.
Only activate and deactivate may modify Nu integration state.
Treat the lockfile as the authoritative source of truth; derived projections such as autoload state are not authoritative.
Install payloads must use versioned, content-addressed paths and must never be overwritten in place.
Never overwrite foreign autoload files; respect OWNERSHIP_MARKER.
Pass plugin paths through environment variables only; do not use runtime interpolation in Nu program strings.
The nupm boundary must remain read-only toward NUPM_HOME, must not execute build.nu, and must not perform bidirectional synchronization.

**/*.rs: Use Rust 2021 edition and MSRV 1.88; ensure code builds with the stable toolchain.
Use anyhow::Result for application code and thiserror for library error types; add context with .context(...) or ?, and nev...

Files:

  • src/cmd/use_cmd.rs
  • src/nu/bootstrap.rs
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{rs,toml}: All changes must pass formatting and linting: cargo fmt and cargo clippy -- -D warnings.
Behavior changes require corresponding tests, including relevant failure paths.

Use cargo test as the required test gate; all tests must pass, with platform-specific behavior covered using mock platforms.

Files:

  • src/cmd/use_cmd.rs
  • src/nu/bootstrap.rs
**/*.{rs,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update documentation when changing structure, conventions, or user-visible behavior, using AGENTS.md, docs/, or command help as appropriate.

Files:

  • src/cmd/use_cmd.rs
  • src/nu/bootstrap.rs
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Redact secrets when reporting relevant logs or lockfile excerpts in issues.

Files:

  • src/cmd/use_cmd.rs
  • src/nu/bootstrap.rs
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Use #[cfg(target_env)] and compile-time target information for platform detection, not std::env::consts.
Use serde/serde_json for JSON state and toml for configuration serialization.
All JSON state files, including the lockfile, journals, and nu_state/paths.json, must use write_json_atomic with a same-directory temporary file and persist operation.
Serialize mutations with acquire_mutation_lock(root); the second acquisition for the same root must fail immediately rather than block.

Files:

  • src/cmd/use_cmd.rs
  • src/nu/bootstrap.rs
**/*.{js,jsx,ts,tsx,py,java,go,rs,rb,php,c,cpp,h,hpp,cs,swift,kt,kts,scala}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{js,jsx,ts,tsx,py,java,go,rs,rb,php,c,cpp,h,hpp,cs,swift,kt,kts,scala}: For code files, use Serena's semantic tools as the primary tools for structure inspection, symbol reading, searching, and editing; use built-in Read, Glob, Grep, and Edit only under the explicitly listed fallback conditions.
Before editing a code file, run get_symbols_overview, then find_symbol with include_body=true for the symbols being changed, and edit using Serena symbol-editing tools such as replace_symbol_body, insert_before_symbol, insert_after_symbol, or replace_content.
Add comments only when the reason is non-obvious, such as a hidden constraint, workaround, or subtle invariant; do not narrate behavior or reference the current task or PR.
Watch for and fix security issues when discovered, including injection, XSS, SQL injection, path traversal, and secret leaks.

Files:

  • src/cmd/use_cmd.rs
  • src/nu/bootstrap.rs
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:20.434Z
Learning: Source builds require explicit user consent before cloning or building, with a separate consent scope.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:20.434Z
Learning: Do not silently switch the user's Nushell version; managed Nu pinning must be offer-based and require user consent.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:20.434Z
Learning: Do not force-push to `master`; use imperative commit messages under 72 characters and branch names such as `feature/description` or `fix/description`.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: Understand existing code before changing it and make the smallest change that satisfies the request; do not add unrelated cleanup, speculative abstractions, unnecessary error handling, feature flags, or compatibility shims unless requested.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: Prefer editing existing files over creating new ones, and never create Markdown or README files unless explicitly requested.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: For exploratory questions, respond with a 2–3 sentence recommendation and its main tradeoff; do not implement until the user agrees.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: Pause and confirm before destructive, hard-to-reverse, externally visible, or third-party-upload actions, including deleting data, modifying CI/CD, pushing changes, posting messages, or uploading content.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: When encountering an obstacle, investigate the root cause; do not bypass it with `--no-verify`, `--force`, or deletion of the blocking item, and investigate unfamiliar files, branches, or configuration before deleting them.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: Only commit when the user asks; never update Git configuration; do not skip hooks; prefer new commits over amend; stage files by name; do not commit likely secrets; and do not push unless asked.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: If a pre-commit hook fails, treat the commit as unsuccessful, fix the issue, restage, and create a new commit rather than amending.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: Use a HEREDOC for commit messages and include the trailer `Co-Authored-By: Claude Opus 4.7 (1M context) <noreplyanthropic.com>`.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: Never force-push to `main` or `master`; warn if such an action is requested.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: For pull requests, use `gh` via Bash and inspect the full diff against the base branch before drafting the title and body.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: Before the first tool call, briefly state the intended action; provide short updates at key moments; avoid narrating internal deliberation; and end with a one- or two-sentence summary.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: When tool calls are independent, issue them in parallel; when dependent, run them sequentially after resolving required values.
Learnt from: CR
Repo: tonythethompson/numan

Timestamp: 2026-08-02T03:19:30.030Z
Learning: Ask one focused question when ambiguity materially changes the work; otherwise choose a reasonable interpretation and state it.
🔍 Remote MCP DeepWiki, GitHub Copilot

Relevant review context

  • The PR’s actual implementation adds a reserved numan use stub: it always returns an error and suggests numan setup nu [VERSION]; it does not implement active-version persistence, discovery, migration, or switching. [src/cmd/use_cmd.rs]
  • DeepWiki identifies NuPaths, lockfile activation records, autoload state, and journals as identity-bound to the selected Nu executable. Switching binaries requires refreshing paths.json, activation identities, autoload state, and stale journals via numan init --refresh; merely adding a use command would not be sufficient.
  • execute_use_existing removes the managed Nu directory before validating the supplied replacement path. An invalid or non-runnable path can therefore destroy the existing managed installation. [src/cmd/setup.rs]
  • The same destructive ordering exists for setup nu path after PATH discovery, although that path is validated before removal. [src/cmd/setup.rs]
  • Confirmation behavior changed globally: non-TTY invocations now auto-confirm activation, deactivation, snapshot rollback, setup, loader overwrite/configuration, and PATH changes. This is a broad security/behavioral change, not limited to Nu version management. [src/util/confirm.rs, CHANGELOG.md`]
  • All reported CI checks passed, including Ubuntu, Windows, macOS, real-Nu acceptance, MSRV, Clippy, CodeQL, formatting, and static analysis.
🔀 Multi-repo context tonythethompson/QuickShell, Trackdubllc/Trackdub

Linked repositories findings

tonythethompson/QuickShell

  • Nushell is launched generically via nu -c '…'; no numan commands or managed-version paths are referenced. [::tonythethompson/QuickShell::] (docs/architecture/launch.md:130, QuickShell.Core/Services/TerminalLauncherArgs.cs:62-70)

Trackdubllc/Trackdub

  • Only incidental documentation references to Nushell-related projects; no numan CLI or setup API consumers found. [::Trackdubllc/Trackdub::] (docs/plans/open-core-split-continuation.md:264, docs/plans/phase-3-validation-report.md:140)

tonythethompson/dependency-chain-substrate

  • No references to numan, Nushell setup commands, or version-management paths were found.
🔇 Additional comments (1)
src/nu/bootstrap.rs (1)

668-685: LGTM!

Also applies to: 741-741

greptile-apps[bot]

This comment was marked as resolved.

Comment thread src/nu/version_manager.rs
Comment on lines +188 to +233
pub fn active_nu_binary(root: &Path) -> Result<Option<PathBuf>> {
let Some(active) = read_active_version(root)? else {
return Ok(None);
};
let version = normalize_version(&active.version)?;

// Prefer the on-tree version-binary when present. This covers the
// common case where the off-tree marker was later matched by an
// on-tree install (e.g. the user ran `setup nu <version>` to give the
// off-tree selection a versioned home).
let on_tree = version_binary(root, &version);
if on_tree.exists() {
return Ok(Some(on_tree));
}

// Fall back to the recorded off-tree path when one was stored.
if let Some(off_tree) = active.binary_path.as_ref() {
let off_tree_path = std::path::PathBuf::from(off_tree);
if off_tree_path.is_file() {
return Ok(Some(off_tree_path));
}
}

// Build the message conditionally: skip the off-tree clause when no
// off-tree path is recorded. The literal "<none>" placeholder previously
// rendered here was clunky in `numan doctor` output.
match active.binary_path.as_ref() {
Some(off_tree) => Err(anyhow::anyhow!(
"Active Nu version '{}' is set but neither the on-tree binary at '{}' \
nor the recorded off-tree path '{}' is present. \
Run 'numan setup nu' to install the selected version or \
'numan use <version>' / 'numan use latest' to choose a different one.",
version,
on_tree.display(),
std::path::PathBuf::from(off_tree).display(),
)),
None => Err(anyhow::anyhow!(
// pre-migration `nu_state/active-version.json` markers have no off-tree field
"Active Nu version '{}' is set but the on-tree binary at '{}' is missing. \
Run 'numan setup nu' to install the selected version or \
'numan use <version>' / 'numan use latest' to choose a different one.",
version,
on_tree.display(),
)),
}
}

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.

🔍 Off-tree marker resolution is tolerant of stale paths in one lookup and fatal in another

find_nu_executable_with_root deliberately falls through to PATH when both the on-tree and recorded off-tree paths are stale (src/nu/paths.rs:316-333), while version_manager::active_nu_binary returns Err for the same state (src/nu/version_manager.rs:210-232). That divergence is documented, but active_nu_binary currently has no callers, so the "doctor surfaces dangling markers" behaviour the doc comments promise does not exist yet — nothing reports a marker pointing at a deleted binary.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 2, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 2, 2026
devin-ai-integration Bot and others added 3 commits August 2, 2026 14:37
The 
equire_tty_or_yes hard-fail guard introduced for destructive

uman setup paths correctly blocks non-TTY, non---yes runs, but it
also prevented the unit test that injects a decline-only confirm seam
from exercising the merged prompt. Skip the TTY guard only when a
confirm seam is provided, so the production fail-closed behavior is
preserved while tests can still verify the decline path and the
'no undo' prompt text.
@tonythethompson
tonythethompson merged commit 3920cfd into master Aug 2, 2026
17 of 18 checks passed
@tonythethompson
tonythethompson deleted the feature/numan-use branch August 2, 2026 15:03
@linear-code

linear-code Bot commented Aug 2, 2026

Copy link
Copy Markdown

NUM-46

tonythethompson added a commit that referenced this pull request Aug 2, 2026
Resolve conflicts between journaled legacy migration (#69) and
shipped numan use (#67): keep mutate-path migration under the use
mutation guard, prefer off-tree-aware version resolution from master,
and retain PR setup_subcommand_lock audit labeling for doctor repair.

Co-authored-by: Cursor <cursoragent@cursor.com>
cursor Bot pushed a commit that referenced this pull request Aug 2, 2026
Resolve conflicts from #67 (numan use) landing on master while this
branch carried migration/hardening:

- Keep PR71 migration journal, setup_subcommand_lock, --force, and
  require_tty_or_yes hardening.
- Fold master additions: confirm_or_auto_with_tty, resolve_installed_version,
  execute_nu_repair alias, reject_skip_path helper, installed-version
  PATH fallback, and roadmap PATH docs.
- Align setup unit fake-nu + confirm-seam tty gates so execute_use_existing
  tests pass after the merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-authored-by: Anthony Thompson <github@trackdub.com>
@coderabbitai coderabbitai Bot mentioned this pull request Aug 7, 2026
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.

2 participants