Skip to content

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

25 Commits

Folders and files

Repository files navigation

claude-profile

claude-profile is a small CLI for managing layered Claude settings profiles out of a local ~/.claude-profile repository.

Install

Install the latest release binary:

curl -fsSL https://raw.githubusercontent.com/lossend/claude-profile/main/scripts/install.sh | sh

Install to a custom directory:

curl -fsSL https://raw.githubusercontent.com/lossend/claude-profile/main/scripts/install.sh | BINDIR="$HOME/bin" sh

Install a specific release:

curl -fsSL https://raw.githubusercontent.com/lossend/claude-profile/main/scripts/install.sh | VERSION=v0.1.0 sh

From a downloaded release asset

Download 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 version

Quick Start

Create your first profile from the current Claude settings:

claude-profile create work --description "Work profile"

Inspect available profiles:

claude-profile list

Preview what would change before applying a profile:

claude-profile diff work

Export the fully merged settings snapshot for a profile:

claude-profile export work

Apply a profile back into Claude:

claude-profile apply work

Rename a profile:

claude-profile rename work office

Delete a profile when you no longer need it:

claude-profile delete work

Edit layered config files under:

  • ~/.claude-profile/common/
  • ~/.claude-profile/profiles/work/
  • ~/.claude-profile/secrets/work.json

What It Does

  • Bootstraps ~/.claude-profile as a local Git repository on first create
  • Splits shared Claude settings into common/*.json
  • Stores per-profile overrides in profiles/<name>/layers/*.json
  • create writes the initial profile diff to profiles/<name>/layers/010-config.json by default
  • Stores sensitive overrides in secrets/<name>.json, ignored by Git
  • Rebuilds ~/.claude/settings.json from layered config with apply
  • Tracks the active profile in state/active.json
  • Installs shell completion for zsh, bash, and fish

Repository Layout

~/.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/

Commands

create

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.

apply

Rebuild ~/.claude/settings.json from common, profile-specific JSON files, and the local secret overlay.

claude-profile apply work

Useful flags:

  • --target: write to a non-default settings file

export

Export a complete settings snapshot for a profile without touching ~/.claude/settings.json.

claude-profile export work

By default, export writes settings-work.json to the current working directory. The exported file uses the same merged result as apply:

  • common/*.json
  • profiles/<name>/layers/*.json
  • secrets/<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

diff

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 diff

Shows 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

List available profiles, their description, config files, secret presence, and active marker.

claude-profile list

migrate

Migrate legacy profile directories from the old profile.json plus root-level *.json layout into manifest.json plus layers/*.json.

claude-profile migrate

delete

Delete a profile directory and its local secret file.

claude-profile delete work

delete 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

Rename an existing profile directory, its secret file, and the active profile state if applicable.

claude-profile rename work office

The new name must not already exist. If another rename is in progress the command exits immediately with an error.

Merge Rules

  • Objects merge recursively
  • Arrays replace the previous value
  • Scalars are overwritten by later files
  • Files are applied in lexicographic order
  • manifest.json is metadata only and never merged into Claude settings

Sensitive Fields

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.

Development

Build from source

If you are developing locally and do have Go installed:

go install .

Test

Run the test suite with:

go test ./...

Release

The shortest release path is a single command:

scripts/release.sh patch

That 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.1

If you want to verify the steps without changing git state:

scripts/release.sh --dry-run patch

The 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

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages