config

package
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package config loads, merges, and validates Hygge configuration from multiple TOML sources.

Merge order (lowest → highest precedence)

  1. Builtin defaults (defaults.go)
  2. User config: $XDG_CONFIG_HOME/hygge/config.toml
  3. Profile: $XDG_CONFIG_HOME/hygge/profiles/<name>.toml Resolved recursively via the "extends" key (max depth 8, cycle-detected).
  4. Walk-up .hygge/config.toml starting from opts.Pwd going up to $HOME. Files closer to Pwd have higher precedence.
  5. 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.
  6. 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

Constants

This section is empty.

Variables

View Source
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

func PluginSettings(raw map[string]any) map[string]map[string]any

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

func ResolvePrompt(raw string, baseDir string) string

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

func (c *Config) RawPluginSettings() map[string]map[string]any

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

type InvalidValueError struct {
	Key   string
	Value any
	Msg   string
}

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

type ParseError struct {
	File string
	Err  error
}

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

type Provenance map[string][]Source

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

func Explain(prov Provenance, cfg *Config, key string) (string, []Source, error)

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

type UnknownKeyError struct {
	Key  string
	File string
}

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

type WriteSubagentsTomlOptions struct {
	HomeDir       string
	XDGConfigHome string
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL