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.
1. Aider Implementation
Section titled “1. Aider Implementation”Reference: references/aider/ at commit b9050e1d
File Format and Name
Section titled “File Format and Name”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_",)Key Style
Section titled “Key Style”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-commits → AIDER_AUTO_COMMITS, dark-mode → AIDER_DARK_MODE. Aider writes zero custom env-var mapping code — configargparse handles all of it.
Schema: The Argument Parser Is the Schema
Section titled “Schema: The Argument Parser Is the Schema”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.
Full Field Catalog
Section titled “Full Field Catalog”The fields below are organized by the argument groups defined in aider/args.py:
Model Selection:
model(string) — Primary LLM model nameweak-model(string) — Model for commit messages and summarizationeditor-model(string) — Model for the/editorcommandeditor-edit-format(string) — Edit format for the editor modeledit-format(string) — Primary edit format (diff,udiff,whole,diff-fenced,architect, etc.)architect(boolean, default: false) — Shorthand for--edit-format architectauto-accept-architect(boolean, default: true) — Auto-accept architect suggestions
API Configuration:
openai-api-key(string) — OpenAI API keyanthropic-api-key(string) — Anthropic API keyopenai-api-base(string) — Custom API base URLapi-key(list ofprovider=key) — Generic key assignmentset-env(list ofKEY=value) — Set arbitrary environment variablesverify-ssl(boolean, default: true) — TLS verificationtimeout(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,manualmap-multiplier-no-files(float, default: 2.0) — Map budget multiplier when no files in chatmax-chat-history-tokens(integer) — Soft limit on history tokens before summarization
Cache:
cache-prompts(boolean, default: false) — Enable prompt cachingcache-keepalive-pings(integer, default: 0) — Background cache warming pings (0 disables)
History & Persistence:
input-history-file(path, default:.aider.input.history) — Readline historychat-history-file(path, default:.aider.chat.history.md) — Markdown chat logrestore-chat-history(boolean, default: false) — Resume from chat history on startupllm-history-file(path) — Raw LLM conversation log
Output & Display:
dark-mode(boolean) — Dark terminal presetlight-mode(boolean) — Light terminal presetpretty(boolean, default: true) — Rich terminal outputstream(boolean, default: true) — Stream LLM responsesuser-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 themeshow-diffs(boolean, default: false)
Git:
git(boolean, default: true) — Enable git integrationgitignore(boolean, default: true) — Respect .gitignoreaiderignore(path, default:.aiderignore) — Custom ignore patternssubtree-only(boolean, default: false) — Restrict to current subtreeauto-commits(boolean, default: true) — Commit after each editdirty-commits(boolean, default: true) — Pre-commit dirty filesattribute-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 hookscommit-prompt(string) — Custom commit message promptdry-run(boolean, default: false)
Linting & Testing:
lint-cmd(list oflanguage: command) — Per-language lint commandsauto-lint(boolean, default: true) — Auto-lint after editstest-cmd(string) — Test runner commandauto-test(boolean, default: false) — Auto-test after edits
Other Settings:
yes-always(boolean, default: false) — Auto-approve all promptsvim(boolean, default: false) — Vi editing mode in promptchat-language(string) — Language for AI responsesencoding(string, default: “utf-8”)line-endings(string) —platform,lf, orcrlfsuggest-shell-commands(boolean, default: true)fancy-input(boolean, default: true) — Rich input widgetmultiline(boolean, default: false) — Multiline input by defaultdetect-urls(boolean, default: true)editor(string) — External editor for/editorvoice-format(string, default: “wav”) —wav,webm,mp3voice-language(string, default: “auto”) — ISO 639-1 codefile(list of paths) — Files to add to chat on startupread(list of paths) — Read-only files on startup
Model Settings File
Section titled “Model Settings File”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-diffFields 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.
The .env File
Section titled “The .env File”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.
2. Codex Implementation
Section titled “2. Codex Implementation”Reference: references/codex/ at commit 4ab44e2c
File Format and Name
Section titled “File Format and Name”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).
Key Style
Section titled “Key Style”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.”
Full Field Catalog
Section titled “Full Field Catalog”Model Selection:
model(string, optional) — Primary model namereview_model(string, optional) — Model for/reviewcommandmodel_provider(string, optional) — Key intomodel_providersmapmodel_context_window(integer, optional) — Context window size in tokensmodel_auto_compact_token_limit(integer, optional) — Compaction trigger thresholdmodel_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-5text.verbosity)model_supports_reasoning_summaries(boolean, optional) — Force-enable reasoning summariespersonality(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 promptdeveloper_instructions(string, optional) — Inserted as developer role messagemodel_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 varsinclude_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 keyenv_key_instructions(string, optional) — Help text for setuphttp_headers(table of string→string, optional)env_http_headers(table of string→string, optional) — Headers with env var valuesquery_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 executableargs(array of string, optional)cwd(path, optional)url(string, optional) — HTTP transport endpointbearer_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) — Allowlistdisabled_tools(array of string, optional) — Denylistscopes(array of string, optional) — OAuth scopesenv(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 nameprofiles(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 includingjs_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 viacrate::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 directoryhide_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 thresholdignore_large_untracked_files(integer) — Size in bytes
Memories:
memories(table, optional):phase_1_model(string, optional) — Summarization modelphase_2_model(string, optional) — Consolidation modelmax_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 commandproject_doc_max_bytes(integer, optional) — Max AGENTS.md sizeproject_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"]
Example Config
Section titled “Example Config”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"3. OpenCode Implementation
Section titled “3. OpenCode Implementation”Reference: references/opencode/ at commit 7ed44997
File Format and Name
Section titled “File Format and Name”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).
Key Style
Section titled “Key Style”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.
Variable Interpolation
Section titled “Variable Interpolation”All string values in OpenCode config support two interpolation patterns:
{env:VAR_NAME}— Replaced with the value of environment variableVAR_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}" } }}Full Field Catalog
Section titled “Full Field Catalog”Core Model Settings:
model(string) — Format:"provider/model"(e.g.,"anthropic/claude-4-6-sonnet-20260115")small_model(string) — Lightweight model for titles, summariesdefault_agent(string) — Primary agent name (fallback:"build")
Agents:
agent(record of string→agent config):model(string, optional) — Model overridevariant(string, optional) — Default model varianttemperature(number, optional) — 0–2top_p(number, optional)prompt(string, optional) — System prompt overridedescription(string, optional) — When to use this agentmode(enum, optional) —"subagent","primary","all"hidden(boolean, optional) — Hide from@menucolor(string, optional) — Hex#FF5733or theme color namesteps(integer, optional) — Max agentic iterations before text-onlypermission(record of tool→action) — Per-tool permission overridesdisable(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 templatedescription(string, optional)agent(string, optional) — Agent to execute withmodel(string, optional) — Model overridesubtask(boolean, optional)
Skills:
skills(object):paths(array of string, optional) — Additional skill directoriesurls(array of string, optional) — Remote skill registry URLs
Providers:
provider(record of string→provider config) — Custom model providersdisabled_providers(array of string) — Disable auto-loaded providersenabled_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 endpointbearer_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 IDclientSecret(string, optional) — OAuth secretscope(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")
- Action enum:
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
- Application:
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 commandextensions(array of string) — File extensionsenv(record of string→string) — Environment variablesinitialization(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 commandenvironment(record of string→string)extensions(array of string)
Compaction:
compaction(object):auto(boolean, default: true) — Auto-compact on overflowprune(boolean, default: true) — Prune tool outputs firstreserved(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 sharingautoupdate(boolean or"notify")snapshot(boolean) — Enable undo snapshotsusername(string) — Custom display nameinstructions(array of string) — Additional instruction file pathswatcher(object):ignore(array of glob patterns)logLevel(string) — Log level
Example Config
Section titled “Example Config”{ // 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" }}4. Pitfalls & Hard Lessons
Section titled “4. Pitfalls & Hard Lessons”Silent vs Strict Unknown Key Handling
Section titled “Silent vs Strict Unknown Key Handling”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.
Key Case Conventions Are Inconsistent
Section titled “Key Case Conventions Are Inconsistent”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.
Nested Tables vs Flat Keys
Section titled “Nested Tables vs Flat Keys”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.
API Keys in Config Files
Section titled “API Keys in Config Files”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.
Boolean Defaults Matter
Section titled “Boolean Defaults Matter”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.
Profile Composition Is Partial
Section titled “Profile Composition Is Partial”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”).
JSONC Compatibility
Section titled “JSONC Compatibility”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.
5. OpenOxide Blueprint
Section titled “5. OpenOxide Blueprint”File Format: TOML
Section titled “File Format: TOML”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.
Schema: Derive-Based with schemars
Section titled “Schema: Derive-Based with schemars”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>,}Strict Validation with Helpful Errors
Section titled “Strict Validation with Helpful Errors”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.
Environment Variable References
Section titled “Environment Variable References”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.
Profile Composition: Full Config Subset
Section titled “Profile Composition: Full Config Subset”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.
Key Naming Convention
Section titled “Key Naming Convention”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.
Crates
Section titled “Crates”serde+toml— TOML deserializationschemars— JSON Schema generation from structsstrsim— Edit distance for “did you mean?” on unknown keys