feat: implement numan use for side-by-side Nu version management
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 toversion_managerfor 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 nuis fail-closed without--yes, mirroring setup.rs; refuses to mutate state silently. - Hoisted the post-install PATH-mutation consent through a
caller_consented_destructiveflag onNuSetupOptionsso 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 nuandnuman usecannot 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 subsequentnuman use listandfind_nu_executable_with_rootcan 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-gradeeprintln!records whether the gate was an explicit--yesor an interactive confirmation. - Hoisted via
caller_consented_destructiveso 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 fornuman use. - Documents that
numan use <version>errors (with a hint to runsetup 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 bynuman setup nuwhilenuman useonly 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 warningsclean.cargo fmt --checkclean.- New end-to-end test coverage for the confirm-gate UX in
src/cmd/setup.rs(DI seams viaExecuteUseOpts<'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 aliasfor Numan-level aliases (post-1.0)