diff --git a/claude/uclaude_updater.py b/claude/uclaude_updater.py index 9b974c4..7ade2ab 100644 --- a/claude/uclaude_updater.py +++ b/claude/uclaude_updater.py @@ -218,7 +218,8 @@ def ensure_claude_code(target_version=None): If target_version is set and the installed version doesn't match, reinstall to the exact version so cli.js patch is compatible. """ - cli_js = find_cli_js() + all_paths = find_all_cli_js() + cli_js = all_paths[0] if all_paths else None # If already installed, check version compatibility if cli_js and target_version: @@ -241,8 +242,8 @@ def ensure_claude_code(target_version=None): capture_output=True, text=True, timeout=300, ) if result.returncode == 0: - cli_js = find_cli_js() - if cli_js: + found = find_all_cli_js() + if found: new_ver, _ = get_installed_version() print(f" {G}Claude Code {new_ver or ''} installed{D}") return True @@ -296,6 +297,58 @@ def find_cli_js(): return None +def find_all_cli_js(): + """Find ALL installed Claude Code cli.js paths (for multi-install patching).""" + candidates = set() + + if IS_WINDOWS: + for env_key in ("APPDATA", "LOCALAPPDATA", "PROGRAMFILES"): + base = os.environ.get(env_key, "") + if base: + candidates.add(os.path.join(base, "npm", "node_modules", + "@anthropic-ai", "claude-code", "cli.js")) + else: + # Static well-known paths + for prefix in ("/usr/lib", "/usr/local/lib", "/opt/homebrew/lib"): + candidates.add(os.path.join(prefix, "node_modules", + "@anthropic-ai", "claude-code", "cli.js")) + + # npm root -g (primary install path) + try: + r = subprocess.run(["npm", "root", "-g"], capture_output=True, text=True, timeout=10) + if r.returncode == 0: + candidates.add(os.path.join(r.stdout.strip(), + "@anthropic-ai", "claude-code", "cli.js")) + except Exception: + pass + + # Resolve `which claude` → follow symlinks → find cli.js + try: + r = subprocess.run(["which", "claude"], capture_output=True, text=True, timeout=5) + if r.returncode == 0: + claude_bin = os.path.realpath(r.stdout.strip()) + # claude binary is at node_modules/.bin/claude + # cli.js is in node_modules/@anthropic-ai/claude-code/cli.js + nm = os.path.dirname(os.path.dirname(claude_bin)) # node_modules/ + candidates.add(os.path.join(nm, "@anthropic-ai", "claude-code", "cli.js")) + except Exception: + pass + + # NVM installs: /root/.nvm, /home/*/.nvm + nvm_bases = ["/root/.nvm"] + if os.path.isdir("/home"): + for user in os.listdir("/home"): + nvm_bases.append(f"/home/{user}/.nvm") + for nvm_base in nvm_bases: + versions_dir = os.path.join(nvm_base, "versions", "node") + if os.path.isdir(versions_dir): + for node_ver in os.listdir(versions_dir): + candidates.add(os.path.join(versions_dir, node_ver, "lib", + "node_modules", "@anthropic-ai", "claude-code", "cli.js")) + + return [p for p in candidates if os.path.isfile(p)] + + def get_installed_version(): """Get currently installed Claude Code version. @@ -879,9 +932,12 @@ def cmd_update(force=False, settings_only=False): # Install cli.js if not settings_only: - if not cli_js: - cli_js = find_cli_js() - if not cli_js: + all_paths = find_all_cli_js() + if not all_paths: + cli_js_single = find_cli_js() + if cli_js_single: + all_paths = [cli_js_single] + if not all_paths: eprint(f" {R}Claude Code cli.js not found even after install attempt.{D}") return 1 @@ -890,9 +946,16 @@ def cmd_update(force=False, settings_only=False): eprint(f" Run with: sudo python3 {sys.argv[0]}") return 1 - print(f"\n{W}--- Installing cli.js v{latest} ---{D}") - ok = install_cli_js(latest, cli_js) - if not ok: + print(f"\n{W}--- Installing cli.js v{latest} (found {len(all_paths)} location(s)) ---{D}") + any_ok = False + for path in all_paths: + print(f" Patching: {path}") + ok = install_cli_js(latest, path) + if ok: + any_ok = True + else: + eprint(f" {Y}Failed to patch {path}, continuing...{D}") + if not any_ok: return 1 # Patch settings