Skip to content

refactor(uninstall): run uninstall through internal command#3083

Merged
cv merged 99 commits into
mainfrom
refactor/internal-uninstall-run-plan
May 6, 2026
Merged

refactor(uninstall): run uninstall through internal command#3083
cv merged 99 commits into
mainfrom
refactor/internal-uninstall-run-plan

Conversation

@cv

@cv cv commented May 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Moves the uninstaller execution path behind hidden oclif-native internal commands and turns uninstall.sh into a compatibility wrapper. The uninstall plan is now both inspectable as JSON and executable from TypeScript while preserving the public nemoclaw uninstall flow.

Changes

  • Add hidden internal commands for internal uninstall plan, internal uninstall run-plan, and internal uninstall classify-shim.
  • Add TypeScript run-plan execution for OpenShell, npm, Docker, Ollama, state, shim, and binary cleanup.
  • Reduce uninstall.sh to a thin wrapper around nemoclaw internal uninstall run-plan.
  • Update uninstall and gateway cleanup tests to assert the TypeScript plan/run path instead of sourcing shell helpers.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • make docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Carlos Villela cvillela@nvidia.com

Summary by CodeRabbit

  • New Features

    • Uninstall now also removes related Docker containers and images, plus managed swap and improved gateway resource cleanup.
    • Uninstaller prefers a Node.js-based orchestration (with a compatibility wrapper) for more reliable, consistent execution.
    • Added internal CLI commands to build, preview, classify, and run uninstall plans.
  • Tests

    • Added comprehensive tests covering uninstall plan construction, execution flows, and CLI routing.
  • Chores

    • Simplified uninstall wrapper script to delegate to the Node-based uninstaller.

cv added 30 commits May 2, 2026 13:36
@cv cv self-assigned this May 6, 2026
@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2b3df9b6-280b-490e-be81-e24bee4291d8

📥 Commits

Reviewing files that changed from the base of the PR and between 359fefd and a01e3e2.

📒 Files selected for processing (4)
  • src/lib/actions/uninstall-run-plan.test.ts
  • src/lib/actions/uninstall-run-plan.ts
  • src/lib/domain/uninstall/paths.test.ts
  • src/lib/domain/uninstall/paths.ts

📝 Walkthrough

Walkthrough

Adds a Node.js-based uninstaller flow and three internal oclif commands (classify-shim, plan, run-plan); expands uninstall path shapes and plan actions to cover Docker containers/images and managed swap; replaces the large Bash uninstaller with a lightweight wrapper delegating to the Node uninstaller.

Changes

Uninstall Refactor: CLI Commands, Orchestration, and Path Resolution

Layer / File(s) Summary
Data Shape
src/lib/domain/uninstall/paths.ts
Expanded UninstallPathOptions and UninstallPaths with fields like repoRoot, tmpDir, xdgBinHome, runtimeTempGlobs, shellProfilePaths, nvmDir, and managedSwapMarkerPath. defaultUninstallPaths now computes and returns these fields.
Plan Action Expansion
src/lib/domain/uninstall/plan.ts
Extended UninstallPlanAction union to include delete-managed-swap, delete-related-docker-containers, and delete-related-docker-images. Docker resources and State/binaries steps updated to emit these actions.
Runtime Orchestration
src/lib/actions/uninstall-run-plan.ts
New module introducing RunResult, UninstallRunOptions, UninstallRunDeps, UninstallRunOutcome, plus buildRunPlan and runUninstallPlan. Implements runtime helpers, confirmation/banner flow, plan execution dispatcher, and concrete step implementations (Docker, filesystem, swap, Ollama models, shim handling).
CLI Command Wiring
src/commands/internal/uninstall/classify-shim.ts, src/commands/internal/uninstall/plan.ts, src/commands/internal/uninstall/run-plan.ts
Three new hidden/internal oclif commands: classify-shim (classify shim path, optional JSON), plan (build-only uninstall plan, JSON or summary), and run-plan (execute uninstall run with flags mapped to runUninstallPlan).
Shell Wrapper Delegation
uninstall.sh
Replaced previous Bash-based uninstaller with a small wrapper that detects a Node CLI bundle and delegates to the Node-based uninstaller, falling back to the previous internal uninstaller when Node/CLI bundle is absent.
Coverage/Tooling Markers
src/lib/actions/uninstall-plan.ts, src/lib/domain/uninstall/shims.ts
Added top/bottom V8 ignore markers around modules (no API/behavior changes).
Tests
src/lib/actions/uninstall-run-plan.test.ts, src/lib/domain/uninstall/plan.test.ts, src/lib/domain/uninstall/paths.test.ts, test/internal-cli.test.ts, test/gateway-cleanup.test.ts, test/uninstall.test.ts
Added tests for run-plan build/run flows and confirmations; updated plan expectations to include new actions; added repoRoot defaults test; adjusted internal CLI and gateway-cleanup tests; removed some obsolete uninstall helper tests and a local npm env helper.

Sequence Diagram

sequenceDiagram
    participant User as User/CLI
    participant RunPlanCmd as run-plan Command
    participant Runtime as UninstallRuntime
    participant Executor as executePlan
    participant Docker as Docker
    participant FS as Filesystem
    participant Tools as System Tools

    User->>RunPlanCmd: invoke with flags (--yes, --delete-models, --gateway, ...)
    RunPlanCmd->>Runtime: buildRunPlan(options)
    Runtime->>FS: resolve paths (shim, config, state, repoRoot)
    Runtime->>Runtime: classifyShimPath()
    Runtime-->>RunPlanCmd: { paths, plan }
    RunPlanCmd->>Runtime: confirm(optional)
    alt confirmed
        RunPlanCmd->>Executor: executePlan(plan, runtime)
        Executor->>Tools: stop helper services / kill processes
        Executor->>FS: remove runtime temp globs, shims, binaries
        Executor->>Docker: rm containers
        Executor->>Docker: rmi images
        Executor->>Docker: volume rm
        Executor->>Tools: remove managed swap
        Executor->>Tools: delete Ollama models (conditional)
        Executor-->>RunPlanCmd: completion
    else aborted
        RunPlanCmd-->>User: "Aborted."
    end
    RunPlanCmd-->>User: exit code
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • NVIDIA/NemoClaw#3083: Modifies the same uninstall subsystem and adds the internal uninstall oclif commands plus TypeScript run-plan implementation.
  • NVIDIA/NemoClaw#3080: Extends uninstall planning domain (shim classification, paths, tests) and appears directly related.

Suggested reviewers

  • prekshivyas

Poem

🐰 Hopped from shim to swap with nimble cheer,

Node speaks softly, the old bash yields clear.
Containers, images, and globs take flight,
A tidy clean by morning light. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'refactor(uninstall): run uninstall through internal command' clearly and concisely describes the main change: refactoring the uninstall flow to run through internal commands instead of the previous shell-based approach.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/internal-uninstall-run-plan

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

@cv cv marked this pull request as draft May 6, 2026 06:15
@copy-pr-bot

copy-pr-bot Bot commented May 6, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@cv cv added NemoClaw CLI refactor PR restructures code without intended behavior change labels May 6, 2026
@cv

cv commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/actions/uninstall-run-plan.ts`:
- Line 155: The interactive uninstall always aborts because buildRuntime
currently sets readLine to a no-op (() => null); update buildRuntime so the
readLine default is an actual prompt reader (e.g. a function that reads from
stdin/TTY or delegates to the CLI's readline helper) instead of returning null,
and ensure confirm() receives that real reader; adjust the same defaulting logic
referenced around lines 173-184 so both places use the real stdin prompt
implementation (identify the buildRuntime function and the readLine property
used when constructing the runtime and the confirm(...) call) so typed
confirmations are accepted when no explicit reader is injected.
- Around line 391-393: The success message is emitted unconditionally after
calling runtime.run for "swapoff" and "rm", so change the logic in
uninstall-run-plan.ts around the runtime.run calls (the runtime.run("sudo",
["swapoff", "/swapfile"], ...) and runtime.run("sudo", ["rm", "-f",
"/swapfile"], ...) lines) to check each command's result or catch exceptions
before logging; specifically run both commands in a try/catch (or inspect their
returned exit/status), ensure stdio or captured output is available for error
reporting instead of "ignore", and only call runtime.log("Swap file removed")
when both commands succeeded—otherwise log/throw an error with the command
output/details.

In `@src/lib/domain/uninstall/paths.ts`:
- Line 53: The default repoRoot currently uses path.resolve(__dirname, "..",
"..", "..") which resolves to the dist/ directory after compilation; update the
default in the repoRoot assignment (the options.repoRoot fallback) to go up one
additional directory (use path.resolve(__dirname, "..", "..", "..", "..")) so
compiled code in dist/lib/domain/uninstall resolves to the repository root and
not dist/, ensuring repo-relative assets like scripts/start-services.sh are
found.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8be31edf-ed87-46f0-bcb8-9fe8f64d020e

📥 Commits

Reviewing files that changed from the base of the PR and between cc93aae and 359fefd.

📒 Files selected for processing (14)
  • src/commands/internal/uninstall/classify-shim.ts
  • src/commands/internal/uninstall/plan.ts
  • src/commands/internal/uninstall/run-plan.ts
  • src/lib/actions/uninstall-plan.ts
  • src/lib/actions/uninstall-run-plan.test.ts
  • src/lib/actions/uninstall-run-plan.ts
  • src/lib/domain/uninstall/paths.ts
  • src/lib/domain/uninstall/plan.test.ts
  • src/lib/domain/uninstall/plan.ts
  • src/lib/domain/uninstall/shims.ts
  • test/gateway-cleanup.test.ts
  • test/internal-cli.test.ts
  • test/uninstall.test.ts
  • uninstall.sh

Comment thread src/lib/actions/uninstall-run-plan.ts Outdated
Comment thread src/lib/actions/uninstall-run-plan.ts Outdated
Comment thread src/lib/domain/uninstall/paths.ts Outdated
@cv

cv commented May 6, 2026

Copy link
Copy Markdown
Collaborator Author

Automated PR review summary

Reviewed PR #3083: refactor(uninstall): run uninstall through internal command

Recommendation

  • Recommendation: PASS
  • Highest observed severity: low
  • Block merge: no
  • Why: The highest-risk downside would be unsafe or incomplete uninstall cleanup after moving execution behind hidden internal commands. In the reviewed environment, the new internal plan surfaced the expected cleanup actions, the public uninstall wrapper still exposed the expected interface, and shim classification preserved foreign regular files while marking symlinks as removable. I did not find evidence that this refactor breaks the claimed uninstall flow or broadens deletion risk.
  • Reviewer summary: Reviewed the uninstall refactor with targeted adversarial probes focused on the new internal uninstall commands, wrapper compatibility, and deletion-safety behavior. The tested claims held in the installed environment, with no PR-specific regression found.

Installation and setup findings

  • Installation and end-to-end verification succeeded. The local-repo installer path worked, onboarding created sandbox 'nemoclaw-local-install', ssh into that sandbox succeeded, and an in-sandbox OpenClaw model query returned the expected response. The only incomplete part is that the original installer process was still finalizing policy presets in the background when verification finished.

What was validated

  • The PR revision was checked out in an isolated review environment.
  • The local checkout was installed using the repository installer flow as closely as the environment allowed.
  • Adversarial, PR-specific probes were then run against the installed environment and relevant repository context.
  • Diff summary:
 .../skills/nemoclaw-contributor-create-pr/SKILL.md |   20 +-
 .../nemoclaw-maintainer-pr-comparator/SKILL.md     |  121 --
 .../checks/tier-0-gates.md                         |   61 -
 .../checks/tier-1-correctness.md                   |   86 --
 .../checks/tier-2-quality.md                       |   64 -
 .../repo-policy.md                                 |   86 --
 .../scripts/check-coderabbit-threads.sh            |  105 --
 .../scripts/collect-gates.sh                       |   84 --
 .../scripts/find-candidates.sh                     |   86 --
 .../scripts/parse-supersession.sh                  |   68 --
 .../scripts/render-verdict.py                      |  232 ----
 .../templates/verdict.md                           |   88 --
 .../tiebreakers.md                                 |   61 -
 .../validation/backtest.md                         |   69 --
 .agents/skills/nemoclaw-skills-guide/SKILL.md      |    2 +-
 .../references/agent-skills.md                     |    4 +-
 .../nemoclaw-user-configure-inference/SKILL.md     |  329 ++---
 .../references/inference-options.md                |    4 +-
 .../references/set-up-sub-agent.md                 |  120 --
 .../references/swit
...[truncated]

Failing tests and unresolved impact

  • No failing adversarial tests were captured.

Passing tests and why they mattered

Passing test 1: Internal uninstall plan exposes new TypeScript cleanup actions

  • What was tested: nemoclaw internal uninstall plan --json returns a deterministic uninstall plan that includes the newly claimed TypeScript-managed cleanup actions.
  • Why it mattered: If false, the refactor could silently omit cleanup behavior while still leaving help text and command routing apparently functional.
  • Observed result: Exit 0. Parsed JSON showed gateway=nemoclaw, 6 steps, and action kinds including delete-related-docker-containers, delete-related-docker-images, delete-managed-swap, and delete-docker-volume.
  • Command: nemoclaw internal uninstall plan --json --gateway nemoclaw
  • Recommended follow-up coverage: Add or retain an integration regression test that asserts emitted uninstall plan JSON action kinds, because routing/help tests alone would not catch missing cleanup steps.

Passing test 2: uninstall.sh remains a compatibility wrapper for internal run-plan

  • What was tested: The public uninstall.sh entrypoint still preserves the expected help/interface after being reduced to a wrapper around the internal run-plan command.
  • Why it mattered: If false, users invoking the public uninstall flow could see broken flags or unexpected behavior even though the new internal command exists.
  • Observed result: Both commands exited 0 and both help outputs contained 'NemoClaw Uninstaller', '--keep-openshell', and '--delete-models'.
  • Command: bash uninstall.sh --help; nemoclaw internal uninstall run-plan --help
  • Recommended follow-up coverage: Keep a regression test on wrapper help/flag parity so future refactors cannot silently diverge from the public uninstall entrypoint.

Passing test 3: classify-shim preserves foreign regular files while identifying managed symlinks

  • What was tested: The new hidden shim classifier distinguishes safe-to-delete managed symlinks from non-managed regular files, preventing unsafe uninstall deletions in user bin directories.
  • Why it mattered: If false, uninstall could delete arbitrary user-managed executables under ~/.local/bin, which would be a high-severity safety regression.
  • Observed result: Symlink classified as managed-symlink with reason 'shim path is a symlink'; regular executable classified as preserve-foreign-file with reason 'regular file is not an installer-managed shim'.
  • Command: nemoclaw internal uninstall classify-shim /tmp/pr3083shim/bin/{symlinkshim,regularshim} --json
  • Recommended follow-up coverage: Add a regression test covering symlinks and regular executable files in XDG bin paths, because this behavior is a user-data safety boundary.

Passing test 4: Installed OpenShell sandbox remains reachable for real runtime inspection

  • What was tested: After the refactor, the installed product still coexists with a working OpenShell sandbox, so there is no obvious integration breakage affecting real runtime context.
  • Why it mattered: If false, source/host-only CLI checks could miss broader integration fallout that blocks real NemoClaw/OpenShell workflows.
  • Observed result: Sandbox SSH succeeded and /sandbox/.openclaw/workspace existed, confirming access to the real sandboxed runtime context during review.
  • Command: openshell sandbox ssh-config nemoclaw-local-install; ssh openshell-nemoclaw-local-install 'test -d /sandbox/.openclaw/workspace'
  • Recommended follow-up coverage: No direct unit test recommendation; this was environment-level confirmation rather than a narrow PR contract.

Bottom line

  • Based on the install evidence and adversarial probes, this PR looks reasonable to approve.

@cv cv marked this pull request as ready for review May 6, 2026 19:39
@cv cv changed the base branch from refactor/uninstall-plan-domain to main May 6, 2026 19:39
@cv cv enabled auto-merge (squash) May 6, 2026 19:39
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv cv requested a review from prekshivyas May 6, 2026 19:42

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

Solid bash→TS port of the uninstaller behind internal uninstall {plan,run-plan,classify-shim} (all hidden), with uninstall.sh reduced to a Node bootstrap wrapper. Public nemoclaw uninstall flag surface preserved. New plan actions (delete-related-docker-containers, -images, delete-managed-swap) modeled in the typed plan and tested. Five new unit tests in uninstall-run-plan.test.ts cover plan-build, dry-run with fakes, interactive-yes/no, and swap-disable failure. Stale bash-helper tests in test/uninstall.test.ts correctly removed in step with the helpers themselves; gateway-cleanup.test.ts updated to assert the TS plan.

Approving with the following undeclared behavior trims as follow-up nits — none are blockers, but they're worth tracking for a follow-up PR:

  1. /etc/fstab cleanup is gone. Old remove_nemoclaw_swap ran sudo sed -i ... /etc/fstab to strip /swapfile entries after swapoff+rm. New removeManagedSwap skips this step → users may end up with dangling fstab entries causing boot-time warnings.
  2. managed_swap marker content validation dropped. Old bash refused to act unless the marker file's contents equaled /swapfile. New TS only checks marker existence.
  3. stop_orphaned_openshell_processes narrower. Old bash also pgrep'd ssh processes whose cmdline matched openshell ssh-proxy / openshell- (introduced in #1940). New TS only matches openshell (sandbox create|ssh-proxy) directly — orphan ssh sessions won't be reaped.
  4. Visual UX downgraded. Lost the ASCII-art banner, NVIDIA-green styling, spinner wrapper, [N/6] step headers with rules, and the styled goodbye footer. New output is plain console.log. Probably worth re-adding via the existing terminal-style.ts helper in a follow-up.
  5. Bootstrap dependency. uninstall.sh now requires either dist/nemoclaw.js to exist OR nemoclaw to be on PATH. Partial-install scenarios where the global package is half-removed could fail with exit 127. Probably fine for the common case but worth a fallback path.

PR description claims "preserving the public nemoclaw uninstall flow" — accurate at the flag/argument level, but the UX and the fstab/orphan-ssh trims should ideally be called out in the description so reviewers know what to expect.

CI: pr.yaml rollup checks pass (commit-lint, dco, layer-boundary, check-hash). Self-hosted: 3 prior runs success on pull-request/3083. macos-e2e / wsl-e2e / current self-hosted run still in progress at approval time.

@cv cv merged commit d8f6950 into main May 6, 2026
12 checks passed
@cv cv deleted the refactor/internal-uninstall-run-plan branch May 27, 2026 21:17
@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output and removed NemoClaw CLI labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output refactor PR restructures code without intended behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants