pub struct Config {Show 25 fields
pub providers: HashMap<String, ProviderConfig>,
pub models: HashMap<String, ModelConfig>,
pub pricing: HashMap<String, PriceConfig>,
pub agents: Vec<AgentInfo>,
pub default_agent: String,
pub default_model: Option<ModelConfig>,
pub command_configs: HashMap<String, CommandConfig>,
pub tools: ToolsConfig,
pub session: SessionConfig,
pub tui: TuiConfig,
pub general: GeneralConfig,
pub schema_version: u32,
pub mcp_servers: HashMap<String, McpServerConfig>,
pub lsp: LspConfig,
pub permission: HashMap<String, PermissionAction>,
pub commands: HashMap<String, CustomCommandConfig>,
pub security: SecurityConfig,
pub hooks: Vec<HookConfig>,
pub memory: MemoryConfig,
pub swarm: SwarmConfig,
pub automation: AutomationConfig,
pub notify: NotifyConfig,
pub slop: SlopConfig,
pub websearch: WebSearchConfig,
pub system_prompt_overlays: SystemPromptOverlays,
}Expand description
Main configuration structure
Fields§
§providers: HashMap<String, ProviderConfig>API provider configurations
models: HashMap<String, ModelConfig>Model configurations
pricing: HashMap<String, PriceConfig>USD per million tokens, keyed "provider/model". Overrides prices
learned from the provider’s /v1/models catalog.
agents: Vec<AgentInfo>Agent definitions
default_agent: StringDefault agent name
default_model: Option<ModelConfig>Default model
command_configs: HashMap<String, CommandConfig>Per-command configuration overrides
tools: ToolsConfigTools configuration
session: SessionConfigSession configuration
tui: TuiConfigTUI configuration
general: GeneralConfigGeneral settings
schema_version: u32On-disk schema version. Missing / 0 is migrated to
CONFIG_SCHEMA_VERSION on load.
mcp_servers: HashMap<String, McpServerConfig>MCP server configurations
lsp: LspConfigLanguage-server overlay. Empty servers keeps built-in auto-detect.
permission: HashMap<String, PermissionAction>Global tool permissions (allow/ask/deny).
Merged into each agent; agent-level permission.rules wins on conflict.
commands: HashMap<String, CustomCommandConfig>Custom slash commands keyed by name (from config.toml + markdown files)
security: SecurityConfigShell command risk gating.
hooks: Vec<HookConfig>Pre/post tool hooks (shell commands). Empty by default.
memory: MemoryConfigCross-session semantic / auto memory.
swarm: SwarmConfigParallel multi-agent swarm + file conflict notify.
automation: AutomationConfigBackground jobs and lightweight scheduling (FEATURES §11).
notify: NotifyConfigDiscord / Telegram session notifications (off by default).
slop: SlopConfigCode-slop tripwires (whycodes slop / /slop). Not a training objective.
websearch: WebSearchConfigwebsearch engine choice, fallback order, and the custom endpoint.
system_prompt_overlays: SystemPromptOverlaysExtra system-prompt text loaded from prompts/*.md (not stored in TOML).
Implementations§
Source§impl Config
impl Config
Sourcepub fn project_config_path(project_dir: &Path) -> PathBuf
pub fn project_config_path(project_dir: &Path) -> PathBuf
The project overlay: <project>/.whycodes/config.toml.
Sourcepub fn to_toml_value(&self) -> Result<Value>
pub fn to_toml_value(&self) -> Result<Value>
The whole config as a toml::Value tree, for dotted lookups.
Sourcepub fn set_dotted(
project_dir: &Path,
target: ConfigTarget,
key: &str,
raw: &str,
) -> Result<PathBuf>
pub fn set_dotted( project_dir: &Path, target: ConfigTarget, key: &str, raw: &str, ) -> Result<PathBuf>
Set the dotted key to raw in one config file and return its path.
raw is a TOML literal (bool, number, array, inline table); text
that is not one, or whose type does not fit the key, is tried as a
string. Only that key changes in the file: other keys, comments and
layout stay. Refused, with the file untouched, when the result does
not load as a config, adds a validation error, would not change the
effective value (unknown key, or a merge that keeps the lower layer),
or (project) sets a key the project layer may not set. The first
write to an existing file copies it to <file>.bak.
Source§impl Config
impl Config
Sourcepub fn load() -> Result<Self>
pub fn load() -> Result<Self>
Load config from the default location.
A missing file returns the OpenRouter seed without writing. Call
Self::load_or_create (or Self::load_layered) to persist it.
Tests and TUI dialogs that only peek at disk must not create
~/.whycodes/config.toml as a side effect.
Sourcepub fn load_or_create() -> Result<Self>
pub fn load_or_create() -> Result<Self>
Load, and on first run write the OpenRouter seed to config.toml.
Sourcepub fn load_global_raw() -> Result<Self>
pub fn load_global_raw() -> Result<Self>
The global config.toml alone, as written: no project layer, no
environment overlay, no ${VAR} expansion, nothing from CLI flags.
A missing file is the first-run seed.
Sourcepub fn update_global(edit: &mut dyn FnMut(&mut Config)) -> Result<()>
pub fn update_global(edit: &mut dyn FnMut(&mut Config)) -> Result<()>
Persist one change: apply edit to Self::load_global_raw and
save that. Callers keep their merged, in-memory config for the run
and pass the same edit here, so values from the project layer, the
environment or CLI flags never land in the global file.
Only the keys the edit changed are written: the file keeps its other keys, comments and order, and gains no defaults it did not have.
Sourcepub fn migrate_schema(&mut self) -> Result<bool>
pub fn migrate_schema(&mut self) -> Result<bool>
Rewrite an older config.toml in place so later loads see the current
schema. Missing schema_version is treated as 0.
Sourcepub fn default_path() -> Result<PathBuf>
pub fn default_path() -> Result<PathBuf>
Get default config path
Sourcepub fn load_layered(project_dir: &Path) -> Result<Self>
pub fn load_layered(project_dir: &Path) -> Result<Self>
Load config using priority-based layering:
- Built-in defaults
- Global config (
~/.whycodes/config.toml, or$WHYCODES_HOME/config.toml) - Project config (
<project>/.whycodes/config.toml) - Environment variables (WHYCODES_*)
Returns the merged configuration.
Sourcepub fn load_project_layer(project_dir: &Path) -> Option<Self>
pub fn load_project_layer(project_dir: &Path) -> Option<Self>
Layer 3 of Self::load_layered on its own: <project>/.whycodes/config.toml
parsed but not merged. None when the file is absent, unreadable or
malformed (the last two are logged, as the layered load does).
Sourcepub fn set_openrouter_api_key(&mut self, key: &str) -> Result<()>
pub fn set_openrouter_api_key(&mut self, key: &str) -> Result<()>
Set an OpenRouter API key here and in the global config.toml.
Sourcepub fn mark_openrouter_key_prompt_skipped(&mut self) -> Result<()>
pub fn mark_openrouter_key_prompt_skipped(&mut self) -> Result<()>
Remember that the first-launch OpenRouter key modal was skipped.
Sourcepub fn seeded_openrouter() -> Self
pub fn seeded_openrouter() -> Self
First-run config.toml: OpenRouter provider, no API key yet.
Sourcepub fn apply_slop_file(&mut self, project_dir: &Path)
pub fn apply_slop_file(&mut self, project_dir: &Path)
Overlay .whycodes/slop.toml when present (project-local tripwires).
Sourcepub fn apply_env_overrides(&mut self)
pub fn apply_env_overrides(&mut self)
Apply environment variable overrides to this config in-place.
Sourcepub fn load_command_files(&mut self, project_dir: &Path)
pub fn load_command_files(&mut self, project_dir: &Path)
Load custom commands from markdown files:
- global:
~/.config/.../commands/*.md - project:
<project>/.whycodes/commands/*.md
Sourcepub fn load_prompt_files(&mut self, project_dir: &Path)
pub fn load_prompt_files(&mut self, project_dir: &Path)
Load provider/model system-prompt extras from markdown:
- global:
~/.config/.../prompts/ - project:
<project>/.whycodes/prompts/(wins on the same key)
Sourcepub fn ensure_builtin_prompt_commands(&mut self)
pub fn ensure_builtin_prompt_commands(&mut self)
PromptCommands: fixed prompts that kick a turn.
Does not overwrite user-defined commands.* or commands/*.md.
Source§impl Config
impl Config
Sourcepub fn get_command_config(&self, command: &str) -> Option<&CommandConfig>
pub fn get_command_config(&self, command: &str) -> Option<&CommandConfig>
Get per-command configuration overrides for the given command name.
Sourcepub fn expand_notify_secrets(&mut self)
pub fn expand_notify_secrets(&mut self)
Expand ${VAR} / $VAR in Discord / Telegram secret fields.
Call after layered load so config.toml can store
discord_webhook = "${DISCORD_WEBHOOK_URL}" without committing the URL.
Sourcepub fn substitute_vars(value: &str) -> String
pub fn substitute_vars(value: &str) -> String
Replace ${VAR_NAME} and $VAR_NAME patterns with environment
variable values. Unknown variables are left as-is.
Sourcepub fn project_path(&self) -> PathBuf
pub fn project_path(&self) -> PathBuf
Get the configured project path or current directory
Sourcepub fn get_provider(&self, name: &str) -> Option<&ProviderConfig>
pub fn get_provider(&self, name: &str) -> Option<&ProviderConfig>
Get provider by name
Sourcepub fn configured_context_window(
&self,
provider: &str,
model: &str,
) -> Option<u32>
pub fn configured_context_window( &self, provider: &str, model: &str, ) -> Option<u32>
Explicit context_window from config for this provider/model, if any.
Does not consult the built-in catalog — call
whycodes_llm::resolve_context_window for the full chain.
Sourcepub fn default_agent(&self) -> Option<&AgentInfo>
pub fn default_agent(&self) -> Option<&AgentInfo>
Get the default agent
Source§impl Config
impl Config
Sourcepub fn merge_with(&self, other: &Config) -> Config
pub fn merge_with(&self, other: &Config) -> Config
Deep-merge other into self, producing a new Config.
Fields in other that are Some(_) or non-empty collections
override corresponding fields in self. This implements the
layering semantics where higher-priority layers win.
Sourcepub fn effective_permission(&self, agent: &PermissionSet) -> PermissionSet
pub fn effective_permission(&self, agent: &PermissionSet) -> PermissionSet
Merge global permission map into an agent’s PermissionSet (agent rules win).
Source§impl Config
impl Config
Sourcepub fn load_stream_rule_files(&mut self, project_dir: &Path)
pub fn load_stream_rule_files(&mut self, project_dir: &Path)
Merge .whycodes/rules/*.md stream rules into session.stream_rules.
A file rule replaces a config-table rule of the same name.
Source§impl Config
impl Config
Sourcepub fn validate(&self) -> Result<()>
pub fn validate(&self) -> Result<()>
Validate the configuration and return any issues.
Checks required fields and emits warnings for common misconfigurations.
Sourcepub fn validation_errors(&self) -> Vec<String>
pub fn validation_errors(&self) -> Vec<String>
The issues Self::validate fails on (localhost base_url notes
are only warnings), without logging them.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Config
impl<'de> Deserialize<'de> for Config
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for Config
impl RefUnwindSafe for Config
impl Send for Config
impl Sync for Config
impl Unpin for Config
impl UnsafeUnpin for Config
impl UnwindSafe for Config
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more