claude-lm
Claude Local Manager — an nvm-style profile switcher for Claude Code. Keep separate work and personal Claude setups on the same machine and switch between them per-terminal, instantly.
A profile is just a CLAUDE_CONFIG_DIR. Claude Code stores everything —
account/auth, projects, settings, history, even .claude.json — inside that
directory. claude-lm manages a set of these dirs and flips the env var for you,
exactly like nvm flips your PATH.
$ clm ls
profile type target
● work linked /Users/you/.claude-work default
personal system /Users/you/.claude (CLAUDE_CONFIG_DIR unset)
$ clm use personal
Switched to personal (system default — CLAUDE_CONFIG_DIR unset)
$ clm run work -- claude # one-off in the work profile, no shell switch
Works on macOS and Linux, with zsh, bash, and fish. Zero runtime dependencies (Node ≥ 18).
Install
npm install -g claude-lmThen enable the shell shortcut once (this is what lets clm use change your
current shell, the same way nvm requires a line in your rc):
# zsh (macOS default)
clm init --shell zsh >> ~/.zshrc
# bash (common on Linux)
clm init --shell bash >> ~/.bashrc
# fish
clm init --shell fish >> ~/.config/fish/config.fishRestart your shell (or source the file). Running clm init with no --shell
auto-detects from $SHELL and prints the snippet plus the exact command to run.
Without the shell integration you can still use everything except
clm use/clm deactivate(which must modify the parent shell). Useeval "$(clm use work)"orclm run <profile> -- …instead.
Quick start
Adopt your existing setups as profiles (nothing is moved or copied):
clm register personal --system # your current ~/.claude (CLAUDE_CONFIG_DIR unset)
clm register work ~/.claude-work # an existing alternate config dir
clm default personal # what new shells start asSwitch any time:
clm use work # this terminal is now "work"
clm use personal # back to personal
clm current # -> work
clm ls # see them allOpen two terminals and run clm use work in one and clm use personal in the
other — they stay independent, because each just has its own CLAUDE_CONFIG_DIR.
Commands
Switching (needs clm init)
| Command | What it does |
|---|---|
clm use <profile> |
Switch the current shell to a profile |
clm deactivate |
Clear the active profile (back to system default) |
clm current |
Print the active profile name |
clm default [<profile>] |
Show / set the default profile for new shells |
Managing
| Command | What it does |
|---|---|
clm list (ls) |
List profiles — ● marks active, default is flagged |
clm create <name> [--from <p>] |
Create a new managed profile, optionally copying another |
clm register <name> <dir> |
Register an existing config dir as a profile |
clm register <name> --system |
Register the system default (~/.claude, unset) |
clm rename <old> <new> |
Rename a profile (moves the dir if managed) |
clm rm <name> [--purge] [--force] |
Remove a profile; --purge deletes its files |
clm path [<profile>] |
Print a profile's CLAUDE_CONFIG_DIR |
Running
| Command | What it does |
|---|---|
clm run <profile> [-- cmd…] |
Run a command (default: claude) under a profile. Works without shell integration. |
Setup
| Command | What it does |
|---|---|
clm init [--shell zsh|bash|fish] |
Print the shell integration snippet |
clm doctor |
Show platform, env, and integration status |
claude-lm and clm are the same command.
How it works
- Profile =
CLAUDE_CONFIG_DIR. Each profile maps to a config directory. Selecting one exportsCLAUDE_CONFIG_DIR(plusCLAUDE_PROFILEfor your prompt/statusline). The special system profile unsets the variable so Claude uses its default~/.claude. clm usechanges the current shell via a tiny shell function (installed byclm init) thatevals the export/unset lines — the same tricknvm useuses. Everything human-readable is printed to stderr so stdout stays clean foreval.- The default for new shells is written to
$CLAUDE_LM_HOME/default.sh(anddefault.fish), which the init snippet sources at startup. Changing it withclm default <name>regenerates those files — no node process runs on shell startup. - State lives in
$CLAUDE_LM_HOME/profiles.json(default~/.claude-profiles/). Managed profiles are created under~/.claude-profiles/profiles/<name>/; linked profiles just reference an existing directory and are never moved or deleted unless you pass--force.
Environment variables
| Variable | Purpose |
|---|---|
CLAUDE_LM_HOME |
Where claude-lm keeps its state + managed profiles (default ~/.claude-profiles) |
CLAUDE_CONFIG_DIR |
The lever claude-lm sets — read by Claude Code itself |
CLAUDE_PROFILE |
Set by claude-lm to the active profile name (handy for statuslines) |
macOS vs Linux
claude-lm is written entirely with Node's fs/path/os APIs (no shelling
out to platform tools), so it behaves the same on both. The only OS-sensitive
bits are handled for you:
- Default shell differs (zsh on modern macOS, often bash on Linux) —
clm initauto-detects via$SHELLand picks the right syntax/rc path. - Default config dir is
~/.claudeon both platforms. - Paths are resolved with
os.homedir()andpath, so~expansion and separators are correct everywhere.
Windows is not supported for the shell integration (PowerShell would be needed);
core commands and clm run still work.
License
MIT