state

package
v0.17.4 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package state manages persisted runtime data for a hygge installation.

What lives here

Runtime mutable data that is machine-written (not human-edited): the active profile name, last-used model, recent session identifiers, and per-file trust decisions. This is distinct from config-as-source (TOML files in $XDG_CONFIG_HOME/hygge/), which belongs in package config.

Storage format

JSON, stored at $XDG_STATE_HOME/hygge/state.json (fallback: $HOME/.local/state/hygge/state.json). The directory is created with mode 0o700 on first write; the file itself is written with mode 0o600.

Atomic writes

Save writes to a sibling .tmp file first, syncs, closes, then renames to the real path. Rename is atomic on POSIX filesystems, so readers always see either the previous complete file or the new complete file — never a partial write.

Single-instance limitation

v0.1 provides no daemon or advisory-lock coordination. Each call to Load and Save does a discrete file operation; concurrent processes on the same state file may overwrite each other's changes if they read, modify, and save the state in overlapping windows. This is acceptable for v0.1 where hygge is typically run as a single process. A future version may add advisory locking or a version/sequence field to detect and reject stale writes.

Index

Constants

View Source
const MaxRecentSessions = 20

MaxRecentSessions is the maximum number of session IDs retained in State.RecentSessions. Older entries are dropped when the cap is reached.

Variables

View Source
var ErrCorruptState = errors.New("state file is corrupt or from a newer version")

ErrCorruptState is returned when the state file exists but cannot be decoded as valid JSON conforming to the known schema. This covers empty files, malformed JSON, and files that contain unknown top-level fields (which indicate a state file written by a newer version of hygge).

Functions

func AddAllowRule

func AddAllowRule(rule AllowRule, opts LoadOptions) error

AddAllowRule appends rule to State.AllowedRules and persists. If a rule with the same Category and Pattern already exists, it is left in place and the timestamp is not refreshed (idempotent). The CreatedAt field is set from time.Now if zero.

func AddProjectAllowRule added in v0.12.0

func AddProjectAllowRule(rule AllowRule, projectDir string) error

AddProjectAllowRule appends rule to the project-scoped allow rules at <projectDir>/.hygge/permissions.json, persisting atomically. If a rule with the same Category and Pattern already exists, it is left in place and the call is a no-op. The CreatedAt field is set from time.Now if zero.

func AddRecentSession

func AddRecentSession(id string, opts LoadOptions) error

AddRecentSession prepends id to State.RecentSessions. If id is already present in the list it is removed from its prior position before being prepended (most-recent reference wins). The list is capped at MaxRecentSessions; entries beyond the cap are dropped from the tail.

func GitBranch

func GitBranch(projectPath string) string

GitBranch returns the current branch (or detached-HEAD short SHA) for the repository rooted at or above projectPath. The result is cached per projectPath for the lifetime of the process.

Detection strategy (no git subprocess):

  1. Walk up from projectPath to the filesystem root looking for a .git directory.
  2. Read .git/HEAD. - If the content begins with "ref: refs/heads/", strip that prefix and return the branch name. - Otherwise assume a detached HEAD: return "@" + first 7 hex characters of the SHA (e.g. "@a1b2c3d").
  3. If no .git directory is found, return "".

func InvalidateBranchCache added in v0.12.0

func InvalidateBranchCache(projectPath string)

InvalidateBranchCache removes the cached branch name for projectPath so the next call to GitBranch re-reads .git/HEAD from disk. Call this when the branch is expected to have changed (e.g. when a new user message is sent or a new session starts) to ensure the sidebar reflects the current branch without paying a filesystem read on every render tick.

func IsConfigTrusted

func IsConfigTrusted(absPath string, expectedSha256 string, opts LoadOptions) (bool, error)

IsConfigTrusted returns true iff a sha256 digest has been recorded for absPath and it matches expectedSha256. Missing entries and digest mismatches both return false.

func Path

func Path(opts LoadOptions) (string, error)

Path returns the resolved path to state.json for the given options. The file need not exist.

func ProjectPermissionsPath added in v0.12.0

func ProjectPermissionsPath(projectDir string) (string, error)

ProjectPermissionsPath returns the path to the project-scoped permissions file for the given project directory. The file need not exist. Returns an error only when projectDir is empty.

func RemoveAllowRule

func RemoveAllowRule(category, pattern string, opts LoadOptions) error

RemoveAllowRule removes any rules in State.AllowedRules whose Category and Pattern match the supplied values. Idempotent: removing a missing rule returns nil.

func Save

func Save(s *State, opts LoadOptions) error

Save writes s to disk atomically. The target directory is created with mode 0o700 if it does not exist. The state file is written with mode 0o600.

func SetActiveProfile

func SetActiveProfile(name string, opts LoadOptions) error

SetActiveProfile sets the active profile name and persists the change.

func SetLastUsedModel

func SetLastUsedModel(ref ModelRef, opts LoadOptions) error

SetLastUsedModel records the most recently used provider/model and persists the change.

func ToggleFavoriteModel added in v0.15.0

func ToggleFavoriteModel(ref string, opts LoadOptions) error

ToggleFavoriteModel adds ref to State.FavoriteModels if it is not already present, or removes it if it is. The list preserves insertion order; new favorites are appended. ref should be in "provider/model" form.

func TrustConfig

func TrustConfig(absPath string, sha256hex string, opts LoadOptions) error

TrustConfig records that the file at absPath has been trusted at the given sha256hex digest. On subsequent loads, callers should compare the stored digest to the live file's digest; a mismatch means trust has expired.

Types

type AllowRule

type AllowRule struct {
	// Category is the permission category: "file.read", "file.write",
	// "shell", or "network".
	Category string `json:"category"`

	// Pattern is the doublestar glob the rule applies to.  Typically this is
	// the exact Target from the request that prompted the user, but
	// mutators accept any valid glob.
	Pattern string `json:"pattern"`

	// CreatedAt is the unix-millisecond timestamp when the rule was
	// recorded.
	CreatedAt int64 `json:"created_at"`
}

AllowRule is a persisted "always-allow" decision made by the user through a permission prompt. It is matched against incoming permission requests by the permission engine. Patterns are doublestar globs (the literal Target from the original request is a valid pattern, since it matches itself).

func LoadProjectAllowRules added in v0.12.0

func LoadProjectAllowRules(projectDir string) ([]AllowRule, error)

LoadProjectAllowRules reads the project-scoped allow rules from <projectDir>/.hygge/permissions.json. If the file does not exist, an empty slice is returned with a nil error. Corrupt files return ErrCorruptState.

type LoadOptions

type LoadOptions struct {
	// HomeDir overrides $HOME for XDG fallback computation.  Empty = real HOME.
	HomeDir string

	// XDGStateHome overrides $XDG_STATE_HOME.  Empty = real env or fallback.
	XDGStateHome string
}

LoadOptions controls how Load, Save, and Path resolve the state file path. The zero value uses real environment variables and the real home directory.

type ModelRef

type ModelRef struct {
	Provider string `json:"provider"`
	Name     string `json:"name"`
}

ModelRef identifies a provider and model name.

type State

type State struct {
	ActiveProfile  string            `json:"active_profile,omitempty"`
	LastUsedModel  *ModelRef         `json:"last_used_model,omitempty"`
	RecentSessions []string          `json:"recent_sessions,omitempty"`
	TrustedConfigs map[string]string `json:"trusted_configs,omitempty"` // absolute path -> sha256 hex
	AllowedRules   []AllowRule       `json:"allowed_rules,omitempty"`
	// FavoriteModels is an ordered list of "provider/model" strings the user
	// has marked as favorites in the model picker.  Stored globally so the
	// list is shared across all projects and survives restarts.
	FavoriteModels []string `json:"favorite_models,omitempty"`
}

State is the persisted runtime data for a hygge installation.

All fields are optional (omitempty); a zero-value State is valid and represents a clean first-run installation.

func Load

func Load(opts LoadOptions) (*State, error)

Load reads state from disk. If the file does not exist, Load returns a zero-valued State and a nil error — missing state on first run is not an error. If the file exists but cannot be decoded, Load returns ErrCorruptState wrapping the underlying error.

type TouchedFiles

type TouchedFiles struct {
	// contains filtered or unexported fields
}

TouchedFiles tracks absolute paths of files modified during the session. Paths are deduplicated; the set is safe for concurrent access.

func NewTouchedFiles

func NewTouchedFiles() *TouchedFiles

NewTouchedFiles returns an empty, ready-to-use TouchedFiles.

func (*TouchedFiles) Add

func (t *TouchedFiles) Add(absPath, projectDir string)

Add registers absPath as touched. If absPath is relative it is resolved against projectDir before storing. Empty strings, bare ".", and paths that resolve to the project directory itself are silently ignored.

func (*TouchedFiles) Len

func (t *TouchedFiles) Len() int

Len returns the number of tracked paths.

func (*TouchedFiles) List

func (t *TouchedFiles) List() []string

List returns all tracked paths, sorted lexicographically. The returned slice is a copy; callers may modify it freely.

Jump to

Keyboard shortcuts

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