Documentation
¶
Overview ¶
Package config loads, merges, and validates Hygge configuration from multiple TOML sources.
Merge order (lowest → highest precedence) ¶
- Builtin defaults (defaults.go)
- User config: $XDG_CONFIG_HOME/hygge/config.toml
- Profile: $XDG_CONFIG_HOME/hygge/profiles/<name>.toml Resolved recursively via the "extends" key (max depth 8, cycle-detected).
- Walk-up .hygge/config.toml starting from opts.Pwd going up to $HOME. Files closer to Pwd have higher precedence.
- Environment variables: HYGGE_model__provider → model.provider. Uses "__" (double underscore) as path-segment separator; single underscores within a segment are preserved as part of the key name. Format: HYGGE_<segment>__<segment>__<segment>=<value> Each segment is case-folded to lowercase for config key lookup. Examples: HYGGE_model__provider=openai → model.provider HYGGE_permission__file_write=allow → permission.file_write Values are best-effort coerced.
- CLI flags: opts.Flags (dotted-path map[string]any), merged last.
Unknown-keys policy ¶
The underlying merged map retains every key from every source so future tasks can pick up new sections without breaking existing user configs. The typed Config struct is decoded with ErrorUnused=false so extra keys in the map are silently ignored during struct decode. This lets users have e.g. an [mcp] section today without getting errors — it will be picked up once that package is implemented. Validation of known keys is rigorous.
Unset sentinel ¶
A string value of "__hygge_unset__" removes the key from the merged result. Use this in a higher-precedence source to clear a key set by a lower one.
Env-var interpolation ¶
String values of the form $VAR or ${VAR} are expanded using opts.EnvLookup at merge time. If the variable is not set the literal string is preserved and a slog.Warn message is emitted.
Index ¶
- Variables
- func Load(ctx context.Context, opts LoadOptions) (*Config, Provenance, error)
- func PluginSettings(raw map[string]any) map[string]map[string]any
- func ResolvePrompt(raw string, baseDir string) string
- func WriteDefaultProfile(opts WriteDefaultProfileOptions, profileName string) (string, error)
- func WriteInitStyle(opts WriteInitStyleOptions, style InitStyleConfig) (string, error)
- func WriteModelSelection(opts WriteModelOptions, providerName, modelName string) (string, error)
- func WriteOnboardingMode(opts WriteOnboardingModeOptions, mode ModeConfig) (string, error)
- func WritePluginSources(opts WritePluginSourcesOptions, sources []string) (string, error)
- func WriteProviderAPIKey(opts WriteProviderAPIKeyOptions, providerName, apiKey string) (string, error)
- func WriteSubagentsToml(opts WriteSubagentsTomlOptions, agents []OnboardingSubagent) error
- func WriteThemeSelection(opts WriteThemeSelectionOptions, themeName string) (string, error)
- type CatalogConfig
- type CompactionConfig
- type Config
- type InitStyleConfig
- type InvalidValueError
- type LoadOptions
- type MergeTypeError
- type ModeConfig
- type ModelConfig
- type NotificationsConfig
- type OnboardingSubagent
- type ParseError
- type PermissionConfig
- type PermissionMode
- type PluginsConfig
- type Provenance
- type SessionConfig
- type Source
- type SubagentEntry
- type ThemeConfig
- type UIConfig
- type UnknownKeyError
- type WriteDefaultProfileOptions
- type WriteInitStyleOptions
- type WriteModelOptions
- type WriteOnboardingModeOptions
- type WritePluginSourcesOptions
- type WriteProviderAPIKeyOptions
- type WriteSubagentsTomlOptions
- type WriteThemeSelectionOptions
Constants ¶
This section is empty.
Variables ¶
var ( // ErrProfileNotFound is returned when a named profile file does not exist. // Loading the "default" profile when its file is absent is NOT an error — // it is treated as a clean first-run where no profile layer is applied. ErrProfileNotFound = errors.New("profile not found") // ErrCyclicProfile is returned when profile extends chains form a cycle. ErrCyclicProfile = errors.New("cyclic profile extends chain") // ErrProfileDepth is returned when the profile extends chain exceeds maxProfileDepth. ErrProfileDepth = errors.New("profile extends chain too deep") )
Sentinel errors returned by this package.
Functions ¶
func Load ¶
func Load(ctx context.Context, opts LoadOptions) (*Config, Provenance, error)
Load resolves configuration from all sources and returns the merged, validated Config together with its Provenance map.
func PluginSettings ¶
PluginSettings returns the per-plugin config tables from the raw merged config map. Each key is a plugin name (from [plugins.<name>] in config.toml) and the value is the free-form table content.
The raw map is needed here because mapstructure cannot decode dynamic map keys; we extract them from the merged map directly.
func ResolvePrompt ¶
ResolvePrompt resolves a prompt string. If it starts with "file:" the remainder is treated as a file path and the file contents are returned. Relative paths are resolved against baseDir (typically XDG_CONFIG_HOME/hygge). If the file cannot be read, the raw string is returned with a warning.
func WriteDefaultProfile ¶
func WriteDefaultProfile(opts WriteDefaultProfileOptions, profileName string) (string, error)
WriteDefaultProfile persists default_profile in the user config while preserving unrelated config fields. CLI --profile still overrides this value.
func WriteInitStyle ¶ added in v0.10.0
func WriteInitStyle(opts WriteInitStyleOptions, style InitStyleConfig) (string, error)
WriteInitStyle persists all modes for an init style to the user config at $XDG_CONFIG_HOME/hygge/config.toml. Subagents are written into the [subagents.<name>] tables inside config.toml (the config-defined subagent path) rather than the legacy subagents.toml. Existing unrelated config fields are preserved. Existing modes and subagents are replaced because init is a bootstrap/reset operation for the user's agent layout.
func WriteModelSelection ¶
func WriteModelSelection(opts WriteModelOptions, providerName, modelName string) (string, error)
WriteModelSelection persists provider/name to one deterministic writable file. Target policy: if the winning model provenance already comes from a real config file, update that file; otherwise create/update the user config at $XDG_CONFIG_HOME/hygge/config.toml. Env/flag/default-only selections are never rewritten in place. Existing TOML is decoded to a generic map before writing so unrelated keys and unknown sections are preserved; comments may be reformatted by go-toml.
func WriteOnboardingMode ¶ added in v0.7.0
func WriteOnboardingMode(opts WriteOnboardingModeOptions, mode ModeConfig) (string, error)
WriteOnboardingMode persists a single [[modes]] entry produced by the onboarding wizard into the user config at $XDG_CONFIG_HOME/hygge/config.toml. It always targets the user config (no provenance reuse) because onboarding only ever runs when no real model/auth config exists. The first call creates the file; subsequent calls from mode editing replace the matching entry.
func WritePluginSources ¶ added in v0.4.0
func WritePluginSources(opts WritePluginSourcesOptions, sources []string) (string, error)
WritePluginSources persists [plugins].sources while preserving unrelated config fields and per-plugin [plugins.<name>] tables. Target policy mirrors the other narrow writers: update the winning real plugins.sources file when known; otherwise create/update the user config.
func WriteProviderAPIKey ¶
func WriteProviderAPIKey(opts WriteProviderAPIKeyOptions, providerName, apiKey string) (string, error)
WriteProviderAPIKey persists apiKey into model.options.api_key while preserving unrelated config fields and existing model options.
func WriteSubagentsToml ¶ added in v0.7.0
func WriteSubagentsToml(opts WriteSubagentsTomlOptions, agents []OnboardingSubagent) error
WriteSubagentsToml appends or replaces OnboardingSubagent entries in the user-level $XDG_CONFIG_HOME/hygge/subagents.toml file. Entries sharing a Name are updated in-place; new names are appended. Existing entries for other names are left untouched.
func WriteThemeSelection ¶
func WriteThemeSelection(opts WriteThemeSelectionOptions, themeName string) (string, error)
WriteThemeSelection persists theme.name while preserving unrelated config fields. Target policy mirrors the model writer: update the winning real theme provenance when known, otherwise write the user config.
Types ¶
type CatalogConfig ¶
type CatalogConfig struct {
// RefreshInterval is a Go duration string (e.g. "24h", "1h30m") that,
// when non-empty, schedules a periodic background refresh of the
// Catwalk catalog. Empty string (the default) means no periodic
// refresh — the one-shot startup refresh still fires.
//
// Values that cannot be parsed as a duration, or that are negative,
// are treated as empty with a slog.Warn.
RefreshInterval string `mapstructure:"refresh_interval"`
}
CatalogConfig controls the shared model catalog.
func (CatalogConfig) RefreshIntervalDuration ¶
func (c CatalogConfig) RefreshIntervalDuration() time.Duration
RefreshIntervalDuration parses the RefreshInterval string into a time.Duration. Returns 0 when the string is empty. Logs a Warn and returns 0 when the value is unparseable or negative.
type CompactionConfig ¶
type CompactionConfig struct {
// ThresholdPct is the percentage of the model's context window at which
// the advisory suggestion banner appears. 0 disables the suggestion
// entirely. Valid range: 0–99. Values ≥ 100 warn and reset to the
// default (80). Default: 80.
ThresholdPct float64 `mapstructure:"threshold_pct"`
}
CompactionConfig controls the compaction suggestion banner.
type Config ¶
type Config struct {
// Profile is the name of the active profile. Not decoded from TOML;
// set by Load after profile resolution.
Profile string `mapstructure:"-"`
Model ModelConfig `mapstructure:"model"`
Permission PermissionConfig `mapstructure:"permission"`
Theme ThemeConfig `mapstructure:"theme"`
UI UIConfig `mapstructure:"ui"`
Compaction CompactionConfig `mapstructure:"compaction"`
Session SessionConfig `mapstructure:"session"`
Catalog CatalogConfig `mapstructure:"catalog"`
Notifications NotificationsConfig `mapstructure:"notifications"`
// Subagents holds subagent type definitions declared directly in
// config.toml (or a profile). Each key is the type name; the value
// carries the same fields as a subagents.toml entry. These are merged
// through the normal profile machinery, so a profile can add or override
// individual types. The subagent registry loader consults this map in
// addition to the legacy subagents.toml files.
Subagents map[string]SubagentEntry `mapstructure:"subagents"`
// Modes defines the agent modes available to the user. The app is always
// in a mode; the first mode is the default. Each mode specifies a
// provider and model. The [[modes]] array-of-tables is the preferred
// way to configure provider/model; the [model] section (provider/name)
// is only used as a fallback for modes that omit those fields or when
// no [[modes]] are declared at all.
// When no [[modes]] are declared, a built-in "General" mode is
// synthesized: it uses [model].provider/name when set, otherwise falls
// back to hard-coded defaults ("anthropic" / "claude-sonnet-4-5").
// After Load(), Modes is guaranteed to have at least one entry.
Modes []ModeConfig `mapstructure:"modes"`
// Plugins holds plugin source and per-plugin configuration.
// Sources is the list of plugin source URIs declared in [plugins].sources.
// PluginSettings maps plugin names to their [plugins.<name>] config tables.
Plugins PluginsConfig `mapstructure:"plugins"`
// contains filtered or unexported fields
}
Config is the typed, fully-resolved configuration.
func (*Config) RawPluginSettings ¶
RawPluginSettings returns the per-plugin config tables from the raw merged config. Each key is a plugin name (from [plugins.<name>] in config.toml). Returns nil when no plugin-specific tables are set.
Example TOML:
[plugins.policy-guard] strict = true blocked_patterns = ["rm -rf", "sudo"]
type InitStyleConfig ¶ added in v0.10.0
type InitStyleConfig struct {
Modes []ModeConfig
Subagents []OnboardingSubagent
}
InitStyleConfig is a complete mode/subagent preset selected by `hygge init`.
type InvalidValueError ¶
InvalidValueError is returned when a value fails validation (e.g. bad PermissionMode string).
func (*InvalidValueError) Error ¶
func (e *InvalidValueError) Error() string
type LoadOptions ¶
type LoadOptions struct {
// Pwd is the starting directory for .hygge/config.toml walk-up.
// Defaults to os.Getwd() when empty.
Pwd string
// Profile is the explicit profile name to load. When empty, Load
// consults the state file and falls back to "default".
Profile string
// EnvLookup is used for environment-variable lookups and $VAR
// interpolation. Defaults to os.LookupEnv. Override in tests.
EnvLookup func(key string) (string, bool)
// Flags contains pre-parsed CLI flag overrides keyed by dotted path.
// Pass nil when there are no flag overrides.
Flags map[string]any
// HomeDir overrides $HOME for XDG path computation. Useful in tests.
HomeDir string
// IgnoreExternalSources loads only built-in defaults and explicit Flags.
// It skips user config, profiles, walk-up config, and HYGGE_* env overrides.
IgnoreExternalSources bool
}
LoadOptions controls the behaviour of Load.
type MergeTypeError ¶
type MergeTypeError struct {
Key string
LowFile string
HighFile string
LowType string
HighType string
}
MergeTypeError is returned when two sources provide different concrete types for the same key.
func (*MergeTypeError) Error ¶
func (e *MergeTypeError) Error() string
type ModeConfig ¶
type ModeConfig struct {
// Name is the display name for the mode (e.g. "smart", "rush", "deep").
Name string `mapstructure:"name"`
// Provider is the provider for this mode (e.g. "anthropic", "openrouter").
// Required when modes are explicitly declared.
Provider string `mapstructure:"provider"`
// Model is the model name for this mode (e.g. "claude-sonnet-4-5").
// Required when modes are explicitly declared.
Model string `mapstructure:"model"`
// Reasoning is the reasoning level for this mode ("off"/"low"/"medium"/"high").
// Empty means no reasoning.
Reasoning string `mapstructure:"reasoning"`
// Prompt is an optional system prompt appended to the base system prompt
// when this mode is active. Use "file:path" to read from a file (relative
// paths resolve against the config directory, ~/... is expanded).
Prompt string `mapstructure:"prompt"`
// Description is a short human-readable description shown in the mode picker.
Description string `mapstructure:"description"`
// Color is the hex accent color for bubbles rendered in this mode.
// Empty uses the theme default.
Color string `mapstructure:"color"`
}
ModeConfig defines a named agent mode. The app is always in a mode; each mode specifies a provider, model, and optional reasoning level, system prompt, and accent color. Modes are declared as [[modes]] array-of-tables in TOML. When no modes are configured, a built-in "General" mode is synthesized from defaults.
type ModelConfig ¶
type ModelConfig struct {
Provider string `mapstructure:"provider"`
Name string `mapstructure:"name"`
SmallProvider string `mapstructure:"small_provider"`
SmallModel string `mapstructure:"small_model"`
Options map[string]any `mapstructure:"options"`
// Reasoning is the session-default reasoning knob. Allowed
// values: "" / "off" (no reasoning), "low", "medium", "high".
// Invalid values are reset to "" with a warning at load time so
// a typo in a profile cannot block startup.
Reasoning string `mapstructure:"reasoning"`
// ReasoningBudget is an explicit Anthropic-style token budget
// for extended thinking. Zero means "derive from Reasoning"
// (the standard low/medium/high mapping). Negative values are
// reset to zero with a warning. Ignored by OpenAI-family
// adapters; only the discrete effort knob affects their wire
// format.
ReasoningBudget int `mapstructure:"reasoning_budget"`
}
ModelConfig holds model selection and provider-specific knobs.
type NotificationsConfig ¶
type NotificationsConfig struct {
// Enabled controls whether any desktop notifications are sent.
// When false, all notification types are suppressed regardless of
// the other fields. Default: true.
Enabled bool `mapstructure:"enabled"`
// PermissionAsk, when true, sends a notification when the agent
// requests permission to execute a tool. Useful when the user
// has switched to another window and wants to be alerted.
// Default: true.
PermissionAsk bool `mapstructure:"permission_ask"`
// TurnComplete, when true, sends a notification when the agent
// finishes a full turn. Off by default to avoid notification
// fatigue on short interactive sessions. Default: false.
TurnComplete bool `mapstructure:"turn_complete"`
}
NotificationsConfig controls desktop notification behaviour.
type OnboardingSubagent ¶ added in v0.7.0
type OnboardingSubagent struct {
Name string
Description string
Prompt string
Model string // optional "<provider>/<model>" ref; empty = inherit
}
OnboardingSubagent is a minimal sub-agent descriptor used during onboarding. It carries only what the wizard collects; the runtime loads the canonical subagent.Type after bootstrap.
type ParseError ¶
ParseError wraps a TOML parse failure with the originating file path.
func (*ParseError) Error ¶
func (e *ParseError) Error() string
func (*ParseError) Unwrap ¶
func (e *ParseError) Unwrap() error
type PermissionConfig ¶
type PermissionConfig struct {
FileReadOutsidePwd PermissionMode `mapstructure:"file_read_outside_pwd"`
FileWrite PermissionMode `mapstructure:"file_write"`
Shell PermissionMode `mapstructure:"shell"`
Network PermissionMode `mapstructure:"network"`
// MCP gates MCP tool invocations. Defaults to "ask". Servers
// may override per-server via mcp.toml's permission_category.
MCP PermissionMode `mapstructure:"mcp"`
// Subagent gates subagent dispatch. Defaults to "allow" because
// subagents inherit the session's tool-level permissions.
Subagent PermissionMode `mapstructure:"subagent"`
}
PermissionConfig controls which operations require user approval.
type PermissionMode ¶
type PermissionMode string
PermissionMode is the set of allowed values for permission fields.
const ( // PermAllow grants the operation without prompting. PermAllow PermissionMode = "allow" // PermAsk prompts the user before allowing the operation. PermAsk PermissionMode = "ask" // PermDeny rejects the operation without prompting. PermDeny PermissionMode = "deny" )
Permission mode constants.
type PluginsConfig ¶
type PluginsConfig struct {
// Sources is the list of plugin source URIs.
// Each entry must be a valid source URI (github: or local:).
Sources []string `mapstructure:"sources"`
}
PluginsConfig is the [plugins] section of config.toml.
type Provenance ¶
Provenance maps a dotted-path config key (e.g. "model.name") to the ordered list of Sources that contributed to it. For scalar leaves the last entry is the winning source. For maps/arrays all contributing sources are listed in merge order.
type SessionConfig ¶
type SessionConfig struct {
// ResumeDefault controls the bare `hygge` invocation's default
// behaviour when no --continue or --new flag is set:
//
// "new" — always start a fresh session (default).
// "continue" — resume the cwd's most recent session when one
// exists; fall back to new when none does.
// "ask" — open the resume picker on launch.
//
// The comparison is case-insensitive. Any other value warns and
// resets to "new".
ResumeDefault string `mapstructure:"resume_default"`
}
SessionConfig controls session-lifecycle behaviour.
type Source ¶
type Source struct {
// File is the absolute path to the source file.
// Special values: "<defaults>", "<env>", "<flag>".
File string
// Line is the 1-based line number within File, or 0 when unknown
// (e.g. for defaults, env vars, and CLI flags).
Line int
}
Source identifies where a config key value originated.
func Explain ¶
Explain returns a human-readable explanation of where key got its value, together with the underlying source chain.
key is a dotted path: "model.name", "permission.shell", etc. Returns a multi-line formatted string, the chain of Sources for the key, and any error (e.g. key not found in provenance).
Example output:
permission.shell = "ask"
set by:
1. <defaults> : "ask"
2. ~/.config/hygge/config.toml : "deny"
3. ~/.config/hygge/profiles/work.toml : "ask"
effective: "ask" (from ~/.config/hygge/profiles/work.toml)
type SubagentEntry ¶ added in v0.10.0
type SubagentEntry struct {
Description string `mapstructure:"description"`
Prompt string `mapstructure:"prompt"`
Tools []string `mapstructure:"tools"`
Model string `mapstructure:"model"`
}
SubagentEntry is the per-type configuration that can appear inside the [subagents.<name>] table in config.toml (or in a profile file). The schema is identical to the entries accepted by subagents.toml so both sources are interchangeable.
type ThemeConfig ¶
type ThemeConfig struct {
Name string `mapstructure:"name"`
}
ThemeConfig holds display-theme selection.
type UIConfig ¶
type UIConfig struct {
// NerdFonts controls whether nerd-font glyphs are used in the TUI.
// Disable if your terminal font does not include nerd-font glyphs; we'll
// render plain ASCII alternatives (e.g. ":main" instead of " main").
// Default: true.
NerdFonts bool `mapstructure:"nerd_fonts"`
}
UIConfig holds UI behaviour knobs.
type UnknownKeyError ¶
UnknownKeyError is returned when strict struct decoding encounters an unexpected key.
func (*UnknownKeyError) Error ¶
func (e *UnknownKeyError) Error() string
type WriteDefaultProfileOptions ¶
type WriteDefaultProfileOptions = WriteModelOptions
WriteDefaultProfileOptions controls the narrow config writer used by `hygge profile use`. It always writes the user config, not project config.
type WriteInitStyleOptions ¶ added in v0.10.0
type WriteInitStyleOptions = WriteModelOptions
WriteInitStyleOptions controls the writer used by `hygge init`.
type WriteModelOptions ¶
type WriteModelOptions struct {
HomeDir string
XDGConfigHome string
Pwd string
Provenance Provenance
}
WriteModelOptions controls the narrow config writer used by runtime model selection. It only writes model.provider and model.name.
type WriteOnboardingModeOptions ¶ added in v0.7.0
type WriteOnboardingModeOptions = WriteModelOptions
WriteOnboardingModeOptions controls the narrow config writer for the onboarding wizard's mode result. It shares the model writer's target- resolution inputs.
type WritePluginSourcesOptions ¶ added in v0.4.0
type WritePluginSourcesOptions = WriteModelOptions
WritePluginSourcesOptions controls the narrow config writer used by `hygge plugins install/remove`. It shares the model writer's target-resolution inputs.
type WriteProviderAPIKeyOptions ¶
type WriteProviderAPIKeyOptions = WriteModelOptions
WriteProviderAPIKeyOptions controls the narrow config writer used for provider credentials. It shares the model writer's target-resolution inputs.
type WriteSubagentsTomlOptions ¶ added in v0.7.0
WriteSubagentsTomlOptions configures WriteSubagentsToml. Only HomeDir and XDGConfigHome are used.
type WriteThemeSelectionOptions ¶
type WriteThemeSelectionOptions = WriteModelOptions
WriteThemeSelectionOptions controls the narrow config writer used by runtime theme selection. It shares the model writer's target-resolution inputs.