Skip to content

Configuration File

Every AI coding agent stores user preferences in a configuration file. The format varies — YAML, TOML, JSONC — but the core questions are the same: what fields are valid, how are they validated, how do nested structures (providers, agents, MCP servers) compose, and what happens when a field is unrecognized? This page catalogs the full schema of each reference implementation’s config file. For how multiple config files merge and override each other, see Configuration Overrides.

For runtime behavior of skills config entries (discovery, loading, and prompt injection), see Skills.

Reference: references/aider/ at commit b9050e1d

Aider uses YAML via configargparse.YAMLConfigFileParser. The canonical filename is .aider.conf.yml. There is no JSONC or TOML alternative. The parser is initialized in aider/args.py:35-42:

parser = configargparse.ArgumentParser(
description="aider is AI pair programming in your terminal",
add_config_file_help=True,
default_config_files=default_config_files,
config_file_parser_class=configargparse.YAMLConfigFileParser,
auto_env_var_prefix="AIDER_",
)

All config keys use kebab-case in YAML, matching CLI flag names exactly. The auto_env_var_prefix="AIDER_" setting means every key automatically maps to a SCREAMING_SNAKE_CASE environment variable: auto-commitsAIDER_AUTO_COMMITS, dark-modeAIDER_DARK_MODE. Aider writes zero custom env-var mapping code — configargparse handles all of it.

Aider has no separate schema file. The argument parser in aider/args.py (lines 35–862) serves as both the CLI definition and the config schema. Every add_argument() call creates a valid config key. If a YAML key doesn’t match any argument, configargparse silently ignores it — there is no strict validation or error on unknown keys.

A reference sample file is maintained at aider/website/assets/sample.aider.conf.yml for documentation purposes, but it’s not used for validation.

The fields below are organized by the argument groups defined in aider/args.py:

Model Selection:

  • model (string) — Primary LLM model name
  • weak-model (string) — Model for commit messages and summarization
  • editor-model (string) — Model for the /editor command
  • editor-edit-format (string) — Edit format for the editor model
  • edit-format (string) — Primary edit format (diff, udiff, whole, diff-fenced, architect, etc.)
  • architect (boolean, default: false) — Shorthand for --edit-format architect
  • auto-accept-architect (boolean, default: true) — Auto-accept architect suggestions

API Configuration:

  • openai-api-key (string) — OpenAI API key
  • anthropic-api-key (string) — Anthropic API key
  • openai-api-base (string) — Custom API base URL
  • api-key (list of provider=key) — Generic key assignment
  • set-env (list of KEY=value) — Set arbitrary environment variables
  • verify-ssl (boolean, default: true) — TLS verification
  • timeout (float) — API call timeout in seconds

Reasoning:

  • reasoning-effort (string) — OpenAI reasoning effort level (e.g., “high”)
  • thinking-tokens (string) — Budget for thinking models (e.g., “8k”, “0.5M”, or integer)

Context & Repo Map:

  • map-tokens (integer) — Token budget for repo map (0 disables)
  • map-refresh (string, default: “auto”) — When to refresh: auto, always, files, manual
  • map-multiplier-no-files (float, default: 2.0) — Map budget multiplier when no files in chat
  • max-chat-history-tokens (integer) — Soft limit on history tokens before summarization

Cache:

  • cache-prompts (boolean, default: false) — Enable prompt caching
  • cache-keepalive-pings (integer, default: 0) — Background cache warming pings (0 disables)

History & Persistence:

  • input-history-file (path, default: .aider.input.history) — Readline history
  • chat-history-file (path, default: .aider.chat.history.md) — Markdown chat log
  • restore-chat-history (boolean, default: false) — Resume from chat history on startup
  • llm-history-file (path) — Raw LLM conversation log

Output & Display:

  • dark-mode (boolean) — Dark terminal preset
  • light-mode (boolean) — Light terminal preset
  • pretty (boolean, default: true) — Rich terminal output
  • stream (boolean, default: true) — Stream LLM responses
  • user-input-color (hex, default: #00cc00)
  • tool-output-color (hex)
  • tool-error-color (hex, default: #FF2222)
  • tool-warning-color (hex, default: #FFA500)
  • assistant-output-color (hex, default: #0088ff)
  • completion-menu-color, completion-menu-bg-color, completion-menu-current-color, completion-menu-current-bg-color (hex)
  • code-theme (string, default: “default”) — Pygments syntax theme
  • show-diffs (boolean, default: false)

Git:

  • git (boolean, default: true) — Enable git integration
  • gitignore (boolean, default: true) — Respect .gitignore
  • aiderignore (path, default: .aiderignore) — Custom ignore patterns
  • subtree-only (boolean, default: false) — Restrict to current subtree
  • auto-commits (boolean, default: true) — Commit after each edit
  • dirty-commits (boolean, default: true) — Pre-commit dirty files
  • attribute-author (boolean, default: true)
  • attribute-committer (boolean, default: true)
  • attribute-co-authored-by (boolean, default: true)
  • attribute-commit-message-author (boolean, default: false)
  • attribute-commit-message-committer (boolean, default: false)
  • git-commit-verify (boolean, default: false) — Run pre-commit hooks
  • commit-prompt (string) — Custom commit message prompt
  • dry-run (boolean, default: false)

Linting & Testing:

  • lint-cmd (list of language: command) — Per-language lint commands
  • auto-lint (boolean, default: true) — Auto-lint after edits
  • test-cmd (string) — Test runner command
  • auto-test (boolean, default: false) — Auto-test after edits

Other Settings:

  • yes-always (boolean, default: false) — Auto-approve all prompts
  • vim (boolean, default: false) — Vi editing mode in prompt
  • chat-language (string) — Language for AI responses
  • encoding (string, default: “utf-8”)
  • line-endings (string) — platform, lf, or crlf
  • suggest-shell-commands (boolean, default: true)
  • fancy-input (boolean, default: true) — Rich input widget
  • multiline (boolean, default: false) — Multiline input by default
  • detect-urls (boolean, default: true)
  • editor (string) — External editor for /editor
  • voice-format (string, default: “wav”) — wav, webm, mp3
  • voice-language (string, default: “auto”) — ISO 639-1 code
  • file (list of paths) — Files to add to chat on startup
  • read (list of paths) — Read-only files on startup

Separate from the main config, Aider maintains a model settings registry at aider/resources/model-settings.yml. This is a YAML list of objects with per-model tuning:

- name: gpt-5.2-codex
edit_format: diff
weak_model_name: gpt-5-mini
use_repo_map: true
lazy: true
reminder: sys
examples_as_sys_msg: true
editor_edit_format: editor-diff

Fields per entry: name, edit_format, editor_edit_format, weak_model_name, use_repo_map, lazy, reminder, examples_as_sys_msg. Users can override this with --model-settings-file PATH pointing to a custom .aider.model.settings.yml. Additionally, --model-metadata-file PATH loads a JSON file with context window sizes and cost-per-token data.

Aider loads .env files via python-dotenv (controlled by --env-file, default .env). This is the recommended location for API keys rather than putting them in the YAML config, which might get committed to git.


Reference: references/codex/ at commit 4ab44e2c

Codex uses TOML. The user config file is ~/.codex/config.toml. Project-level config lives at .codex/config.toml in the project root. Enterprise/managed config is at /etc/codex/config.toml. The full struct is defined in codex-rs/core/src/config/mod.rs starting at line 870 (pub struct ConfigToml).

All config keys use snake_case, matching Rust struct field names. Serde handles TOML deserialization directly into the ConfigToml struct. Unknown keys cause a deserialization error — Codex uses strict schema validation unlike Aider’s silent-ignore approach.

Schema: Rust Structs with JSON Schema Generation

Section titled “Schema: Rust Structs with JSON Schema Generation”

Codex derives its schema from the Rust type system using the schemars crate. A generated JSON Schema is maintained at codex-rs/core/config.schema.json. This schema is used for IDE autocompletion and config validation. The ConfigToml struct (mod.rs:870-1090) uses #[serde(default)] for optional collections and Option<T> for all user-facing fields to distinguish “not set” from “set to default.”

Model Selection:

  • model (string, optional) — Primary model name
  • review_model (string, optional) — Model for /review command
  • model_provider (string, optional) — Key into model_providers map
  • model_context_window (integer, optional) — Context window size in tokens
  • model_auto_compact_token_limit (integer, optional) — Compaction trigger threshold
  • model_reasoning_effort (enum, optional) — "none", "minimal", "low", "medium", "high", "xhigh"
  • model_reasoning_summary (enum, optional) — "auto", "concise", "detailed", "none"
  • model_verbosity (enum, optional) — "low", "medium", "high" (GPT-5 text.verbosity)
  • model_supports_reasoning_summaries (boolean, optional) — Force-enable reasoning summaries
  • personality (enum, optional) — "none", "friendly", "pragmatic"

Permissions & Sandbox:

  • approval_policy (enum, optional) — "untrusted", "on-failure", "on-request", "never"
  • sandbox_mode (enum, optional) — "read-only", "workspace-write", "danger-full-access"
  • sandbox_workspace_write (table, optional) — Fine-grained sandbox settings:
    • exclude_slash_tmp (boolean)
    • exclude_tmpdir_env_var (boolean)
    • network_access (boolean)
    • writable_roots (array of absolute paths)

Instructions:

  • instructions (string, optional) — System instructions appended to prompt
  • developer_instructions (string, optional) — Inserted as developer role message
  • model_instructions_file (path, optional) — Override built-in model instructions (discouraged)
  • compact_prompt (string, optional) — Custom compaction prompt

Shell Environment:

  • shell_environment_policy (table) — Controls subprocess environment:
    • inherit (enum) — "core", "all", "none"
    • set (table of string→string) — Explicit env vars
    • include_only (array of regex patterns)
    • exclude (array of regex patterns)
    • ignore_default_excludes (boolean)
    • experimental_use_profile (boolean)

Model Providers:

  • model_providers (table of string→provider) — Custom provider definitions:
    • name (string, required)
    • base_url (string, optional)
    • env_key (string, optional) — Env var holding the API key
    • env_key_instructions (string, optional) — Help text for setup
    • http_headers (table of string→string, optional)
    • env_http_headers (table of string→string, optional) — Headers with env var values
    • query_params (table of string→string, optional)
    • request_max_retries (integer)
    • stream_idle_timeout_ms (integer)
    • stream_max_retries (integer)
    • requires_openai_auth (boolean)
    • supports_websockets (boolean)
    • wire_api (enum) — Currently only "responses"

MCP Servers:

  • mcp_servers (table of string→server config):
    • command (string, optional) — Stdio transport executable
    • args (array of string, optional)
    • cwd (path, optional)
    • url (string, optional) — HTTP transport endpoint
    • bearer_token (string, optional)
    • bearer_token_env_var (string, optional)
    • enabled (boolean, default: true)
    • required (boolean, default: false)
    • startup_timeout_sec (duration, optional)
    • tool_timeout_sec (duration, optional)
    • enabled_tools (array of string, optional) — Allowlist
    • disabled_tools (array of string, optional) — Denylist
    • scopes (array of string, optional) — OAuth scopes
    • env (table of string→string, optional)
    • env_vars (array of string, optional)
  • mcp_oauth_credentials_store (enum, optional) — "auto", "file", "keyring"
  • mcp_oauth_callback_port (integer, optional)

Profiles:

  • profile (string, optional) — Active profile name
  • profiles (table of string→profile):

Each profile inherits a subset of top-level fields (defined in ConfigProfile struct): model, model_provider, model_reasoning_effort, model_reasoning_summary, model_verbosity, personality, approval_policy, sandbox_mode, web_search, tools, and features. Activating a profile overlays these values on top of the base config.

[profiles.fast]
model = "gpt-5-mini"
model_reasoning_effort = "low"
[profiles.careful]
model = "gpt-5.2-codex"
model_reasoning_effort = "high"
approval_policy = "untrusted"

Projects:

  • projects (table of string→project):
    • trust_level (enum) — "trusted" or "untrusted"

This controls whether .codex/config.toml files found in project directories are loaded. Untrusted projects have their project-level config ignored.

Tools & Capabilities:

  • tools (table, optional):
    • view_image (boolean, optional)
    • web_search (boolean, optional)
  • web_search (enum, optional) — "disabled", "cached", "live"

Features:

  • features (table of string→boolean) — 30+ feature flags including js_repl, memory_tool, multi_agent, undo, web_search, sqlite, apply_patch_freeform, steer, shell_tool, search_tool, and many experimental toggles. The schema enforces known keys via crate::config::schema::features_schema — unknown feature names produce a validation error.
  • suppress_unstable_features_warning (boolean, optional)

TUI:

  • tui (table, optional):
    • alternate_screen (enum) — "auto", "always", "never"
    • animations (boolean, default: true)
    • notifications (boolean or array of event types)
    • notification_method (enum) — "auto", "osc9", "bel"
    • show_tooltips (boolean, default: true)
    • status_line (array of string) — Ordered status bar items

History & Logging:

  • history (table, optional):
    • persistence (enum) — "save-all", "none"
    • max_bytes (integer, optional)
  • log_dir (path, optional) — Custom log directory
  • hide_agent_reasoning (boolean, optional)
  • show_raw_agent_reasoning (boolean, optional)

Ghost Snapshots (Undo):

  • ghost_snapshot (table, optional):
    • disable_warnings (boolean)
    • ignore_large_untracked_dirs (integer) — File count threshold
    • ignore_large_untracked_files (integer) — Size in bytes

Memories:

  • memories (table, optional):
    • phase_1_model (string, optional) — Summarization model
    • phase_2_model (string, optional) — Consolidation model
    • max_rollout_age_days (integer)
    • min_rollout_idle_hours (integer)
    • max_rollouts_per_startup (integer)
    • max_raw_memories_for_global (integer)

Agents:

  • agents (table, optional):
    • max_threads (integer, optional) — Concurrent agent limit

Skills:

  • skills (table, optional):
    • config (array of skill):
      • enabled (boolean, required)
      • path (absolute path, required)

Other:

  • file_opener (enum, optional) — "vscode", "vscode-insiders", "windsurf", "cursor", "none"
  • notify (array of string, optional) — Custom notification command
  • project_doc_max_bytes (integer, optional) — Max AGENTS.md size
  • project_doc_fallback_filenames (array of string, optional)
  • tool_output_token_limit (integer, optional)
  • check_for_update_on_startup (boolean, default: true)
  • disable_paste_burst (boolean, optional)
  • analytics (table, optional): enabled (boolean, default: true)
  • feedback (table, optional): enabled (boolean, default: true)
  • cli_auth_credentials_store (enum, optional) — "file", "keyring", "auto", "ephemeral"
  • oss_provider (string, optional) — "ollama", "lmstudio"
  • project_root_markers (array of string, optional) — Defaults to [".git"]
model = "gpt-5.2-codex"
model_reasoning_effort = "high"
approval_policy = "on-failure"
sandbox_mode = "workspace-write"
[model_providers.custom-openai]
name = "Custom OpenAI"
base_url = "https://api.example.com/v1"
env_key = "CUSTOM_API_KEY"
[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
[tui]
alternate_screen = "auto"
status_line = ["model", "tokens", "cost", "session"]
[profiles.fast]
model = "gpt-5-mini"
model_reasoning_effort = "none"

Reference: references/opencode/ at commit 7ed44997

OpenCode uses JSONC (JSON with Comments). The canonical filenames are opencode.jsonc and opencode.json (both accepted). The schema is defined entirely in Zod at packages/opencode/src/config/config.ts (lines 656–1195).

All config keys use snake_case in the top-level schema. Nested objects follow the same convention. The Zod schema uses .strict() in some places and .catchall(z.any()) in others — the Agent schema, for instance, captures unknown keys into an options bag rather than rejecting them.

All string values in OpenCode config support two interpolation patterns:

  • {env:VAR_NAME} — Replaced with the value of environment variable VAR_NAME
  • {file:path/to/file} — Replaced with the contents of the referenced file

This is resolved during config loading, not at runtime. It allows keeping secrets out of the config file:

{
"provider": {
"custom": {
"api_key": "{env:CUSTOM_API_KEY}"
}
}
}

Core Model Settings:

  • model (string) — Format: "provider/model" (e.g., "anthropic/claude-4-6-sonnet-20260115")
  • small_model (string) — Lightweight model for titles, summaries
  • default_agent (string) — Primary agent name (fallback: "build")

Agents:

  • agent (record of string→agent config):
    • model (string, optional) — Model override
    • variant (string, optional) — Default model variant
    • temperature (number, optional) — 0–2
    • top_p (number, optional)
    • prompt (string, optional) — System prompt override
    • description (string, optional) — When to use this agent
    • mode (enum, optional) — "subagent", "primary", "all"
    • hidden (boolean, optional) — Hide from @ menu
    • color (string, optional) — Hex #FF5733 or theme color name
    • steps (integer, optional) — Max agentic iterations before text-only
    • permission (record of tool→action) — Per-tool permission overrides
    • disable (boolean, optional)
    • options (record, optional) — Arbitrary extra options

Unknown keys on an agent definition are silently moved into options via a Zod .catchall(z.any()) transform (config.ts:709-734).

Commands:

  • command (record of string→command):
    • template (string, required) — Command template
    • description (string, optional)
    • agent (string, optional) — Agent to execute with
    • model (string, optional) — Model override
    • subtask (boolean, optional)

Skills:

  • skills (object):
    • paths (array of string, optional) — Additional skill directories
    • urls (array of string, optional) — Remote skill registry URLs

Providers:

  • provider (record of string→provider config) — Custom model providers
  • disabled_providers (array of string) — Disable auto-loaded providers
  • enabled_providers (array of string) — Restrict to only these providers

MCP Servers:

  • mcp (record of string→server config or { enabled: boolean }):
    • command (array of string) — Command and arguments (stdio transport)
    • url (string) — HTTP endpoint
    • bearer_token (string, optional)
    • bearer_token_env_var (string, optional)
    • headers (record of string→string, optional)
    • env_http_headers (record of string→string, optional)
    • clientId (string, optional) — OAuth client ID
    • clientSecret (string, optional) — OAuth secret
    • scope (string, optional) — OAuth scopes

Permissions:

  • permission (record of tool name → action):
    • Action enum: "ask", "allow", "deny"
    • Keys are tool names (e.g., "edit", "shell", "web_search")

Keybinds:

  • keybinds (object) — 70+ customizable keybindings. Each is a comma-separated string of key combos. The leader key (default: ctrl+x) can be referenced as <leader> in bindings. Key categories:
    • Application: app_exit, editor_open, theme_list, sidebar_toggle, command_list
    • Session: session_new, session_list, session_export, session_fork, session_rename, session_delete, session_compact, session_interrupt, session_share, session_timeline
    • Messages: messages_page_up, messages_page_down, messages_line_up, messages_line_down, messages_half_page_up, messages_half_page_down, messages_first, messages_last, messages_copy, messages_undo, messages_redo, messages_toggle_conceal
    • Model: model_list, model_cycle_recent, model_cycle_favorite, variant_cycle
    • Agent: agent_list, agent_cycle, agent_cycle_reverse
    • Input: input_clear, input_paste, input_submit, input_newline, input_move_*, input_select_*, input_delete_*, input_word_*, input_undo, input_redo, and 20+ more cursor/selection operations

TUI:

  • tui (object):
    • alternate_screen (enum) — "auto", "always", "never"
    • animations (boolean, default: true)
    • show_tooltips (boolean, default: true)
    • notifications (boolean or array of event types)
    • notification_method (enum) — "auto", "osc9", "bel"
    • experimental_mode (enum) — "plan", "default"
    • status_line (array of string) — Ordered status bar items

Theming:

  • theme (string) — Theme name

LSP:

  • lsp (false or record of language→server config):
    • disabled (boolean, optional)
    • command (array of string) — LSP server command
    • extensions (array of string) — File extensions
    • env (record of string→string) — Environment variables
    • initialization (record) — LSP initialization options

Setting lsp to false disables all language servers.

Formatters:

  • formatter (false or record of language→formatter config):
    • disabled (boolean, optional)
    • command (array of string) — Formatter command
    • environment (record of string→string)
    • extensions (array of string)

Compaction:

  • compaction (object):
    • auto (boolean, default: true) — Auto-compact on overflow
    • prune (boolean, default: true) — Prune tool outputs first
    • reserved (number) — Token buffer to reserve

Experimental:

  • experimental (object):
    • disable_paste_summary (boolean)
    • batch_tool (boolean)
    • openTelemetry (boolean)
    • primary_tools (array of string)
    • continue_loop_on_deny (boolean)
    • mcp_timeout (number) — Milliseconds

Other:

  • share (enum) — "manual", "auto", "disabled" — Session sharing
  • autoupdate (boolean or "notify")
  • snapshot (boolean) — Enable undo snapshots
  • username (string) — Custom display name
  • instructions (array of string) — Additional instruction file paths
  • watcher (object): ignore (array of glob patterns)
  • logLevel (string) — Log level
{
// Primary model
"model": "anthropic/claude-4-6-sonnet-20260115",
"small_model": "anthropic/claude-4-haiku-5-20260115",
"default_agent": "build",
// Custom agent
"agent": {
"review": {
"model": "anthropic/claude-4-6-sonnet-20260115",
"prompt": "You are a code reviewer. Focus on bugs and security issues.",
"mode": "subagent",
"steps": 10
}
},
// MCP servers
"mcp": {
"filesystem": {
"command": ["npx", "-y", "@modelcontextprotocol/server-filesystem"],
"url": null
}
},
// Keybinds
"keybinds": {
"leader": "ctrl+x",
"session_new": "<leader>n"
}
}

Aider silently ignores unknown YAML keys. This means typos like auto-commmits (triple-m) are never reported — the user thinks they’ve set a flag but it’s doing nothing. Codex rejects unknown keys at the ConfigToml level (serde strict deserialization), which catches typos but makes forward/backward compatibility harder: a config written for a newer version of Codex will fail to load on an older version. OpenCode’s Zod schema sits in the middle — some objects are strict, but the Agent schema uses .catchall() to absorb unknowns.

Aider uses kebab-case (auto-commits), Codex uses snake_case (approval_policy), and OpenCode uses snake_case for most keys but camelCase for some nested fields (e.g., clientId in MCP OAuth). This means users switching between tools must re-learn the convention, and any config migration tooling needs case transformation.

Aider has an entirely flat key structure — every setting is a top-level key with no nesting. This is a direct consequence of using configargparse, which maps CLI flags 1:1 to config keys. Codex and OpenCode both support deeply nested tables (mcp_servers.name.command, agent.review.model). Flat keys are simpler to document but don’t scale well to complex features like per-provider or per-agent configuration.

Aider allows API keys directly in .aider.conf.yml (openai-api-key, anthropic-api-key). This is convenient but dangerous if the config file is committed to git. Codex avoids this entirely — API keys must come from environment variables via env_key references. OpenCode supports {env:VAR} interpolation, providing a middle ground where the config file references the env var name rather than the secret value itself.

When a field is Option<bool> (Codex) or has a Zod .default() (OpenCode), the distinction between “not set” and “set to false” matters. Aider sidesteps this by using store_true/store_false argument pairs (e.g., --auto-commits/--no-auto-commits), but in the YAML file there’s no way to express “unset” versus false. Codex’s Option<T> wrapper preserves the distinction through the entire config pipeline.

Codex profiles can only override a subset of the full config (the ConfigProfile fields). You cannot put MCP server definitions, shell environment policies, or feature flags in a profile. This limits the usefulness of profiles for switching between fundamentally different work contexts (e.g., “local model development” vs “cloud production”).

OpenCode’s JSONC format allows comments and trailing commas, which is friendlier for humans but not supported by standard JSON parsers. This means OpenCode’s config files cannot be validated or processed by generic JSON tools. The patchJsonc() utility for comment-preserving writes adds further complexity. Codex’s TOML avoids this issue since comments are a first-class TOML feature.


TOML is the right choice for a Rust project. It has native serde support, first-class comments, clear nesting semantics, and an established convention in the Rust ecosystem (Cargo.toml). The file name is openoxide.toml.

Follow Codex’s approach: define all config as Rust structs with #[derive(Deserialize, Serialize, JsonSchema)]. Generate a JSON Schema from the structs for IDE autocompletion and external validation. Use Option<T> for all user-facing fields to preserve the “not set” sentinel.

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct OpenOxideConfig {
pub model: Option<String>,
pub model_provider: Option<String>,
pub approval_policy: Option<ApprovalPolicy>,
pub sandbox_mode: Option<SandboxMode>,
pub instructions: Option<String>,
#[serde(default)]
pub providers: HashMap<String, ProviderConfig>,
#[serde(default)]
pub mcp_servers: HashMap<String, McpServerConfig>,
#[serde(default)]
pub agents: HashMap<String, AgentConfig>,
#[serde(default)]
pub profiles: HashMap<String, ProfileConfig>,
#[serde(default)]
pub features: HashMap<String, bool>,
pub tui: Option<TuiConfig>,
pub keybinds: Option<KeybindConfig>,
}

Reject unknown keys (unlike Aider), but produce a helpful error message that includes the unknown key name and suggests the closest valid key using edit distance. This catches typos without being opaque about what went wrong.

Support ${env:VAR} interpolation in string values (like OpenCode) as a post-deserialization pass. Do NOT support ${file:path} — it creates a dependency on external files that complicates reproducibility and caching. API keys should come from env vars, period.

Unlike Codex’s restricted ConfigProfile, allow profiles to override any top-level config field. Implement profiles as a full OpenOxideConfig with all fields optional, merged on top of the base config. This avoids the need to maintain a separate “profile-able fields” list.

Use snake_case consistently throughout. No kebab-case anywhere in the config file. The CLI uses kebab-case for flags (--approval-policy), and a mechanical conversion (-_) maps flags to config keys. Document this mapping explicitly.

  • serde + toml — TOML deserialization
  • schemars — JSON Schema generation from structs
  • strsim — Edit distance for “did you mean?” on unknown keys