claude-profile is a small CLI for managing layered Claude settings profiles out of a local ~/.claude-profile repository.
Install the latest release binary:
curl -fsSL https://raw.githubusercontent.com/lossend/claude-profile/main/scripts/install.sh | shInstall to a custom directory:
curl -fsSL https://raw.githubusercontent.com/lossend/claude-profile/main/scripts/install.sh | BINDIR="$HOME/bin" shInstall a specific release:
curl -fsSL https://raw.githubusercontent.com/lossend/claude-profile/main/scripts/install.sh | VERSION=v0.1.0 shDownload the archive for your OS and architecture from GitHub Releases, extract it, and place the claude-profile binary on your PATH.
Verify the installed binary:
claude-profile versionCreate your first profile from the current Claude settings:
claude-profile create work --description "Work profile"Inspect available profiles:
claude-profile listPreview what would change before applying a profile:
claude-profile diff workExport the fully merged settings snapshot for a profile:
claude-profile export workApply a profile back into Claude:
claude-profile apply workRename a profile:
claude-profile rename work officeDelete a profile when you no longer need it:
claude-profile delete workEdit layered config files under:
~/.claude-profile/common/~/.claude-profile/profiles/work/~/.claude-profile/secrets/work.json
- Bootstraps
~/.claude-profileas a local Git repository on firstcreate - Splits shared Claude settings into
common/*.json - Stores per-profile overrides in
profiles/<name>/layers/*.json createwrites the initial profile diff toprofiles/<name>/layers/010-config.jsonby default- Stores sensitive overrides in
secrets/<name>.json, ignored by Git - Rebuilds
~/.claude/settings.jsonfrom layered config withapply - Tracks the active profile in
state/active.json - Installs shell completion for
zsh,bash, andfish
~/.claude-profile/
.git/
.gitignore
common/
10-hooks.json
20-security.json
30-marketplace-plugin.json
90-shared.json
profiles/
<name>/
manifest.json
layers/
010-config.json
secrets/
<name>.json
state/
active.json
completion.json
backups/
Create a profile from the current Claude settings file.
claude-profile create work --description "Work profile"Useful flags:
--description: store profile metadata--source: read from a non-default settings file--force: overwrite an existing profile or same-name local secret after typing two confirmations
010-config.json is only the default starter file name created by create. It is not part of the merge protocol. After bootstrap, you can split a profile into any number of *.json files under layers/ such as 020-models.json or 030-provider.json, and apply will merge every *.json file in the profile's layers/ directory. manifest.json is metadata only.
Rebuild ~/.claude/settings.json from common, profile-specific JSON files, and the local secret overlay.
claude-profile apply workUseful flags:
--target: write to a non-default settings file
Export a complete settings snapshot for a profile without touching ~/.claude/settings.json.
claude-profile export workBy default, export writes settings-work.json to the current working directory. The exported file uses the same merged result as apply:
common/*.jsonprofiles/<name>/layers/*.jsonsecrets/<name>.json
If the local secret file is missing, export prints a warning and still writes the non-secret snapshot.
Useful flags:
--output,-o: write to a custom output path--force: overwrite an existing output file
Compare the current Claude settings against what a profile would produce when applied. If no profile name is provided, uses the currently active profile.
# Compare against a specific profile
claude-profile diff work
# Compare against the active profile
claude-profile diffShows a colored diff with:
- Green (
+ profile:): keys/values that would be added - Red (
- current:): keys/values that would be removed - Modified: keys with different values in current vs profile
- Sensitive fields: automatically masked (shows first 4 and last 4 characters only)
Useful flags:
--source: compare against a non-default settings file--json: output structured JSON instead of colored terminal output-v, --verbose: show full formatted values for complex objects/arrays (default shows size summary)
Example output:
Diff: ~/.claude/settings.json ↔ profile "work"
3 changes: 1 added, 0 removed, 2 modified
model:
- current: "claude-sonnet-4-20250514"
+ profile: "claude-opus-4-20250514"
env.DEBUG:
+ profile: "true"
env.API_KEY:
- current: sk-12...f8ab
+ profile: sk-98...3def
For complex values (arrays/objects), the output adapts based on the number of changes:
- 1-3 changes: Automatically shows full formatted JSON (easy to review)
- 4+ changes: Shows size summary to keep output manageable
# With 1-3 changes, full JSON is shown automatically
hooks.PostToolUse:
- current:
[
{
"hooks": [...],
"matcher": "ExitPlanMode"
},
...
]
+ profile:
[
{
"hooks": [...],
"matcher": "ExitPlanMode"
},
...
]
# With many changes, size summary is shown
hooks.PostToolUse:
- current: <444 chars, 1 lines> [{"hooks":[{"command":"~/.claude/hooks/...
+ profile: <594 chars, 1 lines> [{"hooks":[{"command":"~/.claude/hooks/...
Use --verbose or -v to always show full formatted JSON regardless of change count.
The summary line shows the total number of changes and breaks them down by type (added, removed, modified) with color coding.
Sensitive fields (containing TOKEN, PASSWORD, SECRET, or ending with _KEY) are automatically masked to protect credentials.
Use diff before apply to preview what would change without modifying your settings.
List available profiles, their description, config files, secret presence, and active marker.
claude-profile listMigrate legacy profile directories from the old profile.json plus root-level *.json layout into manifest.json plus layers/*.json.
claude-profile migrateDelete a profile directory and its local secret file.
claude-profile delete workdelete requires two confirmations:
- first type the profile name exactly
- then type
DELETE
If either confirmation does not match, the command aborts without changing any files.
Rename an existing profile directory, its secret file, and the active profile state if applicable.
claude-profile rename work officeThe new name must not already exist. If another rename is in progress the command exits immediately with an error.
- Objects merge recursively
- Arrays replace the previous value
- Scalars are overwritten by later files
- Files are applied in lexicographic order
manifest.jsonis metadata only and never merged into Claude settings
Keys are treated as sensitive when the final path segment:
- contains
TOKEN - contains
PASSWORD - contains
SECRET - ends with
_KEY
Sensitive paths are written to secrets/<name>.json and stay out of Git history.
If you are developing locally and do have Go installed:
go install .Run the test suite with:
go test ./...The shortest release path is a single command:
scripts/release.sh patchThat command:
- checks that you are on
main - checks that the worktree is clean
- runs
go test ./... - calculates the next version tag
- creates the tag
- pushes
main - pushes the release tag
You can also publish an explicit version:
scripts/release.sh v0.1.1If you want to verify the steps without changing git state:
scripts/release.sh --dry-run patchThe GitHub release workflow is triggered by the pushed tag and then builds the downloadable archives automatically.
If you only want local artifacts without publishing:
scripts/build-release.sh