Switch between multiple Claude Code accounts/config dirs (personal, work,
whatever you add) in your shell, with auto-binding by directory, per-workspace
colored prompt badges, and auto-generated matching Claude Code themes.
Each workspace maps to its own CLAUDE_CONFIG_DIR (a separate ~/.claude-*
directory), so logins, settings, MCP servers, history, and themes are fully
isolated per account. Login is stored per-config-dir in the OS keychain by
claude itself — cws never touches or stores credentials.
- zsh or bash 4+ (macOS ships bash 3.2 — install a newer one for bash support:
brew install bash; zsh has no such requirement) python3(theme generation, JSON settings edits, status line)
git clone https://github.com/mikolajpochec/cws.git ~/dev/cws
cd ~/dev/cws
./install.shThis is idempotent: it won't overwrite an existing workspace registry or an
existing cws block in your shell rc files. On a fresh install it creates two
example workspaces, personal (→ ~/.claude) and work (→ ~/.claude-work),
each with an auto-generated theme, and wires sourcing into ~/.zshrc and
~/.bashrc.
bash support needs bash 4+ (associative arrays) — macOS ships bash 3.2, so
on a Mac without a newer bash (brew install bash) the installer skips the
~/.bashrc block and prints a note; zsh has no such requirement.
After installing, add the prompt segment to your prompt:
# zsh (needs: setopt prompt_subst)
PROMPT='%n@%m $(cws_prompt_seg)%~ > '
# bash
PS1='\u@\h $(cws_prompt_seg_bash)\w \$ 'Open a new terminal and you should see a colored [ personal] badge.
cws show active workspace
cws <name> set env for current shell (auto-rebind keeps it in sync)
cws <name> <claude args> one-shot launch claude in that workspace
cws ls list workspaces + bindings + colors
cws add <name> [globs] add a workspace - color + theme auto-assigned
cws bind <name> [dir] bind a directory (+ subtree) to a workspace - current dir if omitted
cws rm <name> remove a workspace (registry + token; config dir kept)
cws auth <name> claude auth login for that workspace's account
cws theme show/set a workspace's theme
cws trust [path] mark a path trusted in the active workspace's config
cws update pull the latest cws release
cws help full command reference
For an existing workspace, bind an actual directory (its exact path plus subtree, no glob-writing needed):
cd ~/dev/client-x
cws bind client-x # binds the current directory
cws bind client-x ~/other # or bind an arbitrary directoryFor a glob pattern instead (e.g. matching several directories by name), pass it when creating the workspace:
cws add client-x '**/dev/*client-x*'Either way, cd-ing into a bound path auto-switches to that workspace;
cd-ing back out returns to whatever the fallback workspace is (the first
entry in the registry with no bindings — personal by default).
cws add auto-assigns the next unused color from a 12-color palette
(lib/colors.sh) and generates a full Claude Code custom theme
(lib/theme-gen.sh, derives the theme's ~40 color keys from the one accent
color via HSL math) written to <configDir>/themes/<name>.json, set as that
workspace's default theme. No manual theme authoring needed.
cws add (and install.sh for the two example workspaces) also wires each
workspace's settings.json to lib/statusline.sh as its statusLine
command. It shows, in the workspace's badge color:
personal Sonnet 5 | project-dir | [####------] 42% ctx
effort high | cost $0.12 | time 12m5s | 5h limit 24%
workspace badge, model, dir, and context-window bar on line one; effort
level, session cost/duration, and 5-hour rate limit on line two
(fields that aren't present in a given session, like effort or
rate_limits, are just omitted). It reads the badge color back out of the
private registry via $CLAUDE_WORKSPACE/$CWS_ROOT, both exported by
shell/cws.zsh/cws.bash, so it stays in sync with the prompt segment
without hardcoding any workspace name. Never overwrites a statusLine you've
set to something else — it only claims the key when it's unset or already
points at statusline.sh.
cws update fetches origin, shows what's new, and fast-forwards $CWS_HOME
(only works for a git clone install, the recommended path above - it
declines cleanly on a vendored/copied checkout with no remote). Each
interactive shell also does a passive, offline check at startup - it never
hits the network itself, just reads whatever origin/<branch> was last
fetched to, and prints a one-line nudge if you're behind. Once a week it
kicks a silent background git fetch so that check has fresh data next time,
without ever blocking shell startup.
shell/cws.zsh zsh entrypoint (chpwd hook + prompt var)
shell/cws.bash bash entrypoint (PROMPT_COMMAND + PS1)
lib/registry.sh registry parsing, env apply/clear, directory auto-bind
lib/colors.sh palette + color assignment
lib/theme-gen.sh accent-color -> full theme JSON generator
lib/commands.sh add/rm/ls/theme/auth/trust/status/help + the cws dispatcher
lib/prompt.sh cws_prompt_seg (zsh) / cws_prompt_seg_bash (bash)
lib/statusline.sh Claude Code statusLine renderer (workspace-colored)
install.sh idempotent installer
This repo holds only the code. Your actual workspace list (account names,
config dir paths, directory bindings, colors) lives outside the repo in
$CWS_ROOT (default ~/.config/claude-workspaces/workspaces) and is never
read from or written into this repo — safe to keep this repo public while
keeping your account setup private.
name|configDir|keychainService|bindings|color
bindings: comma-separated glob patterns matched against$PWD. Empty = never auto-bound by directory; the first such entry is the fallback workspace used when nothing else matches.color: hex string (#c084fc), used for the prompt badge and as the seed for that workspace's generated theme.
MIT — see LICENSE.