Documentation
¶
Overview ¶
Package settings loads and validates user configuration for jungi.
Layers ¶
Configuration is composed from two layers, applied in order:
- User-level settings: ~/.config/jungi/settings.toml — loaded once at startup and shared across all sessions in the process.
- Project-level settings: <repo>/.jungi/settings.toml — loaded fresh for every new, cleared, or compacted session and merged on top of the user settings so repo-specific overrides take effect automatically.
The merge is deep: scalar and pointer fields in the project file override only when set; the [hooks] and [lsp.servers] maps are merged key-by-key so that changing one entry does not wipe sibling entries. See Merge and LoadProject for the merge semantics.
Loading ¶
Both files are optional. If a file does not exist, Load or LoadProject returns defaults (for user settings) or a zero value (for project settings) with no error — a missing file is not an error condition. If a file exists but cannot be parsed, an error is returned so the caller can surface the problem (user settings) or log a warning and proceed without project overrides (project settings).
Unrecognised keys in either file are ignored, and any field that is absent takes its default value, so partial files are fully supported.
Example settings.toml:
# Provider-prefixed model used for the main chat session. model = "anthropic__claude-sonnet-5" # Optional: default extended-thinking level for the main chat session. # Also acts as the fallback for [plan] and [execute] thinking_level when # those are omitted. Valid values: low, medium, high, xhigh, max. # Omit (or leave empty) to disable thinking entirely. Anthropic models # only; OpenAI models use reasoning_mode/reasoning_level instead. thinking_level = "high" # Optional: for OpenAI models, the reasoning mode and effort level to # use instead of thinking_level. reasoning_mode selects among the # model's supported modes (e.g. "standard", "pro"); reasoning_level # selects the effort (e.g. "low", "medium", "high", "max"). If # reasoning_level is set without reasoning_mode, reasoning_mode falls # back to "standard". Meaningless for Anthropic models. # reasoning_mode = "standard" # reasoning_level = "high" # Optional: per-language LSP server overrides. The harness ships # a multi-language registry (gopls enabled by default, others # disabled). A [lsp.servers.<name>] table overrides individual # fields without re-specifying the whole entry. # # The `command` value supports shell-style expansion at startup: # $(cmd) command substitution via /bin/sh # $VAR env var expansion # ~/... home directory expansion # So e.g. command = "$(asdf which gopls)" is fine for asdf users. # # All built-in servers are disabled by default. Enable the ones # you need: [lsp.servers.gopls] enabled = true warmup = true [lsp.servers.rust-analyzer] enabled = true # Built-in hooks are disabled by default. Opt in by naming them under # [hooks.<name>] with enabled = true: [hooks.notify] enabled = true [hooks.github-pr] enabled = true draft = true # Optional: per-command model and thinking-level defaults applied when # the /plan, /execute, and /review slash commands are invoked. An omitted # `model` falls back to the top-level `model` above; an omitted # `thinking_level` disables extended thinking for that command. [plan] model = "anthropic__claude-opus-4-8" thinking_level = "high" # Optional: model override for the research subagent spawned by /plan. # Falls back to Haiku 4.5 when omitted. [plan.research_subagent] model = "anthropic__claude-sonnet-4-6" [execute] model = "anthropic__claude-sonnet-4-6" thinking_level = "medium" # OpenAI example: use reasoning_mode/reasoning_level instead of # thinking_level when the command's model is an OpenAI model. # [execute] # model = "openai__gpt-5.6-sol" # reasoning_mode = "pro" # reasoning_level = "high" [review] model = "anthropic__claude-sonnet-4-6" thinking_level = "medium" # Optional: model override for the reviewer subagent spawned by /review # and review_ticket. Falls back to Haiku 4.5 when omitted. [review.reviewer_subagent] model = "anthropic__claude-sonnet-4-6" # Optional: connect to the jungi control remote messenger service so # inbound channel messages are injected as user turns and every user # and assistant turn is mirrored back over the same connection. # Disabled by default. [jungi_control] enabled = true url = "https://control.example.com" # Optional: enable the AI auto-approver for run_unsafe_shell commands, # which delegates the approve/deny decision to a model instead of # showing the manual confirmation overlay. Disabled by default. An # omitted model falls back to the auto-approver's built-in default # (Haiku 4.5). [auto_approve] enabled = true model = "anthropic__claude-haiku-4-5-20251001"
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Validate ¶
Validate checks that s carries only valid, provider-consistent values. It wraps the same validation Load applies when parsing a settings file, so callers that build or merge a Settings value programmatically (e.g. the session manager merging project-level overrides on top of user-level settings) can enforce the identical rules.
Types ¶
type AutoApproveSettings ¶
type AutoApproveSettings struct {
// Enabled controls whether the AI auto-approver is active for
// run_unsafe_shell commands. Defaults to false (nil = not set =
// disabled). Users must explicitly opt in with enabled = true.
Enabled *bool `toml:"enabled"`
// Model is the Anthropic model identifier used to evaluate
// auto-approval decisions. Empty falls back to the auto-approver's
// built-in default model (Haiku 4.5).
Model string `toml:"model"`
}
AutoApproveSettings is the [auto_approve] table in settings.toml.
type CommandDefaults ¶
type CommandDefaults struct {
// Model is the model identifier to use for the command's session.
// Empty means the section inherits the top-level Settings.Model (and
// its thinking/reasoning).
Model string `toml:"model"`
// ThinkingLevel is the extended-thinking effort level (e.g. "low",
// "medium", "high", "xhigh", "max"). Only meaningful alongside a Model
// in this section; empty (with a Model set) means thinking is disabled.
// Anthropic models only.
ThinkingLevel string `toml:"thinking_level"`
// ReasoningMode is the OpenAI reasoning mode (e.g. "standard", "pro").
// Only meaningful alongside a Model in this section. A mode resolved
// without a level defaults the level to "medium". Meaningless for
// Anthropic models.
ReasoningMode string `toml:"reasoning_mode"`
// ReasoningLevel is the OpenAI reasoning effort level (e.g. "low",
// "medium", "high", "max"). Only meaningful alongside a Model in this
// section; empty (with a Model set) means reasoning is disabled unless
// a reasoning mode is set (which defaults the level to "medium").
// Meaningless for Anthropic models.
ReasoningLevel string `toml:"reasoning_level"`
}
CommandDefaults holds the optional model and reasoning defaults for a slash command (/plan, /execute, /review). All fields are plain strings; an empty value means "unset". Inheritance is model-gated: when Model is unset the whole section is treated as absent and the model plus thinking/reasoning are inherited from the top-level Settings; when Model is set the section is authoritative and its own fields apply with no top-level fallback (an unset level/mode then means thinking/reasoning is disabled for that command). Because a section that omits its model cannot express reasoning, settings validation rejects any thinking/reasoning field set without a Model here.
type HookEntry ¶
type HookEntry struct {
// Enabled controls whether this hook fires. Defaults to false (nil =
// not set = disabled). Users must explicitly opt in with enabled = true.
Enabled *bool `toml:"enabled"`
// Events is the list of lifecycle events this hook should listen to.
// Used by the notify hook to restrict which events trigger a
// notification. An empty slice means all events the hook supports are
// active.
Events []string `toml:"events"`
// Draft, when non-nil and true, causes the github-pr hook to create
// draft pull requests instead of ready-for-review ones.
Draft *bool `toml:"draft"`
}
HookEntry holds the per-hook configuration for a single built-in hook.
type HooksSettings ¶
HooksSettings is the [hooks] table in settings.toml. Each map key is a built-in hook name; the value carries its configuration. A key that is absent means the hook is disabled.
type JungiControlSettings ¶
type JungiControlSettings struct {
// Enabled controls whether jungi connects to the jungi control service.
// Defaults to false (nil = not set = disabled). Users must explicitly
// opt in with enabled = true.
Enabled *bool `toml:"enabled"`
// URL is the base URL of the jungi control service, e.g.
// "https://control.example.com".
URL string `toml:"url"`
}
JungiControlSettings is the [jungi_control] table in settings.toml. It configures the connection to the jungi control remote messenger service.
func (JungiControlSettings) IsEnabled ¶
func (j JungiControlSettings) IsEnabled() bool
IsEnabled reports whether the jungi control connection is opted in. A nil Enabled pointer defaults to disabled.
type LSPServerSettings ¶
type LSPServerSettings struct {
Command string `toml:"command"`
Args []string `toml:"args"`
FileTypes []string `toml:"file_types"`
RootMarkers []string `toml:"root_markers"`
// Warmup, when true, asks the manager to spawn the server at session
// open rather than on first request. Defaults to false.
Warmup *bool `toml:"warmup"`
// Enabled controls whether this server is active. A nil pointer means
// "not specified" — the registry default applies. Users can explicitly
// enable a disabled-by-default server with `enabled = true` or disable
// a default server with `enabled = false`.
Enabled *bool `toml:"enabled"`
}
LSPServerSettings is the user-overridable shape of a single LSP server registry entry. All fields are optional; pointer-valued fields distinguish "explicitly cleared" from "not specified" where it matters.
type LSPSettings ¶
type LSPSettings struct {
// Enabled controls whether LSP support is active. A nil pointer means
// "not set" which defaults to enabled; users explicitly opt out with
// `enabled = false`.
Enabled *bool `toml:"enabled"`
// Servers maps a logical server name (e.g. "gopls") to its
// configuration. Entries here are merged on top of the built-in
// default registry: any field the user sets wins, anything they
// omit falls back to the default.
Servers map[string]LSPServerSettings `toml:"servers"`
}
LSPSettings groups all LSP-related user configuration.
func (LSPSettings) IsEnabled ¶
func (l LSPSettings) IsEnabled() bool
IsEnabled reports whether LSP support should be turned on. Missing or explicitly true → enabled; explicitly false → disabled.
type ModelOverride ¶
type ModelOverride struct {
// Model is the Anthropic model identifier to use for the subagent.
// Empty falls back to the subagent's built-in default model.
Model string `toml:"model"`
}
ModelOverride holds an optional model identifier override for a subagent. Unlike CommandDefaults, it carries no thinking_level: subagents do not support configurable extended thinking. An empty Model means "fall back to the subagent's built-in default model".
type PlanDefaults ¶
type PlanDefaults struct {
CommandDefaults
// ResearchSubagent overrides the model used for the research subagent.
// Falls back to the subagent's built-in default (Haiku 4.5) when unset.
ResearchSubagent ModelOverride `toml:"research_subagent"`
}
PlanDefaults holds the per-command model defaults for /plan plus the nested [plan.research_subagent] model override for the research subagent spawned by /plan.
type ReviewDefaults ¶
type ReviewDefaults struct {
CommandDefaults
// ReviewerSubagent overrides the model used for the reviewer subagent.
// Falls back to the subagent's built-in default (Haiku 4.5) when unset.
ReviewerSubagent ModelOverride `toml:"reviewer_subagent"`
}
ReviewDefaults holds the per-command model defaults for /review plus the nested [review.reviewer_subagent] model override for the reviewer subagent spawned by /review and review_ticket.
type Settings ¶
type Settings struct {
// Model is the Anthropic model identifier used for the main chat session.
// Defaults to anthropic__claude-sonnet-5 if absent or empty.
Model string `toml:"model"`
// ThinkingLevel is the extended-thinking effort level applied to the
// main chat session at startup (e.g. "low", "medium", "high", "xhigh",
// "max"). An absent or empty value means thinking is disabled. It is
// also inherited by [plan], [execute], and [review] when those sections
// omit their own model (a section that sets its own model is
// authoritative and does not inherit). Anthropic models only; OpenAI
// models use ReasoningMode/ReasoningLevel instead.
ThinkingLevel string `toml:"thinking_level"`
// ReasoningMode is the OpenAI reasoning mode applied to the main chat
// session at startup (e.g. "standard", "pro"). Meaningless for
// Anthropic models. It is also inherited by [plan], [execute], and
// [review] when those sections omit their own model. When a reasoning
// level is resolved without a mode the mode defaults to "standard";
// conversely a mode resolved without a level defaults the level to
// "medium" (a mode alone still enables reasoning).
ReasoningMode string `toml:"reasoning_mode"`
// ReasoningLevel is the OpenAI reasoning effort level applied to the
// main chat session at startup (e.g. "low", "medium", "high", "max").
// An absent or empty value means reasoning is disabled unless a
// reasoning mode is set (which defaults the level to "medium").
// Meaningless for Anthropic models. It is also inherited by [plan],
// [execute], and [review] when those sections omit their own model.
ReasoningLevel string `toml:"reasoning_level"`
// LSP holds language server configuration. Omitting the [lsp] table
// leaves Enabled=true and applies built-in defaults; setting
// `enabled = false` disables LSP entirely.
LSP LSPSettings `toml:"lsp"`
// AutoApprove holds the AI auto-approver configuration for
// run_unsafe_shell commands. The auto-approver delegates the
// approve/deny decision to a model instead of showing the manual
// confirmation overlay. Defaults to disabled.
//
// Breaking change: this was formerly a bare boolean (`auto_approve =
// true`); it is now a table. Existing configs must migrate to:
//
// [auto_approve]
// enabled = true
AutoApprove AutoApproveSettings `toml:"auto_approve"`
// SkipWorktree, when non-nil and true, causes jungi to operate directly
// on the source directory without creating a git worktree branch. The
// repo root is still detected for the plan store, but no worktree is
// created. Useful for new projects with no commits or when working
// directly on an existing branch. Defaults to false (nil pointer).
SkipWorktree *bool `toml:"skip_worktree"`
// Hooks holds the built-in hook opt-in table. Each key is a built-in
// hook name (e.g. "notify", "github-pr"); its value carries enable/disable
// and per-hook options. Built-in hooks are disabled by default and only
// become active when their entry carries enabled = true.
Hooks HooksSettings `toml:"hooks"`
// Plan holds the per-command model defaults applied when the /plan
// slash command is invoked. If it omits its model, the model and
// thinking/reasoning are inherited from the top-level; if it sets its
// own model, only its own fields apply (an omitted level/mode disables
// thinking/reasoning).
Plan PlanDefaults `toml:"plan"`
// Execute holds the per-command model defaults applied when the
// /execute slash command is invoked. If it omits its model, the model
// and thinking/reasoning are inherited from the top-level; if it sets
// its own model, only its own fields apply (an omitted level/mode
// disables thinking/reasoning).
Execute CommandDefaults `toml:"execute"`
// Review holds the per-command model defaults applied when the
// /review slash command is invoked. If it omits its model, the model
// and thinking/reasoning are inherited from the top-level; if it sets
// its own model, only its own fields apply (an omitted level/mode
// disables thinking/reasoning).
Review ReviewDefaults `toml:"review"`
// JungiControl holds the jungi control remote messenger service
// configuration. Disabled by default; opt in with enabled = true and a
// url.
JungiControl JungiControlSettings `toml:"jungi_control"`
}
Settings holds all user-configurable options for jungi.
Fields must remain backward compatible: adding a new field with a sensible zero-value default is always safe; removing or renaming a field is a breaking change for existing settings files.
func Load ¶
Load reads the TOML settings file at path and returns the resulting Settings.
If the file does not exist, Load returns the defaults with no error — a missing settings file is not an error condition. If the file exists but cannot be parsed, an error is returned so the caller can surface the problem to the user before proceeding with potentially wrong configuration.
Fields absent from the file retain their default values, so partial files are fully supported.
func LoadProject ¶
LoadProject reads the project-level TOML settings file at path and returns the raw Settings found there, without applying any defaults.
The function is lenient by design: if the file is absent a zero Settings{} is returned with no error (a missing file is normal — most repos will not have one). If the file exists but cannot be parsed, the error is returned so the caller can log a warning; in that case no project overrides are applied and the user-level settings remain in effect unchanged.
Defaults are deliberately not applied: the returned value is intended to be passed to Merge as the override argument, so only fields explicitly set in the file should carry values.
func Merge ¶
Merge returns a new Settings that combines base and override using a deep merge strategy: scalar and pointer fields in override take effect only when they carry a non-zero value, and the Hooks and LSP.Servers maps are merged key-by-key so that adding or changing one hook or server entry does not silently wipe sibling entries from the base.
Merge is the mechanism by which project-level settings (<repo>/.jungi/settings.toml) are layered on top of user-level settings (~/.config/jungi/settings.toml). The base is the fully-defaulted user settings; the override is the raw project file parsed without defaults (see LoadProject). The result carries default-filled fields for anything the project file does not specify.
func (Settings) ActiveModel ¶
ActiveModel returns the settings Model as a model.ID value, ready for use with the API client and model registry.
func (Settings) IsAutoApprove ¶
IsAutoApprove reports whether the AI auto-approver for run_unsafe_shell is enabled. A nil pointer means "not set" which defaults to disabled.
Source Files
¶
- settings.go