Install, manage, and switch between versions of the Claude Code CLI. Inspired by rbenv, tfenv, and rustup.
cvm install latest # install the latest version
cvm use 2.1.58 # switch globally
cvm local 2.1.71 # pin a project directory
- Installation
- Shell Setup
- Quick Start
- Commands
- Version Resolution
- Architecture
- Supported Platforms
- Requirements
- Documentation
- Development
curl -fsSL https://raw.githubusercontent.com/alexandernicholson/cvm/main/install.sh | bashThe installer downloads CVM to ~/.cvm/bin/cvm, detects your shell, and offers to add the PATH line to your config automatically.
irm https://raw.githubusercontent.com/alexandernicholson/cvm/main/install.ps1 | iexThis downloads cvm.ps1 to ~\.cvm\bin\, creates a cvm.cmd wrapper so cvm works from both PowerShell and CMD, and adds ~\.cvm\bin to your user PATH.
mkdir -p ~/.cvm/bin
curl -fsSL https://raw.githubusercontent.com/alexandernicholson/cvm/main/cvm.sh \
-o ~/.cvm/bin/cvm
chmod +x ~/.cvm/bin/cvmAdd ~/.cvm/bin (or %USERPROFILE%\.cvm\bin on Windows) to your PATH. Run cvm env to print the correct line for your shell:
cvm env # auto-detects your shell
cvm env --bash # force bash syntax
cvm env --zsh # force zsh syntax
cvm env --fish # force fish syntax
cvm env --pwsh # force PowerShell syntax| Shell | Config file | Line to add |
|---|---|---|
| bash | ~/.bashrc |
export PATH="$HOME/.cvm/bin:$PATH" |
| zsh | ~/.zshrc |
export PATH="$HOME/.cvm/bin:$PATH" |
| fish | ~/.config/fish/config.fish |
fish_add_path $HOME/.cvm/bin |
| PowerShell | $PROFILE |
$env:PATH = "$env:USERPROFILE\.cvm\bin;$env:PATH" |
After adding the line, reload your shell (source ~/.zshrc or open a new terminal).
# Install the latest version
cvm install latest
# Verify it works
claude --version
# See installed versions
cvm ls
# Browse all available versions
cvm ls-remote --all| Command | Description |
|---|---|
cvm install <version> |
Install a version (latest, stable, or semver e.g. 2.1.71) |
cvm use <version> |
Set the global active version |
cvm local <version> |
Pin the current directory (writes .claude-version) |
cvm current |
Show the currently resolved version |
cvm which |
Print the full path to the active claude binary |
cvm ls |
List installed versions |
cvm ls-remote [--all] |
List versions available for download |
cvm uninstall <version> |
Remove an installed version |
cvm env [--bash|--zsh|--fish] |
Print the PATH setup line for your shell |
cvm self-update |
Update CVM itself to the latest version |
cvm self-uninstall |
Remove CVM and all installed Claude Code versions |
cvm install latest # most recent release
cvm install stable # last designated stable release
cvm install 2.1.71 # exact version
cvm install v2.1.71 # leading 'v' is stripped automaticallycd my-project
cvm local 2.1.58 # writes .claude-version in this directoryCommit .claude-version to keep your whole team on the same version. Override for a single command:
CVM_VERSION=2.1.55 claude --versionCVM resolves the active version in this order:
$CVM_VERSION env var (highest priority)
↓
.claude-version file (walks up from $PWD to /)
↓
~/.cvm/version (global default, set by cvm use)
↓
error: no version active
flowchart TD
A[cvm_resolve_version] --> B{$CVM_VERSION set?}
B -- yes --> Z[return $CVM_VERSION]
B -- no --> C[dir = $PWD]
C --> D{.claude-version<br>exists in dir?}
D -- yes --> Z2[return contents]
D -- no --> E{dir == /}
E -- yes --> F{~/.cvm/version<br>exists?}
E -- no --> G[dir = parent of dir]
G --> D
F -- yes --> Z3[return contents]
F -- no --> ERR[return error]
CVM is a single bash script with no runtime dependencies beyond bash, curl, and either python3 or jq. It uses the symlink-switching model from tfenv and rbenv.
graph TD
U["User runs: cvm <command>"]
U --> D{Dispatch}
D --> I[cmd_install]
D --> US[cmd_use]
D --> L[cmd_local]
D --> CU[cmd_current]
D --> LS[cmd_list]
D --> LR[cmd_list_remote]
D --> UN[cmd_uninstall]
I --> PD[detect_platform]
I --> DL["Download binary<br>GCS bucket"]
I --> CV[verify_checksum<br>SHA256 from manifest]
I --> SYM[update_symlink]
LR --> GH["GitHub tags API"]
LR --> NP["npm registry"]
LR --> MR[Merge + deduplicate<br>sort_versions]
~/.cvm/
├── bin/
│ ├── cvm ← CVM script itself
│ └── claude → symlink to active version binary
├── versions/
│ ├── 2.1.58/
│ │ └── claude ← downloaded native binary
│ └── 2.1.71/
│ └── claude
├── version ← global default (plain text, e.g. "2.1.71")
└── cache/ ← temporary download staging (cleaned after install)
Only ~/.cvm/bin needs to be on $PATH.
cvm ls-remote fetches from GitHub tags and the npm registry independently, then merges and deduplicates the results. If one source is rate-limited or down, the other covers it. Only if both are unreachable does the command fail.
Claude Code native binaries are served by Anthropic from a GCS bucket. CVM downloads the binary for your platform, verifies its SHA256 checksum against the release manifest, then moves it to ~/.cvm/versions/<version>/claude. A failed checksum leaves no partial install.
| Platform | OS | Architecture |
|---|---|---|
darwin-arm64 |
macOS | Apple Silicon (M-series) |
darwin-x64 |
macOS | Intel |
linux-arm64 |
Linux (glibc) | ARM64 |
linux-x64 |
Linux (glibc) | x86_64 |
linux-arm64-musl |
Linux (musl/Alpine) | ARM64 |
linux-x64-musl |
Linux (musl/Alpine) | x86_64 |
win32-x64 |
Windows (Git Bash/PowerShell) | x86_64 |
CVM auto-detects your platform, including Rosetta 2 on Apple Silicon Macs, musl on Alpine Linux, and MINGW64/MSYS2/Cygwin on Windows.
WSL users: Use the standard Linux install above. WSL runs a real Linux environment, so
linux-x64binaries work correctly.
- bash 4.0 or later (macOS ships bash 3.2; install a newer version via Homebrew:
brew install bash) - curl
- python3 or jq (for JSON parsing in
ls-remoteand checksum extraction;python3is usually pre-installed)
Full documentation lives in docs/:
- User Guide — installation, shell setup, all commands, per-project versions, troubleshooting
- Technical Reference — architecture, distribution infrastructure, version resolution algorithm, platform detection, testing
make test # run all 173 tests
make test-verbose # TAP output
make lint # bash -n syntax check
make install-bats # install bats-core (via Homebrew or npm)Tests use bats-core with a mock curl that intercepts all HTTP calls. Each test runs in an isolated $CVM_DIR and never touches ~/.cvm or the real filesystem.
| File | Tests | Coverage |
|---|---|---|
00-help.bats |
12 | help, version, unknown commands |
01-version-resolution.bats |
11 | env var, walk-up, global default |
02-install.bats |
16 | install, channels, checksums |
03-use.bats |
10 | global switching, symlinks |
04-local.bats |
9 | per-directory .claude-version |
05-current-which.bats |
15 | current/which resolution |
06-list.bats |
22 | ls, ls-remote, dual-source resilience |
07-uninstall.bats |
13 | removal, active version handling |
08-edge-cases.bats |
14 | self-update, self-uninstall, idempotency |
09-shells.bats |
51 | bash/zsh/fish compatibility |
10-windows.bats |
12 | Windows platform detection, .exe binary, win32-x64 paths |
- Fork and clone the repo
- Make changes to
cvm.shorinstall.sh - Run
make test— all 185 tests must pass - Open a pull request
cvm self-uninstall
# then remove the PATH line from your shell config file