Skip to content

fix(installer): remove nemohermes shim on uninstall - #6161

Closed
Dongni-Yang wants to merge 1 commit into
mainfrom
fix/6098-nemohermes-shim-not-removed-on-uninstall
Closed

fix(installer): remove nemohermes shim on uninstall#6161
Dongni-Yang wants to merge 1 commit into
mainfrom
fix/6098-nemohermes-shim-not-removed-on-uninstall

Conversation

@Dongni-Yang

@Dongni-Yang Dongni-Yang commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

nemoclaw uninstall removed the nemoclaw shim at ~/.local/bin/nemoclaw but left the sibling nemohermes shim installed by the Hermes variant untouched, so nemohermes kept resolving as a command after a clean uninstall. This PR adds nemohermsShimPath to UninstallPaths and extends removeNemoclawCli() to classify and remove it using the same installer-shim guard already applied to the nemoclaw shim.

Related Issue

Fixes #6098

Changes

  • src/lib/domain/uninstall/paths.ts — add nemohermsShimPath: string to UninstallPaths interface and populate it as ~/.local/bin/nemohermes in defaultUninstallPaths()
  • src/lib/actions/uninstall/run-plan.ts — after the existing nemoclaw shim block in removeNemoclawCli(), add a matching classify-then-remove block for paths.nemohermsShimPath, including the preserve-foreign-file warning for non-installer-managed files
  • src/lib/domain/uninstall/paths.test.ts — assert nemohermsShimPath === ~/.local/bin/nemohermes
  • src/lib/actions/uninstall/run-plan.test.ts — red→green test: creates a real symlink at tmpHome/.local/bin/nemohermes, runs runUninstallPlan, asserts rmSync is called for it and the symlink is gone

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 not applicable — justification: uninstall behavior change; no user-facing docs reference the nemohermes shim removal path
  • Docs updated for user-facing behavior changes
  • 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

  • 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: Dongni Yang dongniy@nvidia.com

Summary by CodeRabbit

  • Bug Fixes
    • Uninstall now also removes the installer-managed nemohermes command shim when appropriate.
    • Preserved foreign shims are now left in place with a warning instead of being removed.
    • Default uninstall path handling now includes the nemohermes shim location under the user’s local bin directory.

removeNemoclawCli() only classified and removed paths.nemoclawShimPath
(~/.local/bin/nemoclaw). The Hermes install variant additionally writes
~/.local/bin/nemohermes, but no path field or removal block existed for
it, so the shim survived uninstall and kept resolving as a command.

Add nemohermsShimPath to UninstallPaths / defaultUninstallPaths and add a
matching classify-then-remove block in removeNemoclawCli, mirroring the
existing nemoclaw shim handling including the preserve-foreign-file guard.

Fixes #6098

Signed-off-by: Dongni Yang <dongniy@nvidia.com>
@Dongni-Yang Dongni-Yang self-assigned this Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a nemohermsShimPath property to the UninstallPaths interface and populates it in defaultUninstallPaths. removeNemoclawCli now classifies and removes (or warns about) the nemohermes shim during uninstall, alongside the existing nemoclaw shim handling. Tests validate both the path and removal behavior.

Changes

Nemohermes shim cleanup during uninstall

Layer / File(s) Summary
Path contract and derivation
src/lib/domain/uninstall/paths.ts, src/lib/domain/uninstall/paths.test.ts
UninstallPaths gains a nemohermsShimPath: string field, defaultUninstallPaths derives it as <home>/.local/bin/nemohermes, and a test asserts the computed path.
Uninstall shim removal logic and coverage
src/lib/actions/uninstall/run-plan.ts, src/lib/actions/uninstall/run-plan.test.ts
removeNemoclawCli classifies paths.nemohermsShimPath and either removes it or warns if it's a preserved foreign file, mirroring the existing nemoclaw shim handling; a new test verifies the symlink is included in rmSync targets and deleted from disk.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant RunUninstallPlan as runUninstallPlan
  participant RemoveNemoclawCli as removeNemoclawCli
  participant Filesystem

  User->>RunUninstallPlan: trigger uninstall
  RunUninstallPlan->>RemoveNemoclawCli: invoke with paths (includes nemohermsShimPath)
  RemoveNemoclawCli->>RemoveNemoclawCli: classify nemohermsShimPath shim
  alt shim removable
    RemoveNemoclawCli->>Filesystem: rmSync(nemohermsShimPath)
  else preserved foreign file
    RemoveNemoclawCli->>RemoveNemoclawCli: log warning
  end
  RemoveNemoclawCli-->>RunUninstallPlan: cleanup result
Loading

Suggested labels: bug-fix, integration: hermes

Suggested reviewers: cv, ericksoa

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: removing the nemohermes shim during uninstall.
Linked Issues check ✅ Passed The changes add the missing nemohermes uninstall path and cleanup, matching the linked issue's required fix.
Out of Scope Changes check ✅ Passed The modified files stay focused on uninstall path logic and tests, with no clear unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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-nemohermes-shim-not-removed-on-uninstall

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

@github-code-quality

github-code-quality Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the fix/6098-nemohermes-... 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-nemohermes-... 05cd980 +/-
nemoclaw/src/se...cret-scanner.ts 100%
nemoclaw/src/commands/slash.ts 100%
nemoclaw/src/li...bprocess-env.ts 100%
nemoclaw/src/bl...eprint/state.ts 98%
nemoclaw/src/onboard/config.ts 98%
nemoclaw/src/bl...int/snapshot.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-nemohermes-... 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-nemohermes-... 05cd980 +/-
src/lib/actions...dbox/rebuild.ts 82%
src/lib/actions...all/run-plan.ts 82%
src/lib/state/o...oard-session.ts 79%
src/lib/shields/index.ts 75%
src/lib/state/sandbox.ts 73%
src/lib/onboard/preflight.ts 69%
src/lib/onboard...er-gpu-patch.ts 59%
src/lib/actions...licy-channel.ts 58%
src/lib/policy/index.ts 56%
src/lib/onboard.ts 20%

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

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

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

Dispatch hint: hermes-e2e

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • hermes-e2e (high): Closest existing live coverage for the affected Hermes CLI shim lifecycle. It performs a real Hermes install/onboard flow and exercises the installed nemohermes command in runtime operations, giving confidence that the shim remains correctly installed and usable before cleanup changes are merged.

Optional E2E

  • full-e2e (high): Optional broader installer/onboard/cleanup smoke for the default OpenClaw path. It does not directly verify nemohermes uninstall cleanup, but it can catch accidental regressions in shared installer and host lifecycle behavior touched by uninstall path modeling.

New E2E recommendations

  • uninstall-cleanup (high): Existing live E2E coverage appears to exercise install/onboard/runtime cleanup via destroy, but not the user-facing uninstall command or verification that installer-managed shims such as nemoclaw and nemohermes are removed while foreign files are preserved.
    • Suggested test: Add a live uninstall E2E target that installs Hermes, verifies command -v nemohermes, runs the real uninstall flow non-interactively, and asserts ~/.local/bin/nemohermes and other managed host resources are removed without deleting foreign shim files.

Dispatch hint

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

@github-actions

github-actions Bot commented Jul 2, 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 path/run-plan implementation and unit tests. The canonical e2e.yaml workflow has no wired live E2E target or free-standing job that exercises NemoClaw uninstall/nemohermes shim cleanup, so there is no dispatchable E2E target to require for this PR.

Optional E2E targets

  • None.

Relevant changed files

  • None.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Blocked

Merge posture: Do not merge until addressed
Primary next action: Fix PRA-2: Remove the actual installer-created nemohermes wrapper; then add or justify PRA-T1.
Open items: 1 required · 3 warnings · 1 suggestion · 8 test follow-ups
Top item: Remove the installer-created nemohermes wrapper, not only symlinks

Action checklist

  • PRA-2 Fix: Remove the actual installer-created nemohermes wrapper in src/lib/actions/uninstall/run-plan.ts:689
  • PRA-1 Resolve or justify: Source-of-truth review needed: Installer-created agent alias shim cleanup
  • PRA-3 Resolve or justify: Keep planned uninstall mutations aligned with execution in src/lib/actions/uninstall/run-plan.ts:689
  • PRA-4 Resolve or justify: Add alias-specific negative coverage before broadening deletion in src/lib/actions/uninstall/run-plan.test.ts:63
  • 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: Runtime validation
  • PRA-T5 Add or justify test follow-up: Add alias-specific negative coverage before broadening deletion
  • PRA-T6 Add or justify test follow-up: Acceptance clause
  • PRA-T7 Add or justify test follow-up: Acceptance clause
  • PRA-T8 Add or justify test follow-up: Acceptance clause
  • PRA-5 In-scope improvement: Fix the misspelled nemohermes path field before it spreads in src/lib/domain/uninstall/paths.ts:34

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 Required acceptance src/lib/actions/uninstall/run-plan.ts:689 Teach the shim classification/removal path to recognize installer-managed wrappers for the alias being removed, or generalize it to accept the known installer CLI binaries while preserving foreign regular files. Keep the existing content/metadata guardrails; do not delete arbitrary regular files just because they are named `nemohermes`.
PRA-3 Resolve/justify correctness src/lib/actions/uninstall/run-plan.ts:689 Represent all installer-managed CLI shim paths in the uninstall plan, then execute from those planned actions or otherwise assert planned shim mutations match execution. Include alias shim wording in the user-facing removal list if the confirmation text is intended to enumerate filesystem artifacts.
PRA-4 Resolve/justify tests src/lib/actions/uninstall/run-plan.test.ts:63 Add a targeted negative test for a regular `~/.local/bin/nemohermes` file whose contents do not match an installer wrapper, asserting it remains on disk and the preserve warning is logged.
PRA-5 Improvement correctness src/lib/domain/uninstall/paths.ts:34 Rename it to `nemoHermesShimPath`, or better, replace the single bespoke field with a small list of installer-managed CLI shim paths so future aliases do not require more one-off fields.

🚨 Required before merge

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

PRA-2 Required — Remove the actual installer-created nemohermes wrapper

  • Location: src/lib/actions/uninstall/run-plan.ts:689
  • Category: acceptance
  • Problem: The new cleanup calls the existing shim classifier for `paths.nemohermsShimPath`, but that classifier only treats 3-line wrappers as managed when the exec line ends with `/nemoclaw" "$@"`. The linked issue's concrete leftover is a regular executable wrapper whose exec line ends with `/nemohermes" "$@"`, so this PR can still preserve the exact file reported by [Linux][Install] nemoclaw uninstall leaves the nemohermes shim at ~/.local/bin/nemohermes (command still resolves) #6098 while the new test passes because it only covers a symlink.
  • Impact: `nemoclaw uninstall --yes` may continue to leave `~/.local/bin/nemohermes` behind for the standard installer-created wrapper, so `which nemohermes` can still resolve after uninstall and the linked issue remains only partially fixed.
  • Required action: Teach the shim classification/removal path to recognize installer-managed wrappers for the alias being removed, or generalize it to accept the known installer CLI binaries while preserving foreign regular files. Keep the existing content/metadata guardrails; do not delete arbitrary regular files just because they are named `nemohermes`.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Read `scripts/install.sh` around `ensure_cli_shim()` where `expected_shim` is generated as `exec "$cli_path" "$@"`, then compare `src/lib/domain/uninstall/shims.ts` `isInstallerManagedWrapperContents()` which currently requires `execLine.endsWith('/nemoclaw" "$@"')`.
  • Missing regression test: Add a test that writes the issue's exact 3-line regular wrapper at `tmpHome/.local/bin/nemohermes` with an exec target ending in `/nemohermes" "$@"`, runs `runUninstallPlan({ assumeYes: true, ... })`, and asserts the wrapper is removed.
  • Done when: The required change is committed and verification passes: Read `scripts/install.sh` around `ensure_cli_shim()` where `expected_shim` is generated as `exec "$cli_path" "$@"`, then compare `src/lib/domain/uninstall/shims.ts` `isInstallerManagedWrapperContents()` which currently requires `execLine.endsWith('/nemoclaw" "$@"')`.
  • Evidence: Issue [Linux][Install] nemoclaw uninstall leaves the nemohermes shim at ~/.local/bin/nemohermes (command still resolves) #6098 states the leftover is `-rwxrwxr-x ... nemohermes` and shows `exec "$HOME/.hermes/node/bin/nemohermes" "$@"`. The PR adds `classifyShimPath(paths.nemohermsShimPath)` at `run-plan.ts:689`, while the classifier only accepts `/nemoclaw" "$@"` wrapper contents.
Review findings by urgency: 1 required fix, 3 items to resolve/justify, 1 in-scope improvement

⚠️ 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: Installer-created agent alias shim cleanup

  • 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 blocker finding: add a regression using the actual installer wrapper format for `nemohermes`, plus a foreign-regular-file preservation 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: `scripts/install.sh` builds wrappers with `exec "$cli_path" "$@"`; the current classifier recognizes only `/nemoclaw` wrapper exec targets.

PRA-3 Resolve/justify — Keep planned uninstall mutations aligned with execution

  • Location: src/lib/actions/uninstall/run-plan.ts:689
  • Category: correctness
  • Problem: The executable path now removes `nemohermes` imperatively inside `removeNemoclawCli()`, but `buildRunPlan()` and `buildUninstallPlan()` still model only the `nemoclaw` shim. The confirmation copy also says only `Global ... CLI (npm package: nemoclaw)`, so plan/confirmation consumers can miss a filesystem deletion that execution performs.
  • Impact: Any dry-run, audit, UI, or test that relies on `buildRunPlan()` can underreport what uninstall will remove. That makes future uninstall changes harder to verify and weakens user-visible transparency for local file deletion.
  • Recommended action: Represent all installer-managed CLI shim paths in the uninstall plan, then execute from those planned actions or otherwise assert planned shim mutations match execution. Include alias shim wording in the user-facing removal list if the confirmation text is intended to enumerate filesystem artifacts.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read `buildRunPlan()` where only `shim: classifyShimPath(paths.nemoclawShimPath, deps.fs)` is passed into `buildUninstallPlan()`, then read `removeNemoclawCli()` where `paths.nemohermsShimPath` is additionally classified and removed outside the plan.
  • Missing regression test: Add a plan contract test that `buildRunPlan()` reports delete/preserve actions for both `/.local/bin/nemoclaw` and `/.local/bin/nemohermes`, and an execution test that consumes the same planned shim classifications.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read `buildRunPlan()` where only `shim: classifyShimPath(paths.nemoclawShimPath, deps.fs)` is passed into `buildUninstallPlan()`, then read `removeNemoclawCli()` where `paths.nemohermsShimPath` is additionally classified and removed outside the plan.
  • Evidence: `src/lib/domain/uninstall/plan.ts` `cliActions()` accepts a single `shim`, while `src/lib/actions/uninstall/run-plan.ts` now deletes a second shim path after the planned `nemoclaw` shim block.

PRA-4 Resolve/justify — Add alias-specific negative coverage before broadening deletion

  • Location: src/lib/actions/uninstall/run-plan.test.ts:63
  • Category: tests
  • Problem: The new test proves deletion of a `nemohermes` symlink, but it does not prove that a foreign regular file at the same path is preserved. Because the fix needs to recognize regular installer wrappers, alias-specific negative coverage is important to prevent accidental data loss.
  • Impact: A future or local classifier change could delete a user-owned `~/.local/bin/nemohermes` script that was not created by the installer, and the current test suite would not catch it.
  • Recommended action: Add a targeted negative test for a regular `~/.local/bin/nemohermes` file whose contents do not match an installer wrapper, asserting it remains on disk and the preserve warning is logged.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Inspect the added test at `run-plan.test.ts:63`; it creates only `fs.symlinkSync('/dev/null', nemohermsShimPath)` and never exercises a non-managed regular file at that alias path.
  • Missing regression test: Add `preserves a foreign regular ~/.local/bin/nemohermes file and logs the preserve warning` using a temp HOME, a regular executable with arbitrary contents, and `runUninstallPlan()`.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Inspect the added test at `run-plan.test.ts:63`; it creates only `fs.symlinkSync('/dev/null', nemohermsShimPath)` and never exercises a non-managed regular file at that alias path.
  • Evidence: Existing classifier tests cover a foreign regular file for `nemoclaw`; this PR introduces a second deletion target but does not add equivalent alias-specific negative coverage.

💡 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-5 Improvement — Fix the misspelled nemohermes path field before it spreads

  • Location: src/lib/domain/uninstall/paths.ts:34
  • Category: correctness
  • Problem: The new public `UninstallPaths` field is named `nemohermsShimPath`, missing the second `e` in Hermes. The typo is now repeated in source and tests.
  • Impact: Leaving the typo in a shared domain interface makes future uninstall work easier to misread and harder to generalize across alias shims.
  • Suggested action: Rename it to `nemoHermesShimPath`, or better, replace the single bespoke field with a small list of installer-managed CLI shim paths so future aliases do not require more one-off fields.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Read `src/lib/domain/uninstall/paths.ts:34` and `paths.test.ts:30`; both use `nemohermsShimPath` while the binary and issue consistently use `nemohermes`.
  • Missing regression test: Existing path construction coverage can be updated to assert the corrected property or alias-shim list contains `path.join(home, '.local', 'bin', 'nemohermes')`.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: The PR adds `nemohermsShimPath` to `UninstallPaths` and uses the same misspelling in `removeNemoclawCli()`.
Simplification opportunities: 1 possible cut

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

  • PRA-5 shrink (src/lib/domain/uninstall/paths.ts:34): The one-off `nemohermsShimPath` field and duplicated classify/remove block.
    • Replacement: A small `cliShimPaths` or `agentAliasShimPaths` collection derived from known installer shims, iterated by the existing classify-then-remove logic.
    • Safety boundary: Keep per-path `classifyShimPath` validation and foreign-file preservation; do not replace the guard with name-only deletion.
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 — removes the installer-created ~/.local/bin/nemohermes 3-line wrapper that execs the nemohermes binary. The PR changes uninstall filesystem behavior. Unit coverage was added for symlink deletion, but the linked issue's regular wrapper path and plan/execution contract are not yet covered; a targeted temp-HOME runtime-style validation would materially increase confidence without relying on external E2E status.
  • PRA-T2 Runtime validation — preserves a foreign regular ~/.local/bin/nemohermes file and logs the preserve warning. The PR changes uninstall filesystem behavior. Unit coverage was added for symlink deletion, but the linked issue's regular wrapper path and plan/execution contract are not yet covered; a targeted temp-HOME runtime-style validation would materially increase confidence without relying on external E2E status.
  • PRA-T3 Runtime validation — buildRunPlan reports alias-shim delete or preserve actions for nemohermes so planned filesystem mutations match execution. The PR changes uninstall filesystem behavior. Unit coverage was added for symlink deletion, but the linked issue's regular wrapper path and plan/execution contract are not yet covered; a targeted temp-HOME runtime-style validation would materially increase confidence without relying on external E2E status.
  • PRA-T4 Runtime validation — uninstall with a temp HOME containing both nemoclaw and nemohermes installer wrappers leaves neither path on disk. The PR changes uninstall filesystem behavior. Unit coverage was added for symlink deletion, but the linked issue's regular wrapper path and plan/execution contract are not yet covered; a targeted temp-HOME runtime-style validation would materially increase confidence without relying on external E2E status.
  • PRA-T5 Add alias-specific negative coverage before broadening deletion — Add a targeted negative test for a regular `~/.local/bin/nemohermes` file whose contents do not match an installer wrapper, asserting it remains on disk and the preserve warning is logged.
  • PRA-T6 Acceptance clause — [Linux][Install] nemoclaw uninstall leaves the nemohermes shim at /.local/bin/nemohermes (command still resolves) — add test evidence or identify existing coverage. The PR targets `/.local/bin/nemohermes`, but the implementation currently handles symlinks and `nemoclaw`-shaped wrappers, not the regular `nemohermes` wrapper format shown in the issue.
  • PRA-T7 Acceptance clause — `nemoclaw uninstall --yes` reports a clean uninstall ("Claws retracted") and removes the `nemoclaw` CLI shim, the `openshell*` binaries, and the `/.nemoclaw/` directory — but it does **not** remove the sibling **`nemohermes`** shim at `/.local/bin/nemohermes`. — add test evidence or identify existing coverage. The new `runUninstallPlan({ assumeYes: true })` test covers a `nemohermes` symlink. The issue's regular wrapper is not covered and is not recognized by the current wrapper classifier.
  • PRA-T8 Acceptance clause — `nemoclaw uninstall` removes **both** the `nemoclaw` and `nemohermes` shims from `/.local/bin` (in addition to `openshell*` and `/.nemoclaw`, which it already removes). — add test evidence or identify existing coverage. Existing logic handles `nemoclaw`, and this PR adds a `nemohermes` path. However, the standard regular alias wrapper remains classified as a foreign file because its exec line ends with `/nemohermes`, not `/nemoclaw`.

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 2, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — Blocked

Merge posture: Do not merge until addressed
Primary next action: Fix PRA-2: isInstallerManagedWrapperContents() only recognizes /nemoclaw wrapper, not /nemohermes; then add or justify PRA-T1.
Open items: 2 required · 5 warnings · 1 suggestion · 8 test follow-ups
Since last review: 0 prior items resolved · 0 still apply · 0 new items found

Action checklist

  • PRA-2 Fix: isInstallerManagedWrapperContents() only recognizes /nemoclaw wrapper, not /nemohermes in src/lib/domain/uninstall/shims.ts:28
  • PRA-3 Fix: Field name typo: nemohermsShimPath (missing 'e') vs binary name nemohermes in src/lib/domain/uninstall/paths.ts:34
  • PRA-1 Resolve or justify: Source-of-truth review needed: classifyNemoclawShim reused for nemohermes shim classification
  • PRA-4 Resolve or justify: buildUninstallPlan() only accepts one shim (nemoclaw); nemohermes shim removal only at execution time in src/lib/domain/uninstall/plan.ts:55
  • PRA-5 Resolve or justify: Only symlink path tested for nemohermes shim; wrapper/dev-shim/preserve paths untested in src/lib/actions/uninstall/run-plan.test.ts:63
  • PRA-6 Resolve or justify: paths.test.ts only asserts path value, not integration with shim classification in src/lib/domain/uninstall/paths.test.ts:30
  • PRA-8 Resolve or justify: Security test coverage gap: missing negative test for preserve-foreign-file in src/lib/actions/uninstall/run-plan.test.ts:63
  • 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: Runtime validation
  • PRA-T5 Add or justify test follow-up: Runtime validation
  • PRA-T6 Add or justify test follow-up: Only symlink path tested for nemohermes shim; wrapper/dev-shim/preserve paths untested
  • PRA-T7 Add or justify test follow-up: paths.test.ts only asserts path value, not integration with shim classification
  • PRA-T8 Add or justify test follow-up: Acceptance clause
  • PRA-7 In-scope improvement: Duplicate shim removal block; extract helper in src/lib/actions/uninstall/run-plan.ts:682

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 Required correctness src/lib/domain/uninstall/shims.ts:28 Generalize isInstallerManagedWrapperContents to accept agent-specific binary names, or add isNemoHermesWrapperContents and use a shared classifier that checks for the wrapper pattern with any known binary name.
PRA-3 Required correctness src/lib/domain/uninstall/paths.ts:34 Rename to nemohermesShimPath across all 4 changed files (paths.ts, paths.test.ts, run-plan.ts, run-plan.test.ts).
PRA-4 Resolve/justify architecture src/lib/domain/uninstall/plan.ts:55 Extend UninstallPlanOptions.shim to accept a map or array of shims, or add a second optional shim field. Update buildUninstallPlan to include nemohermes shim action in the 'NemoClaw CLI' step when present.
PRA-5 Resolve/justify tests src/lib/actions/uninstall/run-plan.test.ts:63 Add tests mirroring nemoclaw shim classification tests: wrapper -> remove, dev-shim -> remove, regular file -> preserve with warning, directory -> unsupported.
PRA-6 Resolve/justify tests src/lib/domain/uninstall/paths.test.ts:30 Add integration test in paths.test.ts or plan.test.ts that builds paths and runs classifyShimPath on nemohermesShimPath with various fs mocks.
PRA-7 Improvement scope src/lib/actions/uninstall/run-plan.ts:682 Extract helper function removeShimIfManaged(shimPath: string, runtime: UninstallRuntime): void and call for both shims.
PRA-8 Resolve/justify security src/lib/actions/uninstall/run-plan.test.ts:63 Add test case: create regular file at nemohermesShimPath, run uninstall, verify file preserved and warning logged.

🚨 Required before merge

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

PRA-2 Required — isInstallerManagedWrapperContents() only recognizes /nemoclaw wrapper, not /nemohermes

PRA-3 Required — Field name typo: nemohermsShimPath (missing 'e') vs binary name nemohermes

  • Location: src/lib/domain/uninstall/paths.ts:34
  • Category: correctness
  • Problem: The field is named 'nemohermsShimPath' (missing 'e' in 'hermes') in the UninstallPaths interface, defaultUninstallPaths return, and all call sites. The path value is correct ('nemohermes') but the field name is inconsistent with the binary name and other fields (nemoclawShimPath).
  • Impact: Maintenance hazard; grep/search for 'nemohermes' won't find the field; inconsistency with nemoclawShimPath naming pattern.
  • Required action: Rename to nemohermesShimPath across all 4 changed files (paths.ts, paths.test.ts, run-plan.ts, run-plan.test.ts).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: grep -r 'nemohermsShimPath' src/ — should return 0 matches after fix.
  • Missing regression test: N/A — naming fix; existing tests will catch if path value changes.
  • Done when: The required change is committed and verification passes: grep -r 'nemohermsShimPath' src/ — should return 0 matches after fix.
  • Evidence: paths.ts:34 and :63 use 'nemohermsShimPath'; run-plan.ts:689 uses paths.nemohermsShimPath; test files mirror the typo.
Review findings by urgency: 2 required fixes, 5 items to resolve/justify, 1 in-scope improvement

⚠️ 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: classifyNemoclawShim reused for nemohermes shim classification

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as missing.
  • 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: Test that nemohermes wrapper content (#!/usr/bin/env bash\nexport PATH="$HOME/.hermes/node/bin:$PATH"\nexec "$HOME/.hermes/node/bin/nemohermes" "$@") yields { kind: 'managed-wrapper', remove: true }
  • 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:28 execLine.endsWith('/nemoclaw" "$@"'); issue [Linux][Install] nemoclaw uninstall leaves the nemohermes shim at ~/.local/bin/nemohermes (command still resolves) #6098 shows wrapper with '/nemohermes" "$@"'.

PRA-4 Resolve/justify — buildUninstallPlan() only accepts one shim (nemoclaw); nemohermes shim removal only at execution time

  • Location: src/lib/domain/uninstall/plan.ts:55
  • Category: architecture
  • Problem: The domain plan builder accepts a single 'shim' parameter for nemoclaw. The nemohermes shim removal is implemented only in run-plan.ts execution (removeNemoclawCli), not reflected in the plan. buildRunPlan() returns a plan that doesn't show the nemohermes step, creating drift between declared plan and actual execution.
  • Impact: Plan inspection (--dry-run, logging, debugging) won't show nemohermes shim removal; violates single-source-of-truth for uninstall steps.
  • Recommended action: Extend UninstallPlanOptions.shim to accept a map or array of shims, or add a second optional shim field. Update buildUninstallPlan to include nemohermes shim action in the 'NemoClaw CLI' step when present.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run buildRunPlan() and inspect returned plan.steps[2].actions — only nemoclaw shim action present.
  • Missing regression test: Test that buildRunPlan() with nemohermes shim present returns a plan containing a delete-shim action for nemohermes.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run buildRunPlan() and inspect returned plan.steps[2].actions — only nemoclaw shim action present.
  • Evidence: plan.ts:55 buildUninstallPlan accepts single shim; cliActions() only processes that one shim; run-plan.ts:689 handles nemohermes separately at runtime.

PRA-5 Resolve/justify — Only symlink path tested for nemohermes shim; wrapper/dev-shim/preserve paths untested

  • Location: src/lib/actions/uninstall/run-plan.test.ts:63
  • Category: tests
  • Problem: New test only covers 'managed-symlink -> remove'. The actual installer creates a 3-line wrapper (managed-wrapper). No tests for: wrapper removal, dev-shim removal, preserve-foreign-file warning, or unsupported-path-type.
  • Impact: Primary installer artifact path (wrapper) untested; regression likely (as demonstrated by wrapper detection bug). Parity with nemoclaw shim coverage missing.
  • Recommended action: Add tests mirroring nemoclaw shim classification tests: wrapper -> remove, dev-shim -> remove, regular file -> preserve with warning, directory -> unsupported.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run test suite; grep for 'nemohermes' in run-plan.test.ts — only 1 test block found.
  • Missing regression test: Test cases: (1) nemohermes wrapper (3-line installer format) -> remove, (2) nemohermes regular file -> preserve with warning, (3) nemohermes dev shim -> remove, (4) nemohermes directory -> unsupported.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run test suite; grep for 'nemohermes' in run-plan.test.ts — only 1 test block found.
  • Evidence: run-plan.test.ts:63-95 only tests symlink; plan.test.ts has 5 nemoclaw shim classification tests (lines 28-80).

PRA-6 Resolve/justify — paths.test.ts only asserts path value, not integration with shim classification

  • Location: src/lib/domain/uninstall/paths.test.ts:30
  • Category: tests
  • Problem: Test only verifies nemohermsShimPath === ~/.local/bin/nemohermes. No test that this path flows through classifyShimPath and produces correct classification for each shim type.
  • Impact: Domain path construction verified in isolation but not integrated with classification logic that determines removal.
  • Recommended action: Add integration test in paths.test.ts or plan.test.ts that builds paths and runs classifyShimPath on nemohermesShimPath with various fs mocks.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check paths.test.ts — only 1 assertion for nemohermsShimPath (line 30).
  • Missing regression test: Test that defaultUninstallPaths().nemohermesShimPath passed to classifyShimPath yields expected classification for symlink/wrapper/file cases.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check paths.test.ts — only 1 assertion for nemohermsShimPath (line 30).
  • Evidence: paths.test.ts:30 single assertion; no classification integration test.

PRA-8 Resolve/justify — Security test coverage gap: missing negative test for preserve-foreign-file

  • Location: src/lib/actions/uninstall/run-plan.test.ts:63
  • Category: security
  • Problem: Security Category 8 (Security Testing) = WARNING. The nemoclaw shim test (first test case) verifies preserve-foreign-file warning for non-installer-managed file. No equivalent test for nemohermes. This is a security hygiene gap — foreign file preservation logic untested for the new shim.
  • Impact: If classification logic regresses for nemohermes, a foreign file could be incorrectly removed or a managed shim preserved without detection.
  • Recommended action: Add test case: create regular file at nemohermesShimPath, run uninstall, verify file preserved and warning logged.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run test with regular file at nemohermesShimPath — currently no test covers this.
  • Missing regression test: Test that non-installer-managed regular file at nemohermesShimPath is preserved and warning emitted.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run test with regular file at nemohermesShimPath — currently no test covers this.
  • Evidence: Security rubric Category 8: 'Tests cover security edge cases... Negative test cases verify that forbidden actions are denied.'

💡 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-7 Improvement — Duplicate shim removal block; extract helper

  • Location: src/lib/actions/uninstall/run-plan.ts:682
  • Category: scope
  • Problem: Lines 682-696 duplicate the nemoclaw shim classification/removal logic for nemohermes. Extract to removeShimIfManaged(shimPath, runtime) helper.
  • Impact: Code duplication; future agent shims (nemo-deepagents) would require more copy-paste.
  • Suggested action: Extract helper function removeShimIfManaged(shimPath: string, runtime: UninstallRuntime): void and call for both shims.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Compare lines 682-690 (nemoclaw) and 689-697 (nemohermes) — identical structure.
  • Missing regression test: N/A — refactor; existing tests cover behavior.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: run-plan.ts:682-697 two nearly identical blocks.
Simplification opportunities: 1 possible cut, net -8 lines possible

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

  • PRA-7 shrink (src/lib/actions/uninstall/run-plan.ts:682): Lines 689-697 (nemohermes block) and lines 682-690 (nemoclaw block) — replace with two calls to helper
    • Replacement: function removeShimIfManaged(shimPath: string, runtime: UninstallRuntime) { const shim = classifyShimPath(shimPath); if (shim.remove) removePath(shimPath, runtime); else if (shim.kind === 'preserve-foreign-file') runtime.warn(`Leaving ${shimPath} in place because it is not an installer-managed shim.`); }
    • Net: -8 lines
    • Safety boundary: Must preserve classifyShimPath call per shim (different paths) and preserve-foreign-file warning with correct path in message.
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: install NemoClaw with NEMOCLAW_AGENT=hermes, run uninstall, verify both nemoclaw and nemohermes commands removed. Runtime/sandbox/infrastructure paths need behavioral runtime validation: src/lib/actions/uninstall/run-plan.ts, src/lib/domain/uninstall/paths.ts. The wrapper detection bug would only be caught by integration test with real installer artifact.
  • PRA-T2 Runtime validation — Unit test: classifyNemoclawShim (or new classifier) returns managed-wrapper for nemohermes 3-line wrapper format. Runtime/sandbox/infrastructure paths need behavioral runtime validation: src/lib/actions/uninstall/run-plan.ts, src/lib/domain/uninstall/paths.ts. The wrapper detection bug would only be caught by integration test with real installer artifact.
  • PRA-T3 Runtime validation — Unit test: nemohermes regular file -> preserve with warning. Runtime/sandbox/infrastructure paths need behavioral runtime validation: src/lib/actions/uninstall/run-plan.ts, src/lib/domain/uninstall/paths.ts. The wrapper detection bug would only be caught by integration test with real installer artifact.
  • PRA-T4 Runtime validation — Unit test: nemohermes dev shim -> remove. Runtime/sandbox/infrastructure paths need behavioral runtime validation: src/lib/actions/uninstall/run-plan.ts, src/lib/domain/uninstall/paths.ts. The wrapper detection bug would only be caught by integration test with real installer artifact.
  • PRA-T5 Runtime validation — Unit test: buildRunPlan includes nemohermes shim action in returned plan. Runtime/sandbox/infrastructure paths need behavioral runtime validation: src/lib/actions/uninstall/run-plan.ts, src/lib/domain/uninstall/paths.ts. The wrapper detection bug would only be caught by integration test with real installer artifact.
  • PRA-T6 Only symlink path tested for nemohermes shim; wrapper/dev-shim/preserve paths untested — Add tests mirroring nemoclaw shim classification tests: wrapper -> remove, dev-shim -> remove, regular file -> preserve with warning, directory -> unsupported.
  • PRA-T7 paths.test.ts only asserts path value, not integration with shim classification — Add integration test in paths.test.ts or plan.test.ts that builds paths and runs classifyShimPath on nemohermesShimPath with various fs mocks.
  • PRA-T8 Acceptance clause — Remove both nemoclaw and nemohermes shims from ~/.local/bin — add test evidence or identify existing coverage. Code added for nemohermes but wrapper detection broken (isInstallerManagedWrapperContents only matches /nemoclaw) and field name typo.
Since last review details

Current findings, using the urgency labels above:

PRA-1 Resolve/justify — Source-of-truth review needed: classifyNemoclawShim reused for nemohermes shim classification

  • Location: not file-specific
  • Category: architecture
  • Problem: The advisor marked localized patch analysis as missing.
  • 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: Test that nemohermes wrapper content (#!/usr/bin/env bash\nexport PATH="$HOME/.hermes/node/bin:$PATH"\nexec "$HOME/.hermes/node/bin/nemohermes" "$@") yields { kind: 'managed-wrapper', remove: true }
  • 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:28 execLine.endsWith('/nemoclaw" "$@"'); issue [Linux][Install] nemoclaw uninstall leaves the nemohermes shim at ~/.local/bin/nemohermes (command still resolves) #6098 shows wrapper with '/nemohermes" "$@"'.

PRA-2 Required — isInstallerManagedWrapperContents() only recognizes /nemoclaw wrapper, not /nemohermes

PRA-3 Required — Field name typo: nemohermsShimPath (missing 'e') vs binary name nemohermes

  • Location: src/lib/domain/uninstall/paths.ts:34
  • Category: correctness
  • Problem: The field is named 'nemohermsShimPath' (missing 'e' in 'hermes') in the UninstallPaths interface, defaultUninstallPaths return, and all call sites. The path value is correct ('nemohermes') but the field name is inconsistent with the binary name and other fields (nemoclawShimPath).
  • Impact: Maintenance hazard; grep/search for 'nemohermes' won't find the field; inconsistency with nemoclawShimPath naming pattern.
  • Required action: Rename to nemohermesShimPath across all 4 changed files (paths.ts, paths.test.ts, run-plan.ts, run-plan.test.ts).
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: grep -r 'nemohermsShimPath' src/ — should return 0 matches after fix.
  • Missing regression test: N/A — naming fix; existing tests will catch if path value changes.
  • Done when: The required change is committed and verification passes: grep -r 'nemohermsShimPath' src/ — should return 0 matches after fix.
  • Evidence: paths.ts:34 and :63 use 'nemohermsShimPath'; run-plan.ts:689 uses paths.nemohermsShimPath; test files mirror the typo.

PRA-4 Resolve/justify — buildUninstallPlan() only accepts one shim (nemoclaw); nemohermes shim removal only at execution time

  • Location: src/lib/domain/uninstall/plan.ts:55
  • Category: architecture
  • Problem: The domain plan builder accepts a single 'shim' parameter for nemoclaw. The nemohermes shim removal is implemented only in run-plan.ts execution (removeNemoclawCli), not reflected in the plan. buildRunPlan() returns a plan that doesn't show the nemohermes step, creating drift between declared plan and actual execution.
  • Impact: Plan inspection (--dry-run, logging, debugging) won't show nemohermes shim removal; violates single-source-of-truth for uninstall steps.
  • Recommended action: Extend UninstallPlanOptions.shim to accept a map or array of shims, or add a second optional shim field. Update buildUninstallPlan to include nemohermes shim action in the 'NemoClaw CLI' step when present.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run buildRunPlan() and inspect returned plan.steps[2].actions — only nemoclaw shim action present.
  • Missing regression test: Test that buildRunPlan() with nemohermes shim present returns a plan containing a delete-shim action for nemohermes.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run buildRunPlan() and inspect returned plan.steps[2].actions — only nemoclaw shim action present.
  • Evidence: plan.ts:55 buildUninstallPlan accepts single shim; cliActions() only processes that one shim; run-plan.ts:689 handles nemohermes separately at runtime.

PRA-5 Resolve/justify — Only symlink path tested for nemohermes shim; wrapper/dev-shim/preserve paths untested

  • Location: src/lib/actions/uninstall/run-plan.test.ts:63
  • Category: tests
  • Problem: New test only covers 'managed-symlink -> remove'. The actual installer creates a 3-line wrapper (managed-wrapper). No tests for: wrapper removal, dev-shim removal, preserve-foreign-file warning, or unsupported-path-type.
  • Impact: Primary installer artifact path (wrapper) untested; regression likely (as demonstrated by wrapper detection bug). Parity with nemoclaw shim coverage missing.
  • Recommended action: Add tests mirroring nemoclaw shim classification tests: wrapper -> remove, dev-shim -> remove, regular file -> preserve with warning, directory -> unsupported.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run test suite; grep for 'nemohermes' in run-plan.test.ts — only 1 test block found.
  • Missing regression test: Test cases: (1) nemohermes wrapper (3-line installer format) -> remove, (2) nemohermes regular file -> preserve with warning, (3) nemohermes dev shim -> remove, (4) nemohermes directory -> unsupported.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run test suite; grep for 'nemohermes' in run-plan.test.ts — only 1 test block found.
  • Evidence: run-plan.test.ts:63-95 only tests symlink; plan.test.ts has 5 nemoclaw shim classification tests (lines 28-80).

PRA-6 Resolve/justify — paths.test.ts only asserts path value, not integration with shim classification

  • Location: src/lib/domain/uninstall/paths.test.ts:30
  • Category: tests
  • Problem: Test only verifies nemohermsShimPath === ~/.local/bin/nemohermes. No test that this path flows through classifyShimPath and produces correct classification for each shim type.
  • Impact: Domain path construction verified in isolation but not integrated with classification logic that determines removal.
  • Recommended action: Add integration test in paths.test.ts or plan.test.ts that builds paths and runs classifyShimPath on nemohermesShimPath with various fs mocks.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Check paths.test.ts — only 1 assertion for nemohermsShimPath (line 30).
  • Missing regression test: Test that defaultUninstallPaths().nemohermesShimPath passed to classifyShimPath yields expected classification for symlink/wrapper/file cases.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Check paths.test.ts — only 1 assertion for nemohermsShimPath (line 30).
  • Evidence: paths.test.ts:30 single assertion; no classification integration test.

PRA-7 Improvement — Duplicate shim removal block; extract helper

  • Location: src/lib/actions/uninstall/run-plan.ts:682
  • Category: scope
  • Problem: Lines 682-696 duplicate the nemoclaw shim classification/removal logic for nemohermes. Extract to removeShimIfManaged(shimPath, runtime) helper.
  • Impact: Code duplication; future agent shims (nemo-deepagents) would require more copy-paste.
  • Suggested action: Extract helper function removeShimIfManaged(shimPath: string, runtime: UninstallRuntime): void and call for both shims.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Compare lines 682-690 (nemoclaw) and 689-697 (nemohermes) — identical structure.
  • Missing regression test: N/A — refactor; existing tests cover behavior.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: run-plan.ts:682-697 two nearly identical blocks.

PRA-8 Resolve/justify — Security test coverage gap: missing negative test for preserve-foreign-file

  • Location: src/lib/actions/uninstall/run-plan.test.ts:63
  • Category: security
  • Problem: Security Category 8 (Security Testing) = WARNING. The nemoclaw shim test (first test case) verifies preserve-foreign-file warning for non-installer-managed file. No equivalent test for nemohermes. This is a security hygiene gap — foreign file preservation logic untested for the new shim.
  • Impact: If classification logic regresses for nemohermes, a foreign file could be incorrectly removed or a managed shim preserved without detection.
  • Recommended action: Add test case: create regular file at nemohermesShimPath, run uninstall, verify file preserved and warning logged.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Run test with regular file at nemohermesShimPath — currently no test covers this.
  • Missing regression test: Test that non-installer-managed regular file at nemohermesShimPath is preserved and warning emitted.
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Run test with regular file at nemohermesShimPath — currently no test covers this.
  • Evidence: Security rubric Category 8: 'Tests cover security edge cases... Negative test cases verify that forbidden actions are denied.'

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 (2)
src/lib/actions/uninstall/run-plan.ts (1)

682-695: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate classify/remove/warn logic for shim paths.

The nemoclaw and nemoherms blocks are structurally identical. Extracting a small helper would avoid copy-paste drift if more shims are added later.

♻️ Optional refactor
+function removeManagedShim(shimPath: string, runtime: UninstallRuntime): void {
+  const shim = classifyShimPath(shimPath);
+  if (shim.remove) removePath(shimPath, runtime);
+  else if (shim.kind === "preserve-foreign-file") {
+    runtime.warn(`Leaving ${shimPath} in place because it is not an installer-managed shim.`);
+  }
+}
+
 function removeNemoclawCli(paths: UninstallPaths, runtime: UninstallRuntime): void {
   ...
-  const shim = classifyShimPath(paths.nemoclawShimPath);
-  if (shim.remove) removePath(paths.nemoclawShimPath, runtime);
-  else if (shim.kind === "preserve-foreign-file") {
-    runtime.warn(
-      `Leaving ${paths.nemoclawShimPath} in place because it is not an installer-managed shim.`,
-    );
-  }
-  const agentShim = classifyShimPath(paths.nemohermsShimPath);
-  if (agentShim.remove) removePath(paths.nemohermsShimPath, runtime);
-  else if (agentShim.kind === "preserve-foreign-file") {
-    runtime.warn(
-      `Leaving ${paths.nemohermsShimPath} in place because it is not an installer-managed shim.`,
-    );
-  }
+  removeManagedShim(paths.nemoclawShimPath, runtime);
+  removeManagedShim(paths.nemohermsShimPath, runtime);
   removeNvmLeftovers(paths, runtime);
   removeAliases(paths, runtime);
 }
🤖 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.ts` around lines 682 - 695, The uninstall
shim handling in run-plan has duplicated classify/remove/warn logic for nemoclaw
and nemoherms, which should be consolidated. Extract a small helper around
classifyShimPath, removePath, and runtime.warn that takes the shim path and
runtime, then reuse it for both paths in run-plan so future shim additions don’t
drift.
src/lib/domain/uninstall/paths.ts (1)

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

Field name is missing a letter ("nemoherms" vs "nemohermes").

nemohermsShimPath drops the final "e" from "nemohermes", even though the value correctly resolves to the nemohermes shim path. Slightly confusing given the sibling nemoclawShimPath matches its shim name exactly.

✏️ Optional rename for consistency
-  nemohermsShimPath: string;
+  nemohermesShimPath: string;

and correspondingly at line 63, plus all call sites in run-plan.ts and the test file.

Also applies to: 63-63

🤖 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/domain/uninstall/paths.ts` at line 34, The field name in the
uninstall paths types is misspelled as nemohermsShimPath and should be renamed
to match nemohermes consistently. Update the corresponding definition and any
related usage in the uninstall path helpers, then adjust all references in
run-plan.ts and the associated test file to use the corrected symbol name so the
shim path naming stays aligned with the actual nemohermes shim.
🤖 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.ts`:
- Around line 682-695: The uninstall shim handling in run-plan has duplicated
classify/remove/warn logic for nemoclaw and nemoherms, which should be
consolidated. Extract a small helper around classifyShimPath, removePath, and
runtime.warn that takes the shim path and runtime, then reuse it for both paths
in run-plan so future shim additions don’t drift.

In `@src/lib/domain/uninstall/paths.ts`:
- Line 34: The field name in the uninstall paths types is misspelled as
nemohermsShimPath and should be renamed to match nemohermes consistently. Update
the corresponding definition and any related usage in the uninstall path
helpers, then adjust all references in run-plan.ts and the associated test file
to use the corrected symbol name so the shim path naming stays aligned with the
actual nemohermes shim.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 941375ed-2fc1-46c2-b69b-9482a6c49ec7

📥 Commits

Reviewing files that changed from the base of the PR and between ba687cf and 05cd980.

📒 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

@Dongni-Yang

Copy link
Copy Markdown
Contributor Author

Closing as duplicate of #6101.

@Dongni-Yang Dongni-Yang closed this Jul 2, 2026
@Dongni-Yang
Dongni-Yang deleted the fix/6098-nemohermes-shim-not-removed-on-uninstall branch July 2, 2026 02:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants