make uninstall-<tool> removes an npm global package only when the binary lives under ~/.nvm, Homebrew or ~/.local/share/pnpm. For every other npm prefix the package stays installed, and pnpm packages are removed with npm.
#152 made the npm handler of remove_installation (scripts/lib/reconcile.sh) uninstall the package that owns the binary instead of the tool name. These gaps sit around that handler and were found in the review of #152:
- Other prefixes are not classified as npm.
classify_install_path (scripts/lib/capability.sh) returns npm only for the three locations above. With the npm prefix /usr/local, the binary classifies as manual: removal deletes the bin symlink and leaves the package in lib/node_modules. With ~/.npm-global/bin, it classifies as unknown and cannot be removed.
- pnpm installs are removed with npm.
scripts/installers/npm_global.sh installs with pnpm when pnpm is configured. ~/.local/share/pnpm/* classifies as npm, so removal runs npm uninstall -g, which does not know the package. If pnpm's global bins are shim scripts rather than symlinks, the package name also falls back to the tool name. The shim layout was not checked, because pnpm was not installed on the machine that ran the review.
- The active nvm version decides.
npm uninstall -g acts on the node version that nvm has active, not on the prefix the binary lives in. A package installed under another node version stays.
reconcile_tool passes no path. It calls remove_installation without the detected path (fourth argument), so that caller resolves the binary through command -v and can hit a different installation.
A possible direction: take the prefix from the detected path (<prefix>/bin/<tool> → <prefix>), run npm uninstall -g --prefix <prefix> <package> for npm, and use pnpm remove -g for pnpm paths.
Assisted by claude-code:claude-opus-5-5 — Session
make uninstall-<tool>removes an npm global package only when the binary lives under~/.nvm, Homebrew or~/.local/share/pnpm. For every other npm prefix the package stays installed, and pnpm packages are removed with npm.#152 made the npm handler of
remove_installation(scripts/lib/reconcile.sh) uninstall the package that owns the binary instead of the tool name. These gaps sit around that handler and were found in the review of #152:classify_install_path(scripts/lib/capability.sh) returnsnpmonly for the three locations above. With the npm prefix/usr/local, the binary classifies asmanual: removal deletes the bin symlink and leaves the package inlib/node_modules. With~/.npm-global/bin, it classifies asunknownand cannot be removed.scripts/installers/npm_global.shinstalls with pnpm when pnpm is configured.~/.local/share/pnpm/*classifies asnpm, so removal runsnpm uninstall -g, which does not know the package. If pnpm's global bins are shim scripts rather than symlinks, the package name also falls back to the tool name. The shim layout was not checked, because pnpm was not installed on the machine that ran the review.npm uninstall -gacts on the node version that nvm has active, not on the prefix the binary lives in. A package installed under another node version stays.reconcile_toolpasses no path. It callsremove_installationwithout the detected path (fourth argument), so that caller resolves the binary throughcommand -vand can hit a different installation.A possible direction: take the prefix from the detected path (
<prefix>/bin/<tool>→<prefix>), runnpm uninstall -g --prefix <prefix> <package>for npm, and usepnpm remove -gfor pnpm paths.Assisted by claude-code:claude-opus-5-5 — Session