Documentation
¶
Overview ¶
ABOUTME: Applies a profile to Claude Code using replace strategy ABOUTME: Computes diff, resolves secrets, executes via claude CLI
ABOUTME: Concurrent execution engine for profile apply operations ABOUTME: Handles parallel marketplace/plugin installs with progress tracking
ABOUTME: Category detection and plugin mapping for marketplaces ABOUTME: Hardcoded support for known marketplaces (wshobson/agents)
ABOUTME: Non-interactive profile creation from flags or file input ABOUTME: Provides CreateSpec validation and profile construction
ABOUTME: Project detection logic for profile suggestions ABOUTME: Matches project files and content against profile detect rules
ABOUTME: Diff logic for comparing a saved profile against live Claude Code state ABOUTME: Types and functions for scope-aware profile comparison
ABOUTME: Embedded default profiles for claudeup ABOUTME: Uses Go embed to bundle profiles in the binary
ABOUTME: Writes .mcp.json files in Claude Code's native format ABOUTME: Enables project-scoped MCP servers that Claude auto-loads
ABOUTME: Profile struct and Load/Save functionality for claudeup ABOUTME: Profiles define a desired state of Claude Code configuration
ABOUTME: Resolution engine for profile includes (composable stacks) ABOUTME: Flattens include trees into a single merged profile with cycle detection
ABOUTME: Defines the Scope type for profile application targets ABOUTME: Supports user, project, and local scopes for Claude Code configuration
ABOUTME: Creates a profile from current Claude Code state ABOUTME: Reads installed plugins, marketplaces, MCP servers, and extensions
ABOUTME: Interactive wizard for creating profiles ABOUTME: Handles name validation, marketplace selection, plugin selection
ABOUTME: Worker pool for concurrent job execution during profile apply ABOUTME: Executes jobs in parallel with configurable worker count
Index ¶
- Constants
- func BuildRepoToNameLookup(claudeDir string) map[string]string
- func DefaultClaudeDir() string
- func Detect(dir string, p *Profile) (bool, error)
- func EnsureDefaultProfiles(profilesDir string) error
- func FindProfilePaths(profilesDir, name string) ([]string, error)
- func GenerateWizardDescription(marketplaceCount, pluginCount int) string
- func GetEmbeddedProfileScriptDir(profileName string) string
- func HasCategories(marketplaceRepo string) bool
- func IsEmbeddedProfile(name string) bool
- func MCPJSONExists(projectDir string) bool
- func ProjectProfilesDir(projectDir string) string
- func PromptForDescription(autoGenerated string) (string, error)
- func PromptForName() (string, error)
- func RunHook(profile *Profile, opts HookOptions) error
- func Save(profilesDir string, p *Profile) error
- func SaveToProject(projectDir string, p *Profile) error
- func SelectPluginsForMarketplace(marketplace Marketplace) ([]string, error)
- func ShouldRunHook(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, ...) bool
- func ValidateCreateSpec(description string, marketplaces []string, plugins []string) error
- func ValidateName(name string) error
- func ValidatePluginFormat(plugin string) error
- func ValidatePluginMarketplaces(plugins []string, marketplaces []Marketplace, registryKeys []string) error
- func WriteMCPJSON(projectDir string, servers []MCPServer) error
- type AmbiguousProfileError
- type ApplyAllScopesOptions
- type ApplyOptions
- type ApplyResult
- func Apply(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, ...) (*ApplyResult, error)
- func ApplyAllScopes(profile *Profile, claudeDir, claudeJSONPath, projectDir, claudeupHome string, ...) (*ApplyResult, error)
- func ApplyWithExecutor(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, ...) (*ApplyResult, error)
- func ApplyWithOptions(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, ...) (*ApplyResult, error)
- type Category
- type ClaudeJSON
- type ClaudeMCPServer
- type CommandExecutor
- type ConcurrentApplyOptions
- type ConcurrentApplyResult
- type CreateSpec
- type DefaultExecutor
- type DetectRules
- type Diff
- type DiffItem
- type DiffItemKind
- type DiffOp
- type DiffOptions
- type DirLoader
- type ExtensionSettings
- type HookEntry
- type HookOptions
- type InstallPluginsOptions
- type InstallPluginsResult
- type Job
- type JobResult
- type MCPJSONConfig
- type MCPJSONServer
- type MCPServer
- type Marketplace
- type MarketplaceMetadata
- type MarketplaceRegistry
- type MarketplaceSource
- type PerScopeSettings
- type PostApplyHook
- type Profile
- func CreateFromFlags(name, description string, marketplaceArgs, plugins []string, scope string) (*Profile, error)
- func CreateFromReader(name string, r io.Reader, descOverride string, scope string, ...) (*Profile, error)
- func FilterToScopes(p *Profile, scopes map[string]bool) *Profile
- func FindMatchingProfiles(dir string, profiles []*Profile) []*Profile
- func GetEmbeddedProfile(name string) (*Profile, error)
- func ListEmbeddedProfiles() ([]*Profile, error)
- func Load(profilesDir, name string) (*Profile, error)
- func LoadFromPath(path string) (*Profile, error)
- func ResolveIncludes(p *Profile, loader ProfileLoader) (*Profile, error)
- func Snapshot(name, claudeDir, claudeJSONPath, claudeupHome string) (*Profile, error)
- func SnapshotAllScopes(name, claudeDir, claudeJSONPath, projectDir, claudeupHome string) (*Profile, error)
- func SnapshotWithScope(name, claudeDir, claudeJSONPath, claudeupHome string, opts SnapshotOptions) (*Profile, error)
- func SuggestProfile(dir string, profiles []*Profile) *Profile
- func (p *Profile) AsPerScope() *Profile
- func (p *Profile) Clone(newName string) *Profile
- func (p *Profile) CombinedScopes() *Profile
- func (p *Profile) Equal(other *Profile) bool
- func (p *Profile) FilterToScope(scope string)
- func (p *Profile) ForScope(scope string) *Profile
- func (p *Profile) GenerateDescription() string
- func (p *Profile) HasConfigFields() bool
- func (p *Profile) HasMCPServersWithSecrets() bool
- func (p *Profile) IsMultiScope() bool
- func (p *Profile) IsStack() bool
- func (p *Profile) PreserveFrom(existing *Profile)
- func (p *Profile) PreserveMCPSecrets(existing *Profile) []string
- func (p *Profile) UnmarshalJSON(data []byte) error
- func (p *Profile) ValidateMarketplaceRefs(registryKeys []string) error
- type ProfileDiff
- type ProfileEntry
- type ProfileLoader
- type ProfileWithSource
- type ProgressCallback
- type ResetResult
- type Scope
- type ScopeDiff
- type ScopeSettings
- type SecretRef
- type SecretSource
- type SnapshotOptions
Constants ¶
const DefaultWorkers = 4
DefaultWorkers is the default number of concurrent workers
const MCPConfigFile = ".mcp.json"
MCPConfigFile is the filename for Claude's native MCP configuration
const MaxIncludeDepth = 50
MaxIncludeDepth limits how deeply nested include chains can be. Prevents resource exhaustion from pathological deep nesting.
const MaxInputSize = 10 * 1024 * 1024
MaxInputSize is the maximum size for JSON input (10MB)
Variables ¶
This section is empty.
Functions ¶
func BuildRepoToNameLookup ¶
BuildRepoToNameLookup reads known_marketplaces.json and builds a map from repo to name
func DefaultClaudeDir ¶
func DefaultClaudeDir() string
DefaultClaudeDir returns the Claude configuration directory Respects CLAUDE_CONFIG_DIR environment variable if set
func Detect ¶
Detect checks if a profile's detect rules match the given directory Files: ANY file existing is a match (OR-based within files) Contains: ANY pattern matching is a match (OR-based within contains) Overall: If both are specified, BOTH categories must have at least one match
func EnsureDefaultProfiles ¶
EnsureDefaultProfiles extracts embedded profiles to the profiles directory if they don't already exist
func FindProfilePaths ¶
FindProfilePaths walks profilesDir recursively and returns absolute paths to .json files whose filename stem matches name. If name contains a "/", it is treated as a relative path reference: only profilesDir/name.json is checked (after validating the path stays within profilesDir). Returns an empty slice (not an error) if profilesDir does not exist. The profilesDir argument is resolved to an absolute path internally.
func GenerateWizardDescription ¶
GenerateWizardDescription creates description based on wizard selections
func GetEmbeddedProfileScriptDir ¶
GetEmbeddedProfileScriptDir extracts embedded scripts for a profile to a temp directory Returns empty string if no scripts exist or extraction fails
func HasCategories ¶
HasCategories returns true if the marketplace has category metadata
func IsEmbeddedProfile ¶
IsEmbeddedProfile checks if a profile with the given name exists in embedded profiles
func MCPJSONExists ¶
MCPJSONExists returns true if a .mcp.json file exists in the directory
func ProjectProfilesDir ¶
ProjectProfilesDir returns the path to project-local profiles directory
func PromptForDescription ¶
PromptForDescription shows auto-generated description and allows editing
func PromptForName ¶
PromptForName prompts the user to enter a profile name Returns the validated name or an error
func RunHook ¶
func RunHook(profile *Profile, opts HookOptions) error
RunHook executes the post-apply hook
func SaveToProject ¶
SaveToProject saves a profile to the project's .claudeup/profiles/ directory
func SelectPluginsForMarketplace ¶
func SelectPluginsForMarketplace(marketplace Marketplace) ([]string, error)
SelectPluginsForMarketplace prompts user to select plugins from a marketplace Uses category-based selection if marketplace has categories, otherwise flat list
func ShouldRunHook ¶
func ShouldRunHook(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, opts HookOptions) bool
ShouldRunHook checks if the post-apply hook should run based on condition and current state
func ValidateCreateSpec ¶
ValidateCreateSpec validates input for non-interactive profile creation. Requires a description and at least one marketplace. Validates marketplace and plugin formats using ParseMarketplaceArg and ValidatePluginFormat.
func ValidateName ¶
ValidateName checks if a profile name is valid
func ValidatePluginFormat ¶
ValidatePluginFormat validates a plugin string is in "name@marketplace-ref" format. Plugin names can contain colons (e.g., "backend:api-design@marketplace"). Uses LastIndex to find @ since plugin names may contain @, but the last @ is the separator.
func ValidatePluginMarketplaces ¶ added in v5.8.0
func ValidatePluginMarketplaces(plugins []string, marketplaces []Marketplace, registryKeys []string) error
ValidatePluginMarketplaces checks that every plugin's marketplace ref resolves to either a marketplace in the profile or an installed marketplace registry key. Plugins without an @ separator are skipped (they're invalid format but that's caught by ValidatePluginFormat).
func WriteMCPJSON ¶
WriteMCPJSON writes a .mcp.json file to the given directory
Types ¶
type AmbiguousProfileError ¶
type AmbiguousProfileError struct {
Name string // the profile name that was searched for
Paths []string // relative paths of all matching profiles (forward-slash separated, without .json)
}
AmbiguousProfileError is returned when a profile name matches multiple files in the profiles directory (e.g. both "profiles/api.json" and "profiles/backend/api.json").
func (*AmbiguousProfileError) Error ¶
func (e *AmbiguousProfileError) Error() string
type ApplyAllScopesOptions ¶
type ApplyAllScopesOptions struct {
// ReplaceUserScope controls whether user-scope settings are replaced (true)
// or merged additively (false). Default is false (additive).
// Project and local scopes always use declarative (replace) semantics.
ReplaceUserScope bool
Reinstall bool // Force reinstall of plugins even if already installed
ShowProgress bool // Reserved; concurrent progress UI not yet integrated
Executor CommandExecutor // CLI executor; nil = create DefaultExecutor
Output io.Writer // Progress output destination; nil = os.Stdout
}
ApplyAllScopesOptions controls how multi-scope profiles are applied.
type ApplyOptions ¶
type ApplyOptions struct {
Scope Scope // user, project, or local
ProjectDir string // Required for project/local scope
DryRun bool // If true, don't make changes (not yet implemented)
Reinstall bool // If true, reinstall even if already installed
ShowProgress bool // If true, use concurrent apply with progress UI (project/local scope only)
Progress ProgressCallback // Optional progress callback for sequential installs
}
ApplyOptions controls how a profile is applied
type ApplyResult ¶
type ApplyResult struct {
PluginsRemoved []string
PluginsInstalled []string
PluginsAlreadyRemoved []string // Plugins that were already uninstalled
PluginsAlreadyPresent []string // Plugins that were already installed
MCPServersRemoved []string
MCPServersInstalled []string
MCPServersAlreadyPresent []string // MCP servers that were already configured
MarketplacesAdded []string
MarketplacesRemoved []string
Warnings []error // Non-fatal pre-operation notices (e.g. load failures with fallback)
Errors []error // Actual install/operation failures
}
ApplyResult contains the results of applying a profile
func Apply ¶
func Apply(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, secretChain *secrets.Chain) (*ApplyResult, error)
Apply executes the profile changes using the default executor
func ApplyAllScopes ¶
func ApplyAllScopes(profile *Profile, claudeDir, claudeJSONPath, projectDir, claudeupHome string, secretChain *secrets.Chain, opts *ApplyAllScopesOptions) (*ApplyResult, error)
ApplyAllScopes applies a profile to all scope levels. For multi-scope profiles (with PerScope), it applies each scope independently: settings files, plugin CLI installs, and extensions per scope. Project-scope MCP servers are written as .mcp.json; user/local MCP servers are installed via CLI. For legacy profiles (flat format), it applies to user scope only. The secretChain parameter is optional and used for MCP server secret resolution.
func ApplyWithExecutor ¶
func ApplyWithExecutor(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, secretChain *secrets.Chain, executor CommandExecutor) (*ApplyResult, error)
ApplyWithExecutor executes the profile changes using the provided executor. This is the legacy API for backward compatibility; use ApplyWithOptions for new code.
func ApplyWithOptions ¶
func ApplyWithOptions(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, secretChain *secrets.Chain, opts ApplyOptions) (*ApplyResult, error)
ApplyWithOptions applies a profile with the specified scope options
type Category ¶
Category represents a plugin category in a marketplace
func GetCategories ¶
GetCategories returns available categories for a marketplace
type ClaudeJSON ¶
type ClaudeJSON struct {
MCPServers map[string]ClaudeMCPServer `json:"mcpServers"`
}
ClaudeJSON represents the ~/.claude.json file structure (relevant parts)
type ClaudeMCPServer ¶
type ClaudeMCPServer struct {
Type string `json:"type"`
Command string `json:"command"`
Args []string `json:"args"`
Env map[string]string `json:"env"`
}
ClaudeMCPServer represents an MCP server in ~/.claude.json
type CommandExecutor ¶
type CommandExecutor interface {
Run(args ...string) error
RunWithOutput(args ...string) (string, error)
}
CommandExecutor runs claude CLI commands
type ConcurrentApplyOptions ¶
type ConcurrentApplyOptions struct {
ClaudeDir string
Scope string // "user", "project", "local"
Reinstall bool // Force reinstall even if already installed
Output io.Writer
Executor CommandExecutor
}
ConcurrentApplyOptions configures concurrent apply behavior
type ConcurrentApplyResult ¶
type ConcurrentApplyResult struct {
MarketplacesInstalled []string
MarketplacesSkipped []string
PluginsInstalled []string
PluginsSkipped []string
MCPServersInstalled []string
MCPServersSkipped []string // MCP servers skipped (already configured)
Warnings []error // Non-fatal pre-operation notices (e.g. load failures with fallback)
Errors []error // Actual install/operation failures
}
ConcurrentApplyResult contains results from concurrent apply
func ApplyConcurrently ¶
func ApplyConcurrently(profile *Profile, opts ConcurrentApplyOptions) (*ConcurrentApplyResult, error)
ApplyConcurrently installs marketplaces and plugins concurrently with progress tracking
type CreateSpec ¶
type CreateSpec struct {
Description string `json:"description"`
Marketplaces json.RawMessage `json:"marketplaces"`
Plugins []string `json:"plugins"`
MCPServers []MCPServer `json:"mcpServers,omitempty"`
PerScope *PerScopeSettings `json:"perScope,omitempty"`
Detect DetectRules `json:"detect,omitempty"`
}
CreateSpec is the input format for file/stdin profile creation
type DefaultExecutor ¶
type DefaultExecutor struct {
ClaudeDir string // Directory to use for CLAUDE_CONFIG_DIR env var
}
DefaultExecutor runs commands using the real claude CLI
func (*DefaultExecutor) Run ¶
func (e *DefaultExecutor) Run(args ...string) error
Run executes the claude CLI with the given arguments
func (*DefaultExecutor) RunWithOutput ¶
func (e *DefaultExecutor) RunWithOutput(args ...string) (string, error)
RunWithOutput executes the claude CLI and returns captured output
type DetectRules ¶
type DetectRules struct {
Files []string `json:"files,omitempty"`
Contains map[string]string `json:"contains,omitempty"`
}
DetectRules defines how to auto-detect if a profile matches a project
type Diff ¶
type Diff struct {
PluginsToRemove []string
PluginsToInstall []string
MCPToRemove []string
MCPToInstall []MCPServer
MarketplacesToAdd []Marketplace
MarketplacesToRemove []Marketplace
}
Diff represents what needs to change to apply a profile
func ComputeDiff ¶
ComputeDiff calculates what changes are needed to apply a profile. This compares against user scope by default; use ComputeDiffWithScope for scope-aware comparison.
func ComputeDiffWithScope ¶
func ComputeDiffWithScope(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, opts DiffOptions) (*Diff, error)
ComputeDiffWithScope calculates what changes are needed to apply a profile at a specific scope. For project/local scope, it compares only against that scope's current state (not user scope). This prevents confusing "Remove" actions for user-scope items when applying at project scope.
type DiffItem ¶ added in v5.8.0
type DiffItem struct {
Op DiffOp
Kind DiffItemKind
Name string
Detail string // optional context (e.g., extension category, changed MCP field)
}
DiffItem represents a single difference
func UserScopeExtras ¶ added in v5.11.0
UserScopeExtras returns plugins that exist in live user-scope config but not in the saved profile. Both inputs should be in PerScope form (call AsPerScope() before passing). Returns nil if the saved profile has no user scope or there are no extras.
Only plugins are compared because ReplaceUserScope only affects enabledPlugins in settings.json. MCP servers, extensions, and marketplaces are managed separately and not affected by the replace toggle.
type DiffItemKind ¶ added in v5.8.0
type DiffItemKind string
DiffItemKind represents what type of config item differs
const ( DiffPlugin DiffItemKind = "plugin" DiffMCP DiffItemKind = "mcp" DiffExtension DiffItemKind = "extension" DiffMarketplace DiffItemKind = "marketplace" )
type DiffOptions ¶
type DiffOptions struct {
Scope Scope // Target scope for comparison
ProjectDir string // Required for project/local scope
Reinstall bool // Force all profile items into install lists
}
DiffOptions controls how a diff is computed
type DirLoader ¶
type DirLoader struct {
ProfilesDir string
}
DirLoader loads profiles from disk via Load() with embedded fallback.
func (*DirLoader) LoadProfile ¶
LoadProfile loads a profile by name, delegating to Load() which handles both short names (recursive search) and path-qualified names (direct lookup). Falls back to embedded profiles only when the profile is not found on disk. Other errors (ambiguous names, invalid JSON) propagate without fallback.
type ExtensionSettings ¶ added in v5.3.0
type ExtensionSettings struct {
Agents []string `json:"agents,omitempty"`
Commands []string `json:"commands,omitempty"`
Skills []string `json:"skills,omitempty"`
Hooks []string `json:"hooks,omitempty"`
Rules []string `json:"rules,omitempty"`
OutputStyles []string `json:"output-styles,omitempty"`
}
ExtensionSettings contains extension patterns to enable. These are items from ~/.claudeup/ext/ that get symlinked to ~/.claude/
func ReadExtensions ¶ added in v5.9.0
func ReadExtensions(claudeDir, claudeupHome string) (*ExtensionSettings, error)
ReadExtensions reads enabled extensions from enabled.json, verifying each item exists in the active directory. Stale entries are filtered out.
func ReadProjectExtensions ¶ added in v5.9.0
func ReadProjectExtensions(projectDir string) *ExtensionSettings
ReadProjectExtensions scans .claude/{agents,rules}/ in the project directory for regular files (not symlinks). Regular files are project-scoped extensions; symlinks are user-scoped extensions managed by claudeup and should be skipped.
type HookOptions ¶
type HookOptions struct {
ForceSetup bool // Run hook even if first-run check would skip
NoInteractive bool // Skip hook entirely (for CI/scripting)
ScriptDir string // Directory containing hook scripts (for built-in profiles)
}
HookOptions controls post-apply hook behavior
type InstallPluginsOptions ¶
type InstallPluginsOptions struct {
// Scope for plugin installation: "", "project", or "local"
// Empty string means user scope (no --scope flag)
Scope string
// InstalledPlugins is an optional map of already-installed plugins.
// If provided, plugins in this map are skipped before running install.
// If nil, no pre-filtering occurs (relies on "already installed" output).
InstalledPlugins map[string]bool
// Progress is an optional callback for reporting installation progress.
// Called with (current, total, pluginName) for each plugin being installed.
Progress ProgressCallback
}
InstallPluginsOptions configures plugin installation behavior
type InstallPluginsResult ¶
type InstallPluginsResult struct {
Installed []string
Skipped []string // Already installed
Errors []error
}
InstallPluginsResult contains the result of installing plugins
func InstallPluginsWithProgress ¶
func InstallPluginsWithProgress( plugins []string, executor CommandExecutor, opts InstallPluginsOptions, ) *InstallPluginsResult
InstallPluginsWithProgress installs plugins and reports progress. It handles both pre-filtering (when InstalledPlugins is provided) and fallback detection of "already installed" from command output.
type Job ¶
type Job struct {
Name string // Identifier for the job (e.g., plugin name)
Type string // Job type (e.g., "marketplace", "plugin", "mcp")
Execute func() error // The work to perform
}
Job represents a unit of work to execute
type JobResult ¶
type JobResult struct {
Name string // Job identifier
Type string // Job type
Success bool // True if Execute returned nil
Error error // The error if Execute failed
}
JobResult represents the outcome of executing a job
type MCPJSONConfig ¶
type MCPJSONConfig struct {
MCPServers map[string]MCPJSONServer `json:"mcpServers"`
}
MCPJSONConfig represents Claude Code's native .mcp.json format
type MCPJSONServer ¶
type MCPJSONServer struct {
Command string `json:"command"`
Args []string `json:"args,omitempty"`
Env map[string]string `json:"env,omitempty"`
}
MCPJSONServer represents an MCP server in Claude's .mcp.json format
type MCPServer ¶
type MCPServer struct {
Name string `json:"name"`
Command string `json:"command"`
Args []string `json:"args,omitempty"`
Scope string `json:"scope,omitempty"`
Secrets map[string]SecretRef `json:"secrets,omitempty"`
}
MCPServer represents an MCP server configuration
func ReadMCPServersForScope ¶ added in v5.9.0
ReadMCPServersForScope reads MCP servers from the appropriate config file for the given scope. claudeJSONPath is the path to the user-level Claude config file (typically ~/.claude/.claude.json). projectDir is the project root directory, used when scope is "project" to read .mcp.json.
type Marketplace ¶
type Marketplace struct {
Source string `json:"source"`
Repo string `json:"repo,omitempty"` // Used for github sources
URL string `json:"url,omitempty"` // Used for git sources
}
Marketplace represents a plugin marketplace source
func GetAvailableMarketplaces ¶
func GetAvailableMarketplaces() []Marketplace
GetAvailableMarketplaces returns all available marketplaces for selection Loads from ~/.claude/plugins/known_marketplaces.json, falling back to embedded profiles Filters out invalid entries where both repo and url are empty
func ParseMarketplaceArg ¶
func ParseMarketplaceArg(arg string) (Marketplace, error)
ParseMarketplaceArg parses a marketplace argument in "owner/repo" format. Whitespace around owner and repo is trimmed for robustness. Additional path segments (owner/repo/extra) are preserved in Repo field and will be validated when the marketplace is actually accessed.
func SelectMarketplaces ¶
func SelectMarketplaces(available []Marketplace) ([]Marketplace, error)
SelectMarketplaces prompts user to select marketplaces Returns selected marketplaces or error
func UsedMarketplaces ¶ added in v5.6.0
func UsedMarketplaces(claudeDir string, plugins []string) ([]Marketplace, error)
UsedMarketplaces returns marketplaces referenced by the given plugins.
func (Marketplace) DisplayName ¶
func (m Marketplace) DisplayName() string
DisplayName returns the repo or URL for display purposes
type MarketplaceMetadata ¶
type MarketplaceMetadata struct {
Source MarketplaceSource `json:"source"`
}
MarketplaceMetadata represents metadata for a marketplace
type MarketplaceRegistry ¶
type MarketplaceRegistry map[string]MarketplaceMetadata
MarketplaceRegistry represents known_marketplaces.json
type MarketplaceSource ¶
type MarketplaceSource struct {
Source string `json:"source"`
Repo string `json:"repo,omitempty"`
URL string `json:"url,omitempty"`
}
MarketplaceSource represents the source of a marketplace
type PerScopeSettings ¶
type PerScopeSettings struct {
User *ScopeSettings `json:"user,omitempty"`
Project *ScopeSettings `json:"project,omitempty"`
Local *ScopeSettings `json:"local,omitempty"`
}
PerScopeSettings organizes configuration by scope level. This enables profiles to capture and restore settings to the correct scope.
type PostApplyHook ¶
type PostApplyHook struct {
Script string `json:"script,omitempty"` // Script path relative to profile (takes precedence)
Command string `json:"command,omitempty"` // Direct command to run (used if Script is empty)
Condition string `json:"condition,omitempty"` // "always" (default) or "first-run"
}
PostApplyHook defines a hook to run after a profile is applied.
Execution order: Script takes precedence over Command. If both are set, only Script will be executed.
Condition types:
- "always" (default): Hook runs every time the profile is applied
- "first-run": Hook only runs if no plugins from the profile's marketplaces are currently enabled
Security note: Hooks execute arbitrary shell commands. Only use profiles from trusted sources.
type Profile ¶
type Profile struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Includes []string `json:"includes,omitempty"`
MCPServers []MCPServer `json:"mcpServers,omitempty"`
Marketplaces []Marketplace `json:"marketplaces,omitempty"`
Plugins []string `json:"plugins,omitempty"`
SkipPluginDiff bool `json:"skipPluginDiff,omitempty"` // If true, don't add/remove plugins (managed externally e.g. by wizard)
Detect DetectRules `json:"detect,omitempty"`
PostApply *PostApplyHook `json:"postApply,omitempty"`
// PerScope contains settings organized by scope (user, project, local).
// When present, this takes precedence over the flat Plugins/MCPServers fields.
// When absent, the flat fields are treated as user-scope (backward compatibility).
PerScope *PerScopeSettings `json:"perScope,omitempty"`
// Extensions contains patterns for extensions to enable (agents, commands, etc.)
Extensions *ExtensionSettings `json:"extensions,omitempty"`
// SettingsHooks contains hooks to merge into settings.json by event type
SettingsHooks map[string][]HookEntry `json:"settingsHooks,omitempty"`
}
Profile represents a Claude Code configuration profile
func CreateFromFlags ¶
func CreateFromFlags(name, description string, marketplaceArgs, plugins []string, scope string) (*Profile, error)
CreateFromFlags creates a profile from CLI flag values. Uses ValidateCreateSpec for input validation and ParseMarketplaceArg to convert marketplace strings to Marketplace structs. Plugins and MCPServers are placed under the specified scope in PerScope.
func CreateFromReader ¶
func CreateFromReader(name string, r io.Reader, descOverride string, scope string, scopeExplicit bool) (*Profile, error)
CreateFromReader creates a profile from JSON input. If the input contains perScope, it is used directly and the scope argument is unused. If scopeExplicit is true and the input contains perScope, an error is returned because the caller's explicit scope flag would be silently disregarded. If the input uses flat plugins/mcpServers, they are wrapped into perScope under the specified scope. It is an error to specify both flat fields and perScope.
func FilterToScopes ¶ added in v5.10.1
FilterToScopes returns a copy of the profile containing only the scopes present in the given map (keyed by scope name). Marketplaces are included only when user scope is active (they are always user-scoped).
func FindMatchingProfiles ¶
FindMatchingProfiles returns all profiles that match the given directory
func GetEmbeddedProfile ¶
GetEmbeddedProfile returns an embedded profile by name
func ListEmbeddedProfiles ¶
ListEmbeddedProfiles returns all embedded profiles
func Load ¶
Load reads a profile from the profiles directory. If name contains "/", it is treated as a relative path within profilesDir. Otherwise, profilesDir is searched recursively for a matching .json file. Returns an error if the name matches multiple profiles (ambiguous).
func LoadFromPath ¶
LoadFromPath loads a profile from an absolute file path. If the JSON does not contain a name field, the name is derived from the filename.
func ResolveIncludes ¶
func ResolveIncludes(p *Profile, loader ProfileLoader) (*Profile, error)
ResolveIncludes recursively resolves includes and returns a merged profile. Returns an error if:
- p is nil
- the profile has includes alongside config fields (stacks must be pure)
- a cycle is detected
- an included profile cannot be loaded
If the profile has no includes, it is returned as-is.
func SnapshotAllScopes ¶
func SnapshotAllScopes(name, claudeDir, claudeJSONPath, projectDir, claudeupHome string) (*Profile, error)
SnapshotAllScopes creates a Profile capturing settings from all three scopes (user, project, local) and organizing them in the PerScope structure. This is the preferred way to save profiles as it preserves scope information.
func SnapshotWithScope ¶
func SnapshotWithScope(name, claudeDir, claudeJSONPath, claudeupHome string, opts SnapshotOptions) (*Profile, error)
SnapshotWithScope creates a Profile from a specific scope
func SuggestProfile ¶
SuggestProfile finds the best matching profile for a directory Returns nil if no profiles match
func (*Profile) AsPerScope ¶ added in v5.8.0
AsPerScope returns a copy of the profile with flat fields lifted into PerScope.User. If PerScope is already set, returns a shallow copy as-is.
func (*Profile) CombinedScopes ¶
CombinedScopes returns a flat Profile combining all scopes (user + project + local). This aggregates plugins and MCP servers from all scopes into single lists, matching how Claude Code accumulates settings from user → project → local. Useful for comparing a multi-scope profile against the combined system state.
func (*Profile) Equal ¶
Equal compares two profiles for semantic equality, ignoring the Name field. Name is treated as an identifier, not content - two profiles with different names but identical content are considered equal. Nil and empty slices are treated as equivalent.
func (*Profile) FilterToScope ¶ added in v5.4.0
FilterToScope removes all scope data except the specified scope. This is used when saving a profile for a single scope.
func (*Profile) ForScope ¶
ForScope returns a flat Profile containing only settings for the specified scope. This is useful for applying a single scope from a multi-scope profile. Marketplaces are always included since they're user-scoped.
func (*Profile) GenerateDescription ¶
GenerateDescription creates a human-readable description of the profile contents
func (*Profile) HasConfigFields ¶
HasConfigFields returns true if the profile has any configuration fields beyond name, description, and includes.
func (*Profile) HasMCPServersWithSecrets ¶
HasMCPServersWithSecrets returns true if any MCP server in the profile has secrets defined. This is used to warn users that sync cannot resolve secrets.
func (*Profile) IsMultiScope ¶
IsMultiScope returns true if this profile uses per-scope settings.
func (*Profile) IsStack ¶
IsStack returns true if this profile composes other profiles via includes.
func (*Profile) PreserveFrom ¶
PreserveFrom copies extensions from an existing profile. When re-saving, this keeps only the extensions the user originally saved, preventing accumulation of items enabled by other tools.
func (*Profile) PreserveMCPSecrets ¶ added in v5.11.2
PreserveMCPSecrets restores $VAR references and Secrets metadata from an existing profile onto a snapshotted profile. When profile save captures live MCP server configs, secret values are resolved to plaintext. This function matches servers by name and replaces resolved arg values with the original $KEY references using positional matching against the existing profile's args.
Returns warning messages for servers that could not be restored (e.g., when arg counts have changed since the profile was last saved).
func (*Profile) UnmarshalJSON ¶ added in v5.3.0
UnmarshalJSON handles migration from the old "localItems" JSON field to the new "extensions" field. Profiles saved before the rename used "localItems"; this ensures they load correctly.
func (*Profile) ValidateMarketplaceRefs ¶ added in v5.8.0
ValidateMarketplaceRefs checks that all plugin marketplace refs in this profile resolve to either a marketplace in the profile or an installed registry key. Uses CombinedScopes() to aggregate plugins from all scopes.
type ProfileDiff ¶ added in v5.8.0
type ProfileDiff struct {
ProfileName string
DescriptionChange *[2]string // [profile, live] if different
Scopes []ScopeDiff
}
ProfileDiff contains the full diff result
func ComputeProfileDiff ¶ added in v5.8.0
func ComputeProfileDiff(saved, live *Profile) *ProfileDiff
ComputeProfileDiff compares a saved profile against a live snapshot. Both inputs should already be in PerScope form (caller normalizes via AsPerScope).
func (*ProfileDiff) Counts ¶ added in v5.8.0
func (d *ProfileDiff) Counts() (added, removed, modified int)
Counts returns the number of additions, removals, and modifications across all scopes
func (*ProfileDiff) IsEmpty ¶ added in v5.8.0
func (d *ProfileDiff) IsEmpty() bool
IsEmpty returns true if there are no differences
type ProfileEntry ¶
ProfileEntry is a profile with its location relative to the profiles directory. RelPath uses forward slashes (e.g. "backend/api.json" or "mobile.json").
func List ¶
func List(profilesDir string) ([]ProfileEntry, error)
List returns all profiles in the profiles directory (including subdirectories), sorted by name then by relative path for duplicates.
func (ProfileEntry) DisplayName ¶
func (e ProfileEntry) DisplayName() string
DisplayName returns the profile's display name for listing. For root profiles, this is just the profile name. For nested profiles, this is the relative path without the .json extension.
type ProfileLoader ¶
ProfileLoader loads a profile by name or path-qualified name.
type ProfileWithSource ¶
type ProfileWithSource struct {
*Profile
Source string // "user" or "project"
RelPath string // relative path within profiles dir (e.g. "backend/api.json")
}
ProfileWithSource wraps a profile with its source location and relative path
func ListAll ¶
func ListAll(userProfilesDir, projectDir string) ([]*ProfileWithSource, error)
ListAll returns profiles from both user and project directories. Project profiles take precedence over user profiles with the same name.
func (*ProfileWithSource) DisplayName ¶
func (p *ProfileWithSource) DisplayName() string
DisplayName returns the profile's display name for listing. For root profiles, this is just the profile name. For nested profiles, this is the relative path without the .json extension.
type ProgressCallback ¶
ProgressCallback reports installation progress for multi-item operations
type ResetResult ¶
type ResetResult struct {
PluginsRemoved []string
MCPServersRemoved []string
MarketplacesRemoved []string
Errors []error
}
ResetResult contains the results of resetting a profile
func Reset ¶
func Reset(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string) (*ResetResult, error)
Reset removes everything a profile installed (plugins, MCP servers, marketplaces)
func ResetWithExecutor ¶
func ResetWithExecutor(profile *Profile, claudeDir, claudeJSONPath, claudeupHome string, executor CommandExecutor) (*ResetResult, error)
ResetWithExecutor removes everything a profile installed using the provided executor
type Scope ¶
type Scope string
Scope represents where a profile should be applied
const ( // ScopeUser applies profile at user level (~/.claude/) ScopeUser Scope = "user" // ScopeProject applies profile at project level (.claude/settings.json + .mcp.json) ScopeProject Scope = "project" // ScopeLocal applies profile locally for this project only (~/.claudeup/projects.json) ScopeLocal Scope = "local" )
func ParseScope ¶
ParseScope converts a string to a Scope, returning an error for invalid values
type ScopeSettings ¶
type ScopeSettings struct {
Plugins []string `json:"plugins,omitempty"`
MCPServers []MCPServer `json:"mcpServers,omitempty"`
Extensions *ExtensionSettings `json:"extensions,omitempty"`
}
ScopeSettings contains settings for a single scope level.
type SecretRef ¶
type SecretRef struct {
Description string `json:"description,omitempty"`
Sources []SecretSource `json:"sources"`
}
SecretRef defines a secret requirement with multiple resolution sources
type SecretSource ¶
type SecretSource struct {
Type string `json:"type"` // env, 1password, keychain
Key string `json:"key,omitempty"` // for env
Ref string `json:"ref,omitempty"` // for 1password
Service string `json:"service,omitempty"` // for keychain
Account string `json:"account,omitempty"` // for keychain
}
SecretSource defines a single source for resolving a secret
type SnapshotOptions ¶
type SnapshotOptions struct {
Scope string // user, project, or local
ProjectDir string // Required for project/local scope
}
SnapshotOptions controls how a snapshot is taken