Skip to content

fix(uninstall): remove agent-alias CLI shims (nemohermes, nemo-deepagents) (#6098) - #6101

Merged
cv merged 2 commits into
mainfrom
fix/6098-uninstall-remove-agent-alias-shims
Jul 3, 2026
Merged

fix(uninstall): remove agent-alias CLI shims (nemohermes, nemo-deepagents) (#6098)#6101
cv merged 2 commits into
mainfrom
fix/6098-uninstall-remove-agent-alias-shims

Conversation

@jason-ma-nv

@jason-ma-nv jason-ma-nv commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

nemoclaw uninstall removed the nemoclaw CLI shim, the openshell* binaries, and ~/.nemoclaw/, but left the sibling agent-alias shims (nemohermes, nemo-deepagents) in ~/.local/bin. After a "clean" uninstall those commands still resolved and reported a version. This removes them too, using the same installer-managed-shim safety classification.

Related Issue

Fixes #6098

Changes

  • src/lib/domain/uninstall/shims.ts: classifyNemoclawShim / isInstallerManagedWrapperContents accept an optional binName (default nemoclaw) so a wrapper that execs …/nemohermes is recognized as installer-managed.
  • src/lib/domain/uninstall/paths.ts: add agentAliasShimPaths (nemohermes, nemo-deepagents) under the same bin dir.
  • src/lib/actions/uninstall/plan.ts: classifyShimPath threads binName through both the fd-read and metadata paths.
  • src/lib/actions/uninstall/run-plan.ts: removeNemoclawCli now also classifies and removes each alias shim — reusing the existing guard, so a non-managed file of that name (foreign file) is preserved with a warning, exactly like the nemoclaw shim.
  • Tests: per-bin-name classification (managed wrapper matched by its own name, and a nemoclaw wrapper is not treated as a managed nemohermes shim); an end-to-end run-plan test that creates managed alias symlinks and asserts both are removed.

Safe by construction: symlinks and installer-managed wrappers are removed; any other regular file at those paths (e.g. an unrelated user script) is preserved. npm-installed alias bins remain handled by the existing npm uninstall -g nemoclaw step.

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)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: completes existing uninstall cleanup; no command/flag surface change.
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: uninstall/file-deletion path. Removal reuses the pre-existing classifyNemoclawShim guard (only symlinks + installer-managed wrappers removed; foreign files preserved), scoped to two fixed bin names in the resolved bin dir. 53 uninstall tests pass (51 pre-existing + 2 new), including a foreign-file-preserved assertion.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Git hooks passed during commit and push, or npx prek run --from-ref main --to-ref HEAD passes
  • Targeted tests pass for changed behavior
  • Full npm test passes (broad runtime changes only)
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run 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: Jason Ma jama@nvidia.com

Summary by CodeRabbit

  • New Features
    • Uninstall now removes supported CLI alias shims alongside the main binary.
    • Shim detection now supports alias wrappers for different command names.
  • Bug Fixes
    • Improved shim classification and uninstall handling for missing files, symlinks, and other edge cases involving aliases.
    • Preserved “foreign” shims are now recognized more reliably when alias wrappers are involved.
  • Tests
    • Added coverage for removing alias shims and correctly classifying alias wrapper contents.

…ents) (#6098)

nemoclaw uninstall removed the nemoclaw shim but left the sibling nemohermes / nemo-deepagents shims in ~/.local/bin, so those commands still resolved after a clean uninstall.

Generalize the shim classifier to match a managed wrapper by bin name (classifyNemoclawShim/isInstallerManagedWrapperContents/classifyShimPath accept binName, default nemoclaw), enumerate the alias shim paths, and remove them in removeNemoclawCli using the same classification guard so non-managed files of that name are preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Jason Ma <jama@nvidia.com>
@jason-ma-nv jason-ma-nv self-assigned this Jul 1, 2026
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds support for classifying and removing sibling agent-alias CLI shims during nemoclaw uninstall. The uninstall path model, shim classification helpers, run-plan flow, and tests all propagate alias bin names alongside the original nemoclaw shim.

Changes

Agent-alias shim removal

Layer / File(s) Summary
binName-aware shim classification
src/lib/domain/uninstall/shims.ts, src/lib/domain/uninstall/shims.test.ts
isInstallerManagedWrapperContents and classifyNemoclawShim accept an optional binName (default "nemoclaw") used to match the wrapper exec line, with tests validating hermes-alias wrapper classification.
Shim path classification with binName
src/lib/actions/uninstall/plan.ts
classifyShimPathByMetadata and classifyShimPath now accept and propagate binName through metadata fallback, fd-based classification, ENOENT handling, and error-code fallback paths.
Agent-alias path definitions
src/lib/domain/uninstall/paths.ts
Adds AGENT_ALIAS_CLI_BINARIES (nemohermes, nemo-deepagents) and agentAliasShimPaths on UninstallPaths, populated in defaultUninstallPaths().
Uninstall run-plan wiring and test
src/lib/actions/uninstall/run-plan.ts, src/lib/actions/uninstall/run-plan.test.ts
removeNemoclawCli iterates agentAliasShimPaths, classifying and removing installer-managed alias shims or warning when preserving foreign files; new tests verify both alias symlinks and wrapper scripts are removed during uninstall.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant RunPlan as run-plan.ts
    participant Paths as paths.ts
    participant Shims as shims.ts/plan.ts

    User->>RunPlan: runUninstallPlan()
    RunPlan->>Paths: get agentAliasShimPaths
    Paths-->>RunPlan: [{binName, path}, ...]
    loop for each alias shim
        RunPlan->>Shims: classifyShimPath(path, deps, binName)
        Shims-->>RunPlan: classification (managed-wrapper / preserve-foreign-file)
        alt managed-wrapper
            RunPlan->>RunPlan: remove shim
        else preserve-foreign-file
            RunPlan->>User: warn, leave shim in place
        end
    end
    RunPlan-->>User: uninstall result
Loading

Suggested labels: bug-fix

Suggested reviewers: jyaunches, cv

🚥 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 title clearly describes the main change: uninstall now removes agent-alias CLI shims.
Linked Issues check ✅ Passed The PR addresses #6098 by removing the nemohermes shim during uninstall and preserves non-managed files as required.
Out of Scope Changes check ✅ Passed The added nemo-deepagents support and tests stay within the uninstall alias-shim cleanup scope.
✨ 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 fix/6098-uninstall-remove-agent-alias-shims

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

@github-code-quality

github-code-quality Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the fix/6098-uninstall-r... branch is 96%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main fix/6098-uninstall-r... c98ec5b +/-
nemoclaw/src/se...cret-scanner.ts 100%
nemoclaw/src/commands/slash.ts 100%
nemoclaw/src/bl...eprint/state.ts 98%
nemoclaw/src/onboard/config.ts 98%
nemoclaw/src/bl...int/snapshot.ts 97%
nemoclaw/src/blueprint/ssrf.ts 97%
nemoclaw/src/bl...print/runner.ts 95%
nemoclaw/src/co...ration-state.ts 94%
nemoclaw/src/bl...ate-networks.ts 94%
nemoclaw/src/index.ts 94%

TypeScript / code-coverage/cli

The overall coverage in the fix/6098-uninstall-r... branch is 69%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main fix/6098-uninstall-r... c98ec5b +/-
src/lib/actions...dbox/rebuild.ts 82%
src/lib/actions...all/run-plan.ts 81%
src/lib/state/o...oard-session.ts 80%
src/lib/shields/index.ts 75%
src/lib/state/sandbox.ts 73%
src/lib/onboard...er-gpu-patch.ts 69%
src/lib/onboard/preflight.ts 69%
src/lib/actions...licy-channel.ts 59%
src/lib/policy/index.ts 56%
src/lib/onboard.ts 20%

Updated July 02, 2026 18:52 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — Changes requested

Merge posture: Do not merge yet
Primary next action: Fix PRA-4: Test monolith grew from 1350 to 1437 lines (87 lines) — extraction overdue; then add or justify PRA-T1.
Open items: 1 required · 4 warnings · 2 suggestions · 7 test follow-ups
Since last review: 1 prior item resolved · 3 still apply · 2 new items found

Action checklist

  • PRA-4 Fix: Test monolith grew from 1350 to 1437 lines (87 lines) — extraction overdue in src/lib/actions/uninstall/run-plan.test.ts:1
  • PRA-1 Resolve or justify: Source-of-truth review needed: binName-aware wrapper classification workaround
  • PRA-3 Resolve or justify: Missing integration test for foreign-file preservation on agent-alias shims in src/lib/actions/uninstall/run-plan.test.ts:108
  • PRA-5 Resolve or justify: Inline alias shim removal loop in removeNemoclawCli — extract to pure function in src/lib/actions/uninstall/run-plan.ts:628
  • PRA-6 Resolve or justify: Inconsistent deps argument to classifyShimPath for alias shims in src/lib/actions/uninstall/run-plan.ts:630
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation
  • PRA-T4 Add or justify test follow-up: Wrapper-file integration test for agent-alias shims added — resolves prior PRA-1
  • PRA-T5 Add or justify test follow-up: Missing integration test for foreign-file preservation on agent-alias shims
  • PRA-T6 Add or justify test follow-up: Acceptance clause
  • PRA-T7 Add or justify test follow-up: binName-aware wrapper classification workaround
  • PRA-2 In-scope improvement: Wrapper-file integration test for agent-alias shims added — resolves prior PRA-1 in src/lib/actions/uninstall/run-plan.test.ts:148
  • PRA-7 In-scope improvement: binName interpolation in wrapper check — add allowlist assertion for defense-in-depth in src/lib/domain/uninstall/shims.ts:45

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Improvement tests src/lib/actions/uninstall/run-plan.test.ts:148 No action needed; test is present and passing.
PRA-3 Resolve/justify tests src/lib/actions/uninstall/run-plan.test.ts:108 Add integration test creating a non-installer-managed regular file at alias shim path and asserting it is preserved with warning log. Do this in current PR — local to changed code.
PRA-4 Required architecture src/lib/actions/uninstall/run-plan.test.ts:1 Extract uninstall scenario helpers (createTempHome, runUninstallPlan, common mock factories) to a shared test utility file (e.g., src/lib/actions/uninstall/test-utils.ts). Do not remove new tests; extract surrounding scaffolding.
PRA-5 Resolve/justify architecture src/lib/actions/uninstall/run-plan.ts:628 Export function removeAgentAliasShims(paths: UninstallPaths, runtime: UninstallRuntime): void from run-plan.ts (or domain/uninstall/shims.ts) and call it from removeNemoclawCli. Resolve in current PR — local to changed code.
PRA-6 Resolve/justify correctness src/lib/actions/uninstall/run-plan.ts:630 Use undefined to match main shim call style: classifyShimPath(alias.path, undefined, alias.binName). Fix in current PR — local one-line change.
PRA-7 Improvement security src/lib/domain/uninstall/shims.ts:45 Add assertion or allowlist check that binName matches expected values (nemohermes, nemo-deepagents, nemoclaw) before using in string template. Low priority — can be follow-up.

🚨 Required before merge

Address these before merging unless a maintainer explicitly overrides the advisor with rationale.

PRA-4 Required — Test monolith grew from 1350 to 1437 lines (87 lines) — extraction overdue

  • Location: src/lib/actions/uninstall/run-plan.test.ts:1
  • Category: architecture
  • Problem: run-plan.test.ts is a large-file hotspot (1437 lines, 20+ named test blocks). The 87-line addition for alias shim tests is justified new behavior, but file size indicates extraction opportunity. Prior review flagged this at 1389 lines.
  • Impact: Large test files slow CI, reduce readability, increase merge conflict risk. Repeated tmpHome/mock runtime setup patterns across tests should be extracted.
  • Required action: Extract uninstall scenario helpers (createTempHome, runUninstallPlan, common mock factories) to a shared test utility file (e.g., src/lib/actions/uninstall/test-utils.ts). Do not remove new tests; extract surrounding scaffolding.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/uninstall/run-plan.test.ts shows 1437 lines; look for repeated tmpHome/mock runtime setup patterns across 20+ test blocks.
  • Missing regression test: No regression test needed; extraction must preserve all existing test behavior.
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/uninstall/run-plan.test.ts shows 1437 lines; look for repeated tmpHome/mock runtime setup patterns across 20+ test blocks.
  • Evidence: Drift context shows baseLines=1350, headLines=1437, delta=87, severity=blocker. File has 20 named test blocks with duplicated setup.
Review findings by urgency: 1 required fix, 4 items to resolve/justify, 2 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: binName-aware wrapper classification workaround

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: shims.test.ts:43-63 covers binName mismatch (nemoclaw wrapper ≠ managed nemohermes)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: shims.ts:45 interpolates binName; paths.ts:18 defines AGENT_ALIAS_CLI_BINARIES const; shims.test.ts:43-63 tests cross-binName rejection

PRA-3 Resolve/justify — Missing integration test for foreign-file preservation on agent-alias shims

  • Location: src/lib/actions/uninstall/run-plan.test.ts:108
  • Category: tests
  • Problem: Unit test in shims.test.ts covers classification logic (preserve-foreign-file for non-matching wrapper), but no integration test in run-plan.test.ts verifies a user-managed regular file at ~/.local/bin/nemohermes or ~/.local/bin/nemo-deepagents is preserved with warning log.
  • Impact: Regression could silently delete user scripts named nemohermes or nemo-deepagents in the bin directory. Main nemoclaw shim has this guard tested implicitly; alias shims lack equivalent integration coverage.
  • Recommended action: Add integration test creating a non-installer-managed regular file at alias shim path and asserting it is preserved with warning log. Do this in current PR — local to changed code.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check run-plan.test.ts for test creating fs.writeFileSync(hermesShim, '#!/bin/bash echo user') and asserting removed does not contain path and logs contain warning.
  • Missing regression test: it('preserves foreign file at nemohermes path with warning') and it('preserves foreign file at nemo-deepagents path with warning')
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check run-plan.test.ts for test creating fs.writeFileSync(hermesShim, '#!/bin/bash echo user') and asserting removed does not contain path and logs contain warning.
  • Evidence: run-plan.test.ts has no foreign-file test for alias shims; shims.test.ts:43-63 covers classification only.

PRA-5 Resolve/justify — Inline alias shim removal loop in removeNemoclawCli — extract to pure function

  • Location: src/lib/actions/uninstall/run-plan.ts:628
  • Category: architecture
  • Problem: The for-loop over paths.agentAliasShimPaths (lines 628-638) is self-contained classification-and-removal logic that could be a separate pure function for testability.
  • Impact: Large files increase cognitive load and coupling. Extracting keeps classification logic testable in isolation and matches the pattern used for other removal functions (removeAliases, removeNvmLeftovers).
  • Recommended action: Export function removeAgentAliasShims(paths: UninstallPaths, runtime: UninstallRuntime): void from run-plan.ts (or domain/uninstall/shims.ts) and call it from removeNemoclawCli. Resolve in current PR — local to changed code.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read run-plan.ts:628-638; the for-loop over paths.agentAliasShimPaths is self-contained.
  • Missing regression test: Unit test for extracted function with various ShimClassification outcomes (remove, preserve-foreign-file, missing).
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read run-plan.ts:628-638; the for-loop over paths.agentAliasShimPaths is self-contained.
  • Evidence: Drift context shows baseLines=953, headLines=963, delta=10, severity=warning.

PRA-6 Resolve/justify — Inconsistent deps argument to classifyShimPath for alias shims

  • Location: src/lib/actions/uninstall/run-plan.ts:630
  • Category: correctness
  • Problem: Main shim call uses default deps (classifyShimPath(paths.nemoclawShimPath) at line 625), but alias shim call passes empty object (classifyShimPath(alias.path, {}, alias.binName) at line 630). Functionally equivalent but inconsistent.
  • Impact: Minor inconsistency; could confuse future readers. No functional bug since classifyShimPath defaults deps to {}.
  • Recommended action: Use undefined to match main shim call style: classifyShimPath(alias.path, undefined, alias.binName). Fix in current PR — local one-line change.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Compare line 625 (main shim: classifyShimPath(paths.nemoclawShimPath)) with line 630 (alias: classifyShimPath(alias.path, {}, alias.binName)).
  • Missing regression test: None — style consistency only.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Compare line 625 (main shim: classifyShimPath(paths.nemoclawShimPath)) with line 630 (alias: classifyShimPath(alias.path, {}, alias.binName)).
  • Evidence: run-plan.ts:625 vs 630 show different call patterns for same function.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-2 Improvement — Wrapper-file integration test for agent-alias shims added — resolves prior PRA-1

  • Location: src/lib/actions/uninstall/run-plan.test.ts:148
  • Category: tests
  • Problem: New integration test 'removes agent-alias wrapper shims via binName-aware fd-read classification ([Linux][Install] nemoclaw uninstall leaves the nemohermes shim at ~/.local/bin/nemohermes (command still resolves) #6098)' creates installer-pattern wrapper files for nemohermes and nemo-deepagents and asserts removal via fd-read classification path.
  • Impact: Confirms end-to-end removal works for wrapper-file variant (not just symlinks). Risk of stale alias commands on platforms using wrapper files is now covered.
  • Suggested action: No action needed; test is present and passing.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Run test 'removes agent-alias wrapper shims via binName-aware fd-read classification ([Linux][Install] nemoclaw uninstall leaves the nemohermes shim at ~/.local/bin/nemohermes (command still resolves) #6098)' — it creates wrapper files matching installer pattern and asserts removal.
  • Missing regression test: Already covered by new integration test.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: run-plan.test.ts:148-202 creates managedWrapper('nemohermes') and managedWrapper('nemo-deepagents') with exact 3-line installer pattern, writes with mode 0o755, asserts both removed.

PRA-7 Improvement — binName interpolation in wrapper check — add allowlist assertion for defense-in-depth

  • Location: src/lib/domain/uninstall/shims.ts:45
  • Category: security
  • Problem: isInstallerManagedWrapperContents interpolates binName into string template: execLine.endsWith(`/${binName}" "$@`)`). binName currently comes from trusted AGENT_ALIAS_CLI_BINARIES const, but no assertion guards against future misuse if function is called from elsewhere.
  • Impact: Low risk currently (binName from const array), but defense-in-depth would prevent potential misclassification if binName ever sourced from untrusted input.
  • Suggested action: Add assertion or allowlist check that binName matches expected values (nemohermes, nemo-deepagents, nemoclaw) before using in string template. Low priority — can be follow-up.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check shims.ts:45 — binName is interpolated into string end check. AGENT_ALIAS_CLI_BINARIES in paths.ts is const array.
  • Missing regression test: Unit test verifying that unexpected binName values (e.g., with slashes, quotes) do not cause misclassification.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: shims.ts:45 uses binName in template literal without validation.
Simplification opportunities: 2 possible cuts, net -200 lines possible

These are safe simplification checks only. Do not remove validation, security controls, data-loss prevention, or required tests.

  • PRA-4 shrink (src/lib/actions/uninstall/run-plan.test.ts:1): Repeated test setup: tmpHome creation, mock runtime factory, log/removed array patterns
    • Replacement: Shared test utility module (e.g., src/lib/actions/uninstall/test-utils.ts) with createUninstallTestRuntime(), createTempHome()
    • Net: -200 lines
    • Safety boundary: Must preserve real fs/shell boundaries in integration tests; do not over-mock the uninstall execution path
  • PRA-5 shrink (src/lib/actions/uninstall/run-plan.ts:628): Inline for-loop in removeNemoclawCli (lines 628-638)
    • Replacement: export function removeAgentAliasShims(paths: UninstallPaths, runtime: UninstallRuntime): void { ... }
    • Net: 0 lines
    • Safety boundary: Must preserve same classification guard and warning behavior; no change to removal semantics
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — Integration test: foreign file at nemohermes path preserved with warning. Runtime/sandbox/infrastructure paths need behavioral runtime validation: src/lib/actions/uninstall/plan.ts, src/lib/actions/uninstall/run-plan.ts, src/lib/domain/uninstall/paths.ts, src/lib/domain/uninstall/shims.ts. Unit tests cover classification logic; integration tests cover symlink and wrapper-file variants. Foreign-file preservation integration test missing for alias shims.
  • PRA-T2 Runtime validation — Integration test: foreign file at nemo-deepagents path preserved with warning. Runtime/sandbox/infrastructure paths need behavioral runtime validation: src/lib/actions/uninstall/plan.ts, src/lib/actions/uninstall/run-plan.ts, src/lib/domain/uninstall/paths.ts, src/lib/domain/uninstall/shims.ts. Unit tests cover classification logic; integration tests cover symlink and wrapper-file variants. Foreign-file preservation integration test missing for alias shims.
  • PRA-T3 Runtime validation — Unit test: extracted removeAgentAliasShims function handles all ShimClassification kinds. Runtime/sandbox/infrastructure paths need behavioral runtime validation: src/lib/actions/uninstall/plan.ts, src/lib/actions/uninstall/run-plan.ts, src/lib/domain/uninstall/paths.ts, src/lib/domain/uninstall/shims.ts. Unit tests cover classification logic; integration tests cover symlink and wrapper-file variants. Foreign-file preservation integration test missing for alias shims.
  • PRA-T4 Wrapper-file integration test for agent-alias shims added — resolves prior PRA-1 — No action needed; test is present and passing.
  • PRA-T5 Missing integration test for foreign-file preservation on agent-alias shims — Add integration test creating a non-installer-managed regular file at alias shim path and asserting it is preserved with warning log. Do this in current PR — local to changed code.
  • PRA-T6 Acceptance clause — Foreign files at alias paths preserved with warning — add test evidence or identify existing coverage. Unit test in shims.test.ts:43-63 covers classification logic (preserve-foreign-file for non-matching wrapper), but no integration test in run-plan.test.ts (PRA-2)
  • PRA-T7 binName-aware wrapper classification workaround — shims.test.ts:43-63 covers binName mismatch (nemoclaw wrapper ≠ managed nemohermes). shims.ts:45 interpolates binName; paths.ts:18 defines AGENT_ALIAS_CLI_BINARIES const; shims.test.ts:43-63 tests cross-binName rejection
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: binName-aware wrapper classification workaround

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: shims.test.ts:43-63 covers binName mismatch (nemoclaw wrapper ≠ managed nemohermes)
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: shims.ts:45 interpolates binName; paths.ts:18 defines AGENT_ALIAS_CLI_BINARIES const; shims.test.ts:43-63 tests cross-binName rejection

PRA-2 Improvement — Wrapper-file integration test for agent-alias shims added — resolves prior PRA-1

  • Location: src/lib/actions/uninstall/run-plan.test.ts:148
  • Category: tests
  • Problem: New integration test 'removes agent-alias wrapper shims via binName-aware fd-read classification ([Linux][Install] nemoclaw uninstall leaves the nemohermes shim at ~/.local/bin/nemohermes (command still resolves) #6098)' creates installer-pattern wrapper files for nemohermes and nemo-deepagents and asserts removal via fd-read classification path.
  • Impact: Confirms end-to-end removal works for wrapper-file variant (not just symlinks). Risk of stale alias commands on platforms using wrapper files is now covered.
  • Suggested action: No action needed; test is present and passing.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Run test 'removes agent-alias wrapper shims via binName-aware fd-read classification ([Linux][Install] nemoclaw uninstall leaves the nemohermes shim at ~/.local/bin/nemohermes (command still resolves) #6098)' — it creates wrapper files matching installer pattern and asserts removal.
  • Missing regression test: Already covered by new integration test.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: run-plan.test.ts:148-202 creates managedWrapper('nemohermes') and managedWrapper('nemo-deepagents') with exact 3-line installer pattern, writes with mode 0o755, asserts both removed.

PRA-3 Resolve/justify — Missing integration test for foreign-file preservation on agent-alias shims

  • Location: src/lib/actions/uninstall/run-plan.test.ts:108
  • Category: tests
  • Problem: Unit test in shims.test.ts covers classification logic (preserve-foreign-file for non-matching wrapper), but no integration test in run-plan.test.ts verifies a user-managed regular file at ~/.local/bin/nemohermes or ~/.local/bin/nemo-deepagents is preserved with warning log.
  • Impact: Regression could silently delete user scripts named nemohermes or nemo-deepagents in the bin directory. Main nemoclaw shim has this guard tested implicitly; alias shims lack equivalent integration coverage.
  • Recommended action: Add integration test creating a non-installer-managed regular file at alias shim path and asserting it is preserved with warning log. Do this in current PR — local to changed code.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check run-plan.test.ts for test creating fs.writeFileSync(hermesShim, '#!/bin/bash echo user') and asserting removed does not contain path and logs contain warning.
  • Missing regression test: it('preserves foreign file at nemohermes path with warning') and it('preserves foreign file at nemo-deepagents path with warning')
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check run-plan.test.ts for test creating fs.writeFileSync(hermesShim, '#!/bin/bash echo user') and asserting removed does not contain path and logs contain warning.
  • Evidence: run-plan.test.ts has no foreign-file test for alias shims; shims.test.ts:43-63 covers classification only.

PRA-4 Required — Test monolith grew from 1350 to 1437 lines (87 lines) — extraction overdue

  • Location: src/lib/actions/uninstall/run-plan.test.ts:1
  • Category: architecture
  • Problem: run-plan.test.ts is a large-file hotspot (1437 lines, 20+ named test blocks). The 87-line addition for alias shim tests is justified new behavior, but file size indicates extraction opportunity. Prior review flagged this at 1389 lines.
  • Impact: Large test files slow CI, reduce readability, increase merge conflict risk. Repeated tmpHome/mock runtime setup patterns across tests should be extracted.
  • Required action: Extract uninstall scenario helpers (createTempHome, runUninstallPlan, common mock factories) to a shared test utility file (e.g., src/lib/actions/uninstall/test-utils.ts). Do not remove new tests; extract surrounding scaffolding.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: wc -l src/lib/actions/uninstall/run-plan.test.ts shows 1437 lines; look for repeated tmpHome/mock runtime setup patterns across 20+ test blocks.
  • Missing regression test: No regression test needed; extraction must preserve all existing test behavior.
  • Done when: The required change is committed and verification passes: wc -l src/lib/actions/uninstall/run-plan.test.ts shows 1437 lines; look for repeated tmpHome/mock runtime setup patterns across 20+ test blocks.
  • Evidence: Drift context shows baseLines=1350, headLines=1437, delta=87, severity=blocker. File has 20 named test blocks with duplicated setup.

PRA-5 Resolve/justify — Inline alias shim removal loop in removeNemoclawCli — extract to pure function

  • Location: src/lib/actions/uninstall/run-plan.ts:628
  • Category: architecture
  • Problem: The for-loop over paths.agentAliasShimPaths (lines 628-638) is self-contained classification-and-removal logic that could be a separate pure function for testability.
  • Impact: Large files increase cognitive load and coupling. Extracting keeps classification logic testable in isolation and matches the pattern used for other removal functions (removeAliases, removeNvmLeftovers).
  • Recommended action: Export function removeAgentAliasShims(paths: UninstallPaths, runtime: UninstallRuntime): void from run-plan.ts (or domain/uninstall/shims.ts) and call it from removeNemoclawCli. Resolve in current PR — local to changed code.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read run-plan.ts:628-638; the for-loop over paths.agentAliasShimPaths is self-contained.
  • Missing regression test: Unit test for extracted function with various ShimClassification outcomes (remove, preserve-foreign-file, missing).
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read run-plan.ts:628-638; the for-loop over paths.agentAliasShimPaths is self-contained.
  • Evidence: Drift context shows baseLines=953, headLines=963, delta=10, severity=warning.

PRA-6 Resolve/justify — Inconsistent deps argument to classifyShimPath for alias shims

  • Location: src/lib/actions/uninstall/run-plan.ts:630
  • Category: correctness
  • Problem: Main shim call uses default deps (classifyShimPath(paths.nemoclawShimPath) at line 625), but alias shim call passes empty object (classifyShimPath(alias.path, {}, alias.binName) at line 630). Functionally equivalent but inconsistent.
  • Impact: Minor inconsistency; could confuse future readers. No functional bug since classifyShimPath defaults deps to {}.
  • Recommended action: Use undefined to match main shim call style: classifyShimPath(alias.path, undefined, alias.binName). Fix in current PR — local one-line change.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Compare line 625 (main shim: classifyShimPath(paths.nemoclawShimPath)) with line 630 (alias: classifyShimPath(alias.path, {}, alias.binName)).
  • Missing regression test: None — style consistency only.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Compare line 625 (main shim: classifyShimPath(paths.nemoclawShimPath)) with line 630 (alias: classifyShimPath(alias.path, {}, alias.binName)).
  • Evidence: run-plan.ts:625 vs 630 show different call patterns for same function.

PRA-7 Improvement — binName interpolation in wrapper check — add allowlist assertion for defense-in-depth

  • Location: src/lib/domain/uninstall/shims.ts:45
  • Category: security
  • Problem: isInstallerManagedWrapperContents interpolates binName into string template: execLine.endsWith(`/${binName}" "$@`)`). binName currently comes from trusted AGENT_ALIAS_CLI_BINARIES const, but no assertion guards against future misuse if function is called from elsewhere.
  • Impact: Low risk currently (binName from const array), but defense-in-depth would prevent potential misclassification if binName ever sourced from untrusted input.
  • Suggested action: Add assertion or allowlist check that binName matches expected values (nemohermes, nemo-deepagents, nemoclaw) before using in string template. Low priority — can be follow-up.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check shims.ts:45 — binName is interpolated into string end check. AGENT_ALIAS_CLI_BINARIES in paths.ts is const array.
  • Missing regression test: Unit test verifying that unexpected binName values (e.g., with slashes, quotes) do not cause misclassification.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: shims.ts:45 uses binName in template literal without validation.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: full-e2e
Optional E2E: hermes-e2e

Dispatch hint: full-e2e

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: medium

Required E2E

  • full-e2e (high): Closest existing live coverage for installer/onboarding/CLI lifecycle. It validates install.sh, host PATH wiring for nemoclaw/openshell, onboard, inference, CLI operations, and cleanup after changes in uninstall-adjacent installer cleanup code.

Optional E2E

  • hermes-e2e (high): Useful adjacent confidence because the changed uninstall path now explicitly handles the nemohermes alias shim, and this job exercises real Hermes flows through the nemohermes host CLI entrypoint.

New E2E recommendations

  • uninstall / host CLI shims (high): No existing live E2E appears to run the real uninstall command and assert that installer-managed nemoclaw, nemohermes, and nemo-deepagents shims are removed while foreign files are preserved.
    • Suggested test: Add an uninstall live E2E that installs from source, verifies alias shims exist on PATH, runs nemoclaw uninstall --yes, and asserts managed shims/resources are removed without deleting foreign files.

Dispatch hint

  • Workflow: .github/workflows/e2e.yaml
  • jobs input: full-e2e

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: None
Optional E2E targets: None

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • None. Changes are limited to uninstall planning/runtime shim cleanup and related unit tests. The current e2e.yaml registry-target live path does not exercise uninstall behavior, so there is no dispatchable E2E target that would specifically validate this surface.

Optional E2E targets

  • None.

Relevant changed files

  • None.

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Changes requested

Merge posture: Do not merge yet
Primary next action: Resolve or justify PRA-1: Source-of-truth review needed: Agent alias shim bin names.
Open items: 0 required · 3 warnings · 0 suggestions · 6 test follow-ups
Since last review: 0 prior items resolved · 2 still apply · 0 new items found

Action checklist

  • PRA-1 Resolve or justify: Source-of-truth review needed: Agent alias shim bin names
  • PRA-2 Resolve or justify: Constrain exported shim bin names before using them as deletion classifiers in src/lib/domain/uninstall/shims.ts:32
  • PRA-3 Resolve or justify: Add run-plan negative coverage for user-managed alias executables in src/lib/actions/uninstall/run-plan.test.ts:165
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Add run-plan negative coverage for user-managed alias executables
  • PRA-T4 Add or justify test follow-up: Acceptance clause
  • PRA-T5 Add or justify test follow-up: Acceptance clause
  • PRA-T6 Add or justify test follow-up: Agent alias shim bin names

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify architecture Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
PRA-2 Resolve/justify security src/lib/domain/uninstall/shims.ts:32 Make accepted bin names explicit, either by introducing a literal union sourced from the managed CLI constants or by adding a runtime allowlist that returns a non-removable classification for unsupported names before matching wrapper contents.
PRA-3 Resolve/justify tests src/lib/actions/uninstall/run-plan.test.ts:165 Add a focused run-plan test using a temp home with a real regular executable at `/.local/bin/nemohermes` or `/.local/bin/nemo-deepagents` containing non-managed contents; assert `rmSync` is not called for that path and the preservation warning is emitted.
Review findings by urgency: 0 required fixes, 3 items to resolve/justify, 0 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Source-of-truth review needed: Agent alias shim bin names

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Covered by the security finding's requested unsupported-bin-name classifier test.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `AGENT_ALIAS_CLI_BINARIES = ["nemohermes", "nemo-deepagents"]`, but `isInstallerManagedWrapperContents(contents, binName = "nemoclaw")` accepts a plain string.

PRA-2 Resolve/justify — Constrain exported shim bin names before using them as deletion classifiers

  • Location: src/lib/domain/uninstall/shims.ts:32
  • Category: security
  • Problem: `isInstallerManagedWrapperContents` and `classifyNemoclawShim` accept an exported `binName: string` and use it to decide whether a regular file is an installer-managed wrapper eligible for deletion. Current production callers pass fixed values from `agentAliasShimPaths`, but the deletion-boundary API does not encode or validate that only `nemoclaw`, `nemohermes`, and `nemo-deepagents` are managed CLI names.
  • Impact: A future caller could pass an unsupported or malformed bin name and unintentionally widen the regular-file deletion classifier. This PR expands uninstall's deletion surface, so the accepted wrapper names should be a source-of-truth allowlist rather than an arbitrary string.
  • Recommended action: Make accepted bin names explicit, either by introducing a literal union sourced from the managed CLI constants or by adding a runtime allowlist that returns a non-removable classification for unsupported names before matching wrapper contents.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `src/lib/domain/uninstall/shims.ts` and confirm `binName` is no longer an unconstrained `string`; then read `src/lib/actions/uninstall/run-plan.ts` and confirm alias cleanup still passes only the fixed managed alias names.
  • Missing regression test: Add `does not classify wrappers for unsupported shim bin names as managed`: pass an unsupported bin name with otherwise wrapper-shaped contents and assert the classifier does not return `{ kind: "managed-wrapper", remove: true }`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `src/lib/domain/uninstall/shims.ts` and confirm `binName` is no longer an unconstrained `string`; then read `src/lib/actions/uninstall/run-plan.ts` and confirm alias cleanup still passes only the fixed managed alias names.
  • Evidence: `execLine.endsWith(`/${binName}" "$@"`)` is driven by an exported parameter, and `removeNemoclawCli` removes paths when `classifyShimPath(alias.path, {}, alias.binName)` returns `remove: true`.

PRA-3 Resolve/justify — Add run-plan negative coverage for user-managed alias executables

  • Location: src/lib/actions/uninstall/run-plan.test.ts:165
  • Category: tests
  • Problem: The new tests cover managed alias symlinks and managed three-line wrapper files, and the domain test covers bin-specific wrapper mismatch. The full uninstall run path still lacks a negative test proving a regular user-managed file named `nemohermes` or `nemo-deepagents` is preserved and warned about.
  • Impact: This PR adds new filenames to uninstall's removal loop. Without run-plan-level negative coverage, a later refactor could delete user scripts at those alias paths while the current positive deletion tests continue to pass.
  • Recommended action: Add a focused run-plan test using a temp home with a real regular executable at `/.local/bin/nemohermes` or `/.local/bin/nemo-deepagents` containing non-managed contents; assert `rmSync` is not called for that path and the preservation warning is emitted.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the new tests in `src/lib/actions/uninstall/run-plan.test.ts` around the alias cases; they create managed symlinks and managed wrapper files, but no foreign regular alias file.
  • Missing regression test: Add `preserves user-managed agent-alias executables during uninstall`: create `~/.local/bin/nemohermes` with `#!/usr/bin/env bash\necho user\n`, run uninstall with `assumeYes: true`, assert the alias path is absent from `removed`, and assert the warning says it is not an installer-managed shim.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the new tests in `src/lib/actions/uninstall/run-plan.test.ts` around the alias cases; they create managed symlinks and managed wrapper files, but no foreign regular alias file.
  • Evidence: The issue caveat notes the leftover may have pre-existed, and `run-plan.ts` claims the classification guard preserves non-managed alias files; the changed run-plan tests only assert removal for managed symlinks and managed wrappers.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

  • None.
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — preserves user-managed agent-alias executables during uninstall. The changed behavior is local uninstall/file-deletion logic. The added tests use real temp files for positive removal paths, but the expanded deletion surface needs negative runtime-style coverage for foreign alias files and classifier coverage for unsupported names.
  • PRA-T2 Runtime validation — does not classify wrappers for unsupported shim bin names as managed. The changed behavior is local uninstall/file-deletion logic. The added tests use real temp files for positive removal paths, but the expanded deletion surface needs negative runtime-style coverage for foreign alias files and classifier coverage for unsupported names.
  • PRA-T3 Add run-plan negative coverage for user-managed alias executables — Add a focused run-plan test using a temp home with a real regular executable at `/.local/bin/nemohermes` or `/.local/bin/nemo-deepagents` containing non-managed contents; assert `rmSync` is not called for that path and the preservation warning is emitted.
  • PRA-T4 Acceptance clause — After uninstall, neither `nemoclaw` nor `nemohermes` resolves as a command. — add test evidence or identify existing coverage. The unit-level run-plan tests verify the underlying alias files are removed via `rmSync`; they do not perform a shell `PATH` resolution check, which is reasonable for this layer but not direct evidence of command resolution.
  • PRA-T5 Acceptance clause — **Caveat (honest QA note):** on the test VM the leftover `nemohermes` had mtime `03:35`, *before* this run's install (`03:54`) — i.e. the VM was not verified clean and the shim may be a leftover from a prior install. — add test evidence or identify existing coverage. The code uses the existing managed-shim guard, but the run-plan tests do not yet prove that a non-managed pre-existing regular alias file is preserved with a warning.
  • PRA-T6 Agent alias shim bin names — Covered by the security finding's requested unsupported-bin-name classifier test.. `AGENT_ALIAS_CLI_BINARIES = ["nemohermes", "nemo-deepagents"]`, but `isInstallerManagedWrapperContents(contents, binName = "nemoclaw")` accepts a plain string.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: Agent alias shim bin names

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as needs_followup.
  • Impact: A localized workaround can preserve or hide an invalid state when the source boundary is unclear.
  • Recommended action: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Missing regression test: Covered by the security finding's requested unsupported-bin-name classifier test.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the localized patch and source-of-truth review fields for a concrete invalid state, source boundary, source-fix constraint, regression test, and removal condition.
  • Evidence: `AGENT_ALIAS_CLI_BINARIES = ["nemohermes", "nemo-deepagents"]`, but `isInstallerManagedWrapperContents(contents, binName = "nemoclaw")` accepts a plain string.

PRA-2 Resolve/justify — Constrain exported shim bin names before using them as deletion classifiers

  • Location: src/lib/domain/uninstall/shims.ts:32
  • Category: security
  • Problem: `isInstallerManagedWrapperContents` and `classifyNemoclawShim` accept an exported `binName: string` and use it to decide whether a regular file is an installer-managed wrapper eligible for deletion. Current production callers pass fixed values from `agentAliasShimPaths`, but the deletion-boundary API does not encode or validate that only `nemoclaw`, `nemohermes`, and `nemo-deepagents` are managed CLI names.
  • Impact: A future caller could pass an unsupported or malformed bin name and unintentionally widen the regular-file deletion classifier. This PR expands uninstall's deletion surface, so the accepted wrapper names should be a source-of-truth allowlist rather than an arbitrary string.
  • Recommended action: Make accepted bin names explicit, either by introducing a literal union sourced from the managed CLI constants or by adding a runtime allowlist that returns a non-removable classification for unsupported names before matching wrapper contents.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `src/lib/domain/uninstall/shims.ts` and confirm `binName` is no longer an unconstrained `string`; then read `src/lib/actions/uninstall/run-plan.ts` and confirm alias cleanup still passes only the fixed managed alias names.
  • Missing regression test: Add `does not classify wrappers for unsupported shim bin names as managed`: pass an unsupported bin name with otherwise wrapper-shaped contents and assert the classifier does not return `{ kind: "managed-wrapper", remove: true }`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `src/lib/domain/uninstall/shims.ts` and confirm `binName` is no longer an unconstrained `string`; then read `src/lib/actions/uninstall/run-plan.ts` and confirm alias cleanup still passes only the fixed managed alias names.
  • Evidence: `execLine.endsWith(`/${binName}" "$@"`)` is driven by an exported parameter, and `removeNemoclawCli` removes paths when `classifyShimPath(alias.path, {}, alias.binName)` returns `remove: true`.

PRA-3 Resolve/justify — Add run-plan negative coverage for user-managed alias executables

  • Location: src/lib/actions/uninstall/run-plan.test.ts:165
  • Category: tests
  • Problem: The new tests cover managed alias symlinks and managed three-line wrapper files, and the domain test covers bin-specific wrapper mismatch. The full uninstall run path still lacks a negative test proving a regular user-managed file named `nemohermes` or `nemo-deepagents` is preserved and warned about.
  • Impact: This PR adds new filenames to uninstall's removal loop. Without run-plan-level negative coverage, a later refactor could delete user scripts at those alias paths while the current positive deletion tests continue to pass.
  • Recommended action: Add a focused run-plan test using a temp home with a real regular executable at `/.local/bin/nemohermes` or `/.local/bin/nemo-deepagents` containing non-managed contents; assert `rmSync` is not called for that path and the preservation warning is emitted.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the new tests in `src/lib/actions/uninstall/run-plan.test.ts` around the alias cases; they create managed symlinks and managed wrapper files, but no foreign regular alias file.
  • Missing regression test: Add `preserves user-managed agent-alias executables during uninstall`: create `~/.local/bin/nemohermes` with `#!/usr/bin/env bash\necho user\n`, run uninstall with `assumeYes: true`, assert the alias path is absent from `removed`, and assert the warning says it is not an installer-managed shim.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the new tests in `src/lib/actions/uninstall/run-plan.test.ts` around the alias cases; they create managed symlinks and managed wrapper files, but no foreign regular alias file.
  • Evidence: The issue caveat notes the leftover may have pre-existed, and `run-plan.ts` claims the classification guard preserves non-managed alias files; the changed run-plan tests only assert removal for managed symlinks and managed wrappers.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

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

🧹 Nitpick comments (1)
src/lib/actions/uninstall/run-plan.test.ts (1)

165-203: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test only exercises the symlink path, not the new binName-aware wrapper classification.

Both alias shims here are plain symlinks, which classifyShimPath resolves via the isSymlink branch — a path that doesn't depend on binName at all. This test doesn't actually prove that removeNemoclawCli's new classifyShimPath(alias.path, {}, alias.binName) call correctly wires binName through to isInstallerManagedWrapperContents for a wrapper-style shim; only the pure-function unit tests in shims.test.ts cover that. Consider adding a wrapper-script variant (installer-managed wrapper content, per-bin) alongside the symlink case to close this gap end-to-end.

As per path instructions, tests should provide "behavioral confidence rather than implementation lock-in" and flag "conditionals that make a test pass without exercising its claim."

🤖 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/lib/actions/uninstall/run-plan.test.ts` around lines 165 - 203, The
uninstall test only covers plain symlink cleanup and does not exercise the new
binName-aware wrapper classification path. Update the test around
runUninstallPlan/removeNemoclawCli to include a wrapper-script fixture with
installer-managed contents and a specific binName so
classifyShimPath(alias.path, {}, alias.binName) is actually validated
end-to-end, while keeping the existing symlink case as a separate behavioral
check. Use the existing symbols removeNemoclawCli, runUninstallPlan, and
classifyShimPath to locate the right test area.

Source: Path instructions

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

Nitpick comments:
In `@src/lib/actions/uninstall/run-plan.test.ts`:
- Around line 165-203: The uninstall test only covers plain symlink cleanup and
does not exercise the new binName-aware wrapper classification path. Update the
test around runUninstallPlan/removeNemoclawCli to include a wrapper-script
fixture with installer-managed contents and a specific binName so
classifyShimPath(alias.path, {}, alias.binName) is actually validated
end-to-end, while keeping the existing symlink case as a separate behavioral
check. Use the existing symbols removeNemoclawCli, runUninstallPlan, and
classifyShimPath to locate the right test area.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: e1d32049-1624-44c5-a215-f255824b862f

📥 Commits

Reviewing files that changed from the base of the PR and between e4b9111 and f2870f0.

📒 Files selected for processing (6)
  • 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/shims.test.ts
  • src/lib/domain/uninstall/shims.ts

@wscurran wscurran added area: install Install, setup, prerequisites, or uninstall flow bug-fix PR fixes a bug or regression labels Jul 1, 2026
@wscurran

wscurran commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

@wscurran wscurran added integration: hermes Hermes integration behavior integration: dcode LangChain Deep Code integration behavior labels Jul 1, 2026
@cjagwani cjagwani self-assigned this Jul 2, 2026
…ppers (#6098)

The existing agent-alias test (#6098) only creates symlinks, which
classify via classifyShimPath's metadata fast path. It does not
exercise the fd-read branch that reads the wrapper contents and
matches against the per-alias binName — the new behavior this PR
introduces.

Add a second run-plan test that writes real installer-managed wrapper
files (with the correct 'exec ..../{binName}' line) at each alias path
and asserts both are removed. CodeRabbit follow-up on #6101.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: Charan Jagwani <cjagwani@nvidia.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.

🧹 Nitpick comments (1)
src/lib/actions/uninstall/run-plan.test.ts (1)

165-251: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider deduplicating the two alias-shim uninstall tests.

This new test (lines 204-251) duplicates nearly all boilerplate from the adjacent symlink test (lines 165-202) — temp dir setup, runtime options object, assertions, and teardown — differing only in how the shim artifact is created (symlink vs. wrapper script). Extracting a shared helper that takes a createShim(path, binName) callback would reduce duplication and keep both cases easy to maintain as more alias binaries are added.

Separately, since the fixtures already use the real filesystem (symlinkSync, writeFileSync), consider asserting on real post-run filesystem state (fs.existsSync(hermesShim) etc.) in addition to/instead of the mocked rmSync call capture — this exercises the same public runUninstallPlan entrypoint while reducing reliance on mock-call assertions. That said, this repo already uses injected rmSync for testability per its actions/adapters layering, so the current approach is consistent with existing conventions and not a functional problem.

The static-analysis path-traversal warnings on lines 219-221 are false positives — the paths originate from mkdtempSync-generated temp directories combined with hardcoded bin names, not external input.

🤖 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/lib/actions/uninstall/run-plan.test.ts` around lines 165 - 251, The two
alias-shim uninstall tests in run-plan.test.ts are duplicating the same setup,
options, assertions, and cleanup, differing only in how the shim is created.
Refactor them by extracting a shared helper around runUninstallPlan that accepts
a createShim(path, binName) callback (or similar) and reuse it for both the
symlink and wrapper-script cases. While touching the tests, consider asserting
the real filesystem state after runUninstallPlan in addition to the injected
rmSync capture, using the existing hermesShim and deepagentsShim fixtures to
keep the checks focused and maintainable.

Source: Path instructions

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

Nitpick comments:
In `@src/lib/actions/uninstall/run-plan.test.ts`:
- Around line 165-251: The two alias-shim uninstall tests in run-plan.test.ts
are duplicating the same setup, options, assertions, and cleanup, differing only
in how the shim is created. Refactor them by extracting a shared helper around
runUninstallPlan that accepts a createShim(path, binName) callback (or similar)
and reuse it for both the symlink and wrapper-script cases. While touching the
tests, consider asserting the real filesystem state after runUninstallPlan in
addition to the injected rmSync capture, using the existing hermesShim and
deepagentsShim fixtures to keep the checks focused and maintainable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a13f1847-7bad-4929-9ce5-1bb9d028e561

📥 Commits

Reviewing files that changed from the base of the PR and between f2870f0 and c98ec5b.

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

@wscurran
wscurran requested a review from cv July 2, 2026 22:06
@cv
cv merged commit 640b814 into main Jul 3, 2026
56 of 59 checks passed
@cv
cv deleted the fix/6098-uninstall-remove-agent-alias-shims branch July 3, 2026 07:31
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
…ents) (NVIDIA#6098) (NVIDIA#6101)

## Summary

`nemoclaw uninstall` removed the `nemoclaw` CLI shim, the `openshell*`
binaries, and `~/.nemoclaw/`, but left the sibling **agent-alias shims**
(`nemohermes`, `nemo-deepagents`) in `~/.local/bin`. After a "clean"
uninstall those commands still resolved and reported a version. This
removes them too, using the same installer-managed-shim safety
classification.

## Related Issue

Fixes NVIDIA#6098

## Changes

- `src/lib/domain/uninstall/shims.ts`: `classifyNemoclawShim` /
`isInstallerManagedWrapperContents` accept an optional `binName`
(default `nemoclaw`) so a wrapper that execs `…/nemohermes` is
recognized as installer-managed.
- `src/lib/domain/uninstall/paths.ts`: add `agentAliasShimPaths`
(`nemohermes`, `nemo-deepagents`) under the same bin dir.
- `src/lib/actions/uninstall/plan.ts`: `classifyShimPath` threads
`binName` through both the fd-read and metadata paths.
- `src/lib/actions/uninstall/run-plan.ts`: `removeNemoclawCli` now also
classifies and removes each alias shim — reusing the existing guard, so
a **non-managed file** of that name (foreign file) is preserved with a
warning, exactly like the `nemoclaw` shim.
- Tests: per-bin-name classification (managed wrapper matched by its own
name, and a `nemoclaw` wrapper is *not* treated as a managed
`nemohermes` shim); an end-to-end run-plan test that creates managed
alias symlinks and asserts both are removed.

Safe by construction: symlinks and installer-managed wrappers are
removed; any other regular file at those paths (e.g. an unrelated user
script) is preserved. npm-installed alias bins remain handled by the
existing `npm uninstall -g nemoclaw` step.

## Type of Change

- [x] 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)

## Quality Gates
- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: completes existing uninstall
cleanup; no command/flag surface change.
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: uninstall/file-deletion
path. Removal reuses the pre-existing `classifyNemoclawShim` guard (only
symlinks + installer-managed wrappers removed; foreign files preserved),
scoped to two fixed bin names in the resolved bin dir. 53 uninstall
tests pass (51 pre-existing + 2 new), including a foreign-file-preserved
assertion.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Git hooks passed during commit and push, or `npx prek run
--from-ref main --to-ref HEAD` passes
- [x] Targeted tests pass for changed behavior
- [ ] Full `npm test` passes (broad runtime changes only)
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run 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: Jason Ma <jama@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Uninstall now removes supported CLI alias shims alongside the main
binary.
* Shim detection now supports alias wrappers for different command
names.
* **Bug Fixes**
* Improved shim classification and uninstall handling for missing files,
symlinks, and other edge cases involving aliases.
* Preserved “foreign” shims are now recognized more reliably when alias
wrappers are involved.
* **Tests**
* Added coverage for removing alias shims and correctly classifying
alias wrapper contents.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Jason Ma <jama@nvidia.com>
Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Charan Jagwani <cjagwani@nvidia.com>
prekshivyas added a commit that referenced this pull request Aug 19, 2026
)

## Summary

`nemoclaw uninstall` reports success while leaving the NemoClaw CLI
binaries installed under every
nvm node version other than the one selected at uninstall time.
`removeNvmLeftovers` matched only
`entry.isFile()` on a path ending in `bin/nemoclaw`, but
`fs.readdirSync(…, { withFileTypes: true })`
uses `lstat` semantics and npm publishes every declared bin as a
symlink, so that branch never ran.
It also only matched `nemoclaw`, never the `nemohermes` and
`nemo-deepagents` bins that #6098 taught
the `~/.local/bin` sweep about. After this change the sweep removes all
three names whether they are
regular files or symlinks, so the commands stop resolving.

## Related Issue

Fixes #9500

## Changes

- `src/lib/actions/uninstall/run-plan.ts` — `removeNvmLeftovers`
enumerates each direct Node-version
directory under `$NVM_DIR/versions/node` and inspects only
`<version>/lib/node_modules` and
`<version>/bin`, instead of walking the whole tree with `entry.isFile()
&& target.endsWith(…)`.
Alias names come from `paths.agentAliasShimPaths`, so no new module edge
or constant is introduced.
- `src/lib/actions/uninstall/run-plan.test.ts` — extended the existing
`removes agent-alias CLI shims … (#6098)` case rather than adding a
parallel one.

No abstraction, configuration, fallback, migration, or compatibility
path is added.

### Scope of deletion

Only an entry named exactly `nemoclaw`, `nemohermes`, or
`nemo-deepagents` that is a regular file or a
symlink, whose parent is the `bin` directory of a direct child of
`$HOME/.nvm/versions/node`. Deletion
stays `rmSync(target, { force: true })` with no `recursive`, so a
symlink is unlinked and never followed.
A nested `<version>/lib/node_modules/<package>/bin/<name>` is not
reachable — the sweep never descends
there. The module branch still matches a real directory named `nemoclaw`
directly under
`<version>/lib/node_modules`, so an `npm link`-ed source checkout, which
is a symlink, is left in place.
`node`, `npm`, other global packages' bins, and the user's nvm
installation are untouched. The regression
test asserts the exact set of created paths that the run removes,
including three foreign package bins and
an unrelated `<version>/bin/tsc` that must survive.
## Type of Change

- [x] 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)

## Quality Gates

- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification

- [x] PR description includes a `Signed-off-by:` line and every commit
appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run validate:pr` passed after refreshing `origin/main` when hooks
were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification:
`npx vitest run --project cli
src/lib/actions/uninstall/run-plan.test.ts` → 40 passed. The extended
`#6098` case fails on `main` (the three
`.nvm/versions/node/v22.19.0/bin/*` paths are absent from
the removed set) and passes with this change. `npm run typecheck:cli`
and
  `npx oxlint src/lib/actions/uninstall/` are clean.
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run 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)

### Notes for reviewers

- `src/lib/actions/uninstall/run-plan.test.ts` goes 1495 → 1500 lines,
exactly at
`ci/test-file-size-budget.json`'s `defaultMaxLines`. The existing setup
comment was re-worded in
place to pay for the added lines inside the same case, so no new
scenario or helper was created.
- Prior art: #6098 / #6101 fixed the same class for `~/.local/bin` and
introduced
`AGENT_ALIAS_CLI_BINARIES`, which this change reuses. #7905 is a related
open report on a
different code path (gateway scoping on a Homebrew node) and is
intentionally not closed here.

---

Signed-off-by: Udaya Tejas <udayatejas2004@gmail.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved uninstall reliability when sandbox deletion reports that a
resource is already absent.
* Portable cleanup now completes in the correct order and avoids
unnecessary work for non-portable installations.
* Improved removal of NVM-related CLI links and packages, including
broken links.
* Uninstall now preserves unrelated packages, binaries, linked packages,
and files across Node.js versions.
* Cleanup safely skips inaccessible directories while allowing uninstall
to finish successfully.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Udaya Tejas <udayatejas2004@gmail.com>
Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
Co-authored-by: Prekshi Vyas <prekshiv@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: install Install, setup, prerequisites, or uninstall flow bug-fix PR fixes a bug or regression integration: dcode LangChain Deep Code integration behavior integration: hermes Hermes integration behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Linux][Install] nemoclaw uninstall leaves the nemohermes shim at ~/.local/bin/nemohermes (command still resolves)

4 participants