feat(installer): second Claude Code profile and claude-<profile> launcher - #58
Merged
Merged
Conversation
CLAUDE_CONFIG_DIR moves a Claude Code config root whole: credentials,
projects/, history.jsonl, sessions/ and .claude.json travel together.
That is what makes a second login a second directory, and it is what
keeps a personal conversation out of the work account's history.
Declare the roots in the registry under claude_profiles:. The primary
keeps the default location, so a bare `claude` needs nothing remembered;
every secondary is created by setup and shares the surfaces it names in
shared_paths: by symlinking back into the primary. Plugins install once,
because plugins/ is shared and enabledPlugins lives in the shared
settings.json. MCP servers are registered once per profile, because a
server lives in the profile's own .claude.json - the one file the two
profiles must not share.
The renderer refuses a shared_paths: entry naming projects,
history.jsonl, sessions, .claude.json or the credentials file: sharing
one merges the histories the second profile exists to keep apart.
Two defects found while running the result:
- An unset OPTIONAL credential aborted the whole script. It renders as
--env VAR="${VAR}", and under `set -u` that is not an empty string,
it is an exit - so an unexported OVERLEAF_SESSION took every step
after overleaf with it. Optional credentials now render ${VAR:-}.
- The unknown-server check used a PCRE lookahead, `(?=:)`, which BSD
grep rejects as "repetition-operator operand invalid". It printed an
error and inspected nothing on macOS. A sed expression replaces it.
…ofile A secondary profile was reachable only as CLAUDE_CONFIG_DIR=$HOME/.claude-personal claude; nothing created a command for it. Setup now writes ~/.local/bin/claude-<profile> (or $CLAUDE_LAUNCHER_DIR), which sets the config root and execs claude. A file at that path that setup did not write is left alone and reported, --check writes nothing, and a launcher directory missing from PATH is warned about. Profile names are validated as command names. Also fix a defect found while running it: setup run from inside a secondary profile's session sees CLAUDE_CONFIG_DIR as the primary root, and linked every shared surface of that root onto itself, leaving the profile unable to load skills, plugins or settings. Setup now fails that profile instead, and link_profile_path refuses to link a root onto itself.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
One workstation has two Claude Code logins, work and personal. Their conversation histories must stay apart.
CLAUDE_CONFIG_DIRmoves a whole config root: credentials,projects/,history.jsonl,sessions/and.claude.json. So a second login needs a second config root. Nothing in the kit created that root. Nothing gave it the skills, plugins or MCP fleet, and nothing gave it a command to start it.Change
claude_profiles:inregistry/estate-tooling.yaml.workis the primary and keeps~/.claude, so a bareclaudeworks as before.personallives at~/.claude-personal.shared_paths:by symlink back into the primary:skills,agents,commands,hooks,pluginsandsettings.json.projects,history.jsonl,sessions,.claude.jsonor the credentials file..claude.json.claude-<profile>launcher into~/.local/bin(or$CLAUDE_LAUNCHER_DIR). It setsCLAUDE_CONFIG_DIRand execsclaude.--checkwrites nothing.PATH.--no-profilesskips the secondary profiles.docs/SETUP.mdanddocs/REGISTRY.mddocument the profiles.Bug fixes
set -u, so--env OVERLEAF_SESSION="${OVERLEAF_SESSION}"exited, and every step after overleaf was skipped. Optional credentials now render as${VAR:-}.sedexpression replaces it.claude-personalsession saw~/.claude-personalas the primary root. It then linked every shared path of that root onto itself, and the profile could no longer load skills, plugins or settings. This happened on a real run. Setup now fails that profile, andlink_profile_pathrefuses to link a root onto itself.Verification
render_registry.py --checkpasses, 103 tests pass,ruffis clean,shellcheck -S warningis clean, andbash -npasses.link_profile_pathandinstall_profile_launcheragainst a real temporary filesystem:claude.--checkwrites nothing.~/.claude-personalholds six symlinks into~/.claudeand has its own.claude.jsonwith the MCP fleet.claude-personal --versionreturns2.1.272 (Claude Code)from a shell withoutCLAUDE_CONFIG_DIRset.setup-workstation.sh --check, run withCLAUDE_CONFIG_DIRset to the personal root, stops withCLAUDE_CONFIG_DIR points at ... re-run with it unset.docs/SETUP.mddescribes how to check this after both profiles have logged in.