Auto-switching Claude Code between personal and work accounts on Windows
If you're using Claude Code professionally and have a personal subscription on the side, you've probably hit this: you cd into a work repo, type claude, and later realize you've been burning personal quota the whole time. Or vice versa.
The fix is simpler than it sounds, and once it's in your $PROFILE you'll never think about it again.
How Claude Code picks a config
Claude Code respects an environment variable called CLAUDE_CONFIG_DIR. When it's set, Claude reads its config from that directory instead of the default ~/.claude.json. When it's unset, it falls back to the default.
That's the whole mechanism. Everything else is just wiring around it.
The setup
My machine has two drives relevant here. Work repos live on W:. Personal projects live everywhere else. So the rule becomes simple: if I'm on W:, set CLAUDE_CONFIG_DIR to my work config directory. Otherwise, clear it and let Claude use the personal default.
I put the work config in W:.claude during initial setup (claude auth login with my work credentials, then moved the generated config to that path).
Here's the PowerShell that goes in $PROFILE:
powershell$env:CLAUDE_WORK_CONFIG = 'W:\.claude'
function script:Resolve-ClaudeExe {
(Get-Command claude -CommandType Application -ErrorAction SilentlyContinue |
Select-Object -First 1).Source
}
function script:Use-WorkConfig { \(env:CLAUDE_CONFIG_DIR = \)env:CLAUDE_WORK_CONFIG }
function script:Use-PersonalConfig { Remove-Item Env:\CLAUDE_CONFIG_DIR -ErrorAction SilentlyContinue }
function Invoke-ClaudeAuto {
if ((Get-Location).Drive.Name -eq 'W') { Use-WorkConfig } else { Use-PersonalConfig }
$exe = Resolve-ClaudeExe
if (-not $exe) { Write-Error 'claude executable not found on PATH'; return }
& $exe @args
}
function Invoke-ClaudeWork { Use-WorkConfig; \(e = Resolve-ClaudeExe; if (\)e) { & $e @args } }
function Invoke-ClaudePersonal { Use-PersonalConfig; \(e = Resolve-ClaudeExe; if (\)e) { & $e @args } }
function Get-ClaudeContext {
if ((Get-Location).Drive.Name -eq 'W') {
[pscustomobject]@{ Profile = 'WORK'; ConfigDir = $env:CLAUDE_WORK_CONFIG }
} else {
[pscustomobject]@{ Profile = 'PERSONAL'; ConfigDir = "$HOME\.claude.json (default)" }
}
}
Set-Alias claude Invoke-ClaudeAuto # auto-picks by current drive
Set-Alias claudew Invoke-ClaudeWork # force WORK
Set-Alias claudep Invoke-ClaudePersonal # force PERSONAL
What each piece does
Invoke-ClaudeAuto is where the logic lives. It checks the current drive name, sets or clears CLAUDE_CONFIG_DIR, resolves the Claude executable path, and passes all arguments through. The @args splat means it behaves identically to calling claude directly.
Use-PersonalConfig uses Remove-Item on the env var rather than setting it to an empty string. That distinction matters: an empty string isn't the same as unset, and Claude Code reads absence of the variable as "use the default", not an empty path.
Resolve-ClaudeExe exists because once you alias claude, you can't call the original claude directly inside the function without causing infinite recursion. This resolves the underlying executable path instead.
The two override functions (Invoke-ClaudeWork and Invoke-ClaudePersonal) are for the rare case where you want to explicitly force a profile regardless of where you are. I almost never use them, but they're handy when I'm in a personal project but need to quickly check something against the work environment.
The status line
The auto-switching handles 95% of cases, but I also added a Claude Code status line so I can always confirm which account is active at a glance. Claude Code lets you replace the default status bar with the output of any script.
My script reads CLAUDE_CONFIG_DIR: if it's set, show WORK in yellow. If it's unset, show PERSONAL in green. One line at the bottom of the terminal, updated after every turn.
I won't paste the full status line script here since it's pretty specific to my terminal setup, but the Claude Code docs cover the mechanism well. The key is that the script runs in the same shell session where CLAUDE_CONFIG_DIR was set, so it always reflects the current state accurately.
Why drive-based rather than path-based
The obvious alternative would be detecting based on the full path, like checking if the current directory starts with a specific prefix. That works too, but drive-based is more reliable on Windows because drive letters are always present and consistent regardless of how deep in a directory tree you are.
It also maps cleanly to how Windows developers typically organize work. Most people with a dedicated work drive or a mapped network drive have a clear separation already.
Setting up a second Claude config
If you haven't done this yet, the setup is:
Log in to your work Claude account in a browser
Run claude auth login (this will write a config to ~/.claude.json or ~/.claude/)
Move that config to wherever you want your work config to live (in my case, W:.claude)
Run claude auth login again for your personal account to restore the default
Your personal MCP servers and preferences live in the default config. The work config is separate and only gets what you configure for that context. Summary
CLAUDE_CONFIG_DIR is all you need to separate Claude Code accounts
Drive detection in PowerShell makes switching automatic based on where you are
claudew and claudep aliases handle the edge cases
A status line gives you a constant visual confirmation
Paste the block into your $PROFILE, reload it, and you're done.

