Configuration System
mistral-vibe
Configuration System
The configuration system uses Pydantic v2 settings with multiple sources and validation.
Overview
File: vibe/core/config.py
Class: VibeConfig at line 297
Configuration Sources
Priority Order (highest to lowest)
- Constructor arguments (
init_settings) - Environment variables (
VIBE_*prefix) - TOML config file (
TomlFileSettingsSource) - Default values
Source Customization
# config.py:388-409
@classmethod
def settings_customise_sources(
cls,
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> tuple[PydanticBaseSettingsSource, ...]:
return (
init_settings,
env_settings,
TomlFileSettingsSource(settings_cls),
file_secret_settings,
)File Locations
Path Resolution
Path resolution is split across two modules:
Global Paths: vibe/core/paths/global_paths.py
# global_paths.py:10-16
class GlobalPath:
def __init__(self, resolver: Callable[[], Path]) -> None:
self._resolver = resolver
@property
def path(self) -> Path:
return self._resolver()Global path constants:
| Constant | Resolves To |
|---|---|
VIBE_HOME |
~/.vibe (or $VIBE_HOME) |
GLOBAL_CONFIG_FILE |
~/.vibe/config.toml |
GLOBAL_ENV_FILE |
~/.vibe/.env |
GLOBAL_TOOLS_DIR |
~/.vibe/tools |
GLOBAL_SKILLS_DIR |
~/.vibe/skills |
GLOBAL_AGENTS_DIR |
~/.vibe/agents |
GLOBAL_PROMPTS_DIR |
~/.vibe/prompts |
SESSION_LOG_DIR |
~/.vibe/logs/session |
TRUSTED_FOLDERS_FILE |
~/.vibe/trusted_folders.toml |
DEFAULT_TOOL_DIR |
vibe/core/tools/builtins |
Config Paths (Trust-Aware): vibe/core/paths/config_paths.py
# config_paths.py:12-17
class ConfigPath(GlobalPath):
@property
def path(self) -> Path:
if _config_paths_locked:
raise RuntimeError("Config path is locked")
return super().pathConfig paths are locked until unlock_config_paths() is called after the trusted folder check. This prevents accidental resolution before the trust decision is made. See Trusted Folders for details.
File Hierarchy
~/.vibe/ # Global config directory
├── config.toml # Main configuration
├── .env # API keys (not in TOML)
├── instructions.md # User instructions for system prompt
├── vibehistory # Command history
├── trusted_folders.toml # Trusted/untrusted directory decisions
├── vibe.log # Application log
├── logs/
│ └── session/ # Session log directories
│ └── session_YYYYMMDD_HHMMSS_<id>/
│ ├── meta.json
│ └── messages.jsonl
├── agents/ # Custom agent TOML files
│ └── myagent.toml
├── skills/ # Custom skills
│ └── my-skill/
│ └── SKILL.md
├── prompts/ # Custom system prompts
│ └── myprompt.md
└── tools/ # Custom tools
└── my_tool.py
./.vibe/ # Project-local (requires trusted folder)
├── config.toml
├── tools/
├── skills/
├── agents/
└── ...
VibeConfig Fields
Class: at config.py:297
Core Settings
# config.py:298-313
class VibeConfig(BaseSettings):
active_model: str = "devstral-2" # Model alias to use
vim_keybindings: bool = False # Enable vim mode in input
disable_welcome_banner_animation: bool = False
displayed_workdir: str = "" # Override displayed path
auto_compact_threshold: int = 200_000 # Token threshold for auto-compact
context_warnings: bool = False # Warn when approaching limit
textual_theme: str = "textual-dark" # TUI theme
instructions: str = "" # Additional instructions
workdir: Path | None = None # Override working directory
system_prompt_id: str = "cli" # System prompt to use
include_commit_signature: bool = True # Include git signature in prompt
include_model_info: bool = True # Include model info in prompt
include_project_context: bool = True # Include project context
include_prompt_detail: bool = True # Include detailed prompts
enable_update_checks: bool = True # Check for version updates
api_timeout: float = 720.0 # API timeout in secondsProviders and Models
# config.py:314-317
providers: list[ProviderConfig] = Field(default_factory=lambda: list(DEFAULT_PROVIDERS))
models: list[ModelConfig] = Field(default_factory=lambda: list(DEFAULT_MODELS))Agent, Skill, and Tool Settings
# Additional VibeConfig fields
auto_approve: bool = False # Auto-approve all tool executions
# Tool configuration
tools: dict[str, BaseToolConfig] # Per-tool configuration
tool_paths: list[str] # Custom tool directories
enabled_tools: list[str] # Tool whitelist patterns
disabled_tools: list[str] # Tool blacklist patterns
# Agent configuration
agent_paths: list[Path] # Custom agent directories
enabled_agents: list[str] # Agent whitelist patterns
disabled_agents: list[str] # Agent blacklist patterns
# Skill configuration
skill_paths: list[Path] # Custom skill directories
enabled_skills: list[str] # Skill whitelist patterns
disabled_skills: list[str] # Skill blacklist patternsSub-Configurations
project_context: ProjectContextConfig # Project context settings
session_logging: SessionLoggingConfig # Session logging settings
mcp_servers: list[MCPServer] # MCP server configurationsSub-Configuration Classes
ProjectContextConfig
# config.py:117-125
class ProjectContextConfig(BaseSettings):
max_chars: int = 40_000 # Max project context size
default_commit_count: int = 5 # Recent commits to include
max_doc_bytes: int = 32 * 1024 # Max doc file size
truncation_buffer: int = 1_000
max_depth: int = 3 # Directory tree depth
max_files: int = 1000 # Max files in tree
max_dirs_per_level: int = 20
timeout_seconds: float = 2.0 # Context gathering timeoutSessionLoggingConfig
# config.py:128-143
class SessionLoggingConfig(BaseSettings):
save_dir: str = "" # Log directory (default: ~/.vibe/logs/session)
session_prefix: str = "session" # Log file prefix
enabled: bool = True # Enable session loggingProviderConfig
# config.py:151-156
class ProviderConfig(BaseModel):
name: str # Provider identifier
api_base: str # API base URL
api_key_env_var: str = "" # Env var for API key
api_style: str = "openai" # API style
backend: Backend = Backend.GENERIC # Backend typeModelConfig
# config.py:241-247
class ModelConfig(BaseModel):
name: str # Model name for API
provider: str # Provider reference
alias: str # User-friendly alias
temperature: float = 0.2 # Default temperature
input_price: float = 0.0 # Price per million input tokens
output_price: float = 0.0 # Price per million output tokensMCP Server Configuration
# config.py:159-238
class MCPHttp(_MCPBase, _MCPHttpFields):
transport: Literal["http"]
class MCPStreamableHttp(_MCPBase, _MCPHttpFields):
transport: Literal["streamable-http"]
class MCPStdio(_MCPBase):
transport: Literal["stdio"]
command: str | list[str]
args: list[str] = Field(default_factory=list)
MCPServer = Annotated[MCPHttp | MCPStreamableHttp | MCPStdio, Field(discriminator="transport")]TOML Configuration File
Example config.toml
# Model selection
active_model = "devstral-2"
# UI Settings
vim_keybindings = false
textual_theme = "textual-dark"
disable_welcome_banner_animation = false
# Features
include_project_context = true
include_model_info = true
include_commit_signature = true
include_prompt_detail = true
enable_update_checks = true
# Context Management
auto_compact_threshold = 200000
context_warnings = false
# System Prompt
system_prompt_id = "cli"
# API Timeout
api_timeout = 720.0
# Provider Configuration
[[providers]]
name = "mistral"
api_base = "https://api.mistral.ai/v1"
api_key_env_var = "MISTRAL_API_KEY"
backend = "mistral"
[[providers]]
name = "llamacpp"
api_base = "http://127.0.0.1:8080/v1"
api_key_env_var = ""
# Model Configuration
[[models]]
name = "mistral-vibe-cli-latest"
provider = "mistral"
alias = "devstral-2"
temperature = 0.2
input_price = 0.4
output_price = 2.0
[[models]]
name = "devstral-small-latest"
provider = "mistral"
alias = "devstral-small"
input_price = 0.1
output_price = 0.3
# Tool Configuration
[tools.bash]
permission = "always"
timeout = 60
[tools.read_file]
permission = "always"
max_read_bytes = 64000
[tools.write_file]
permission = "ask"
# Tool Enable/Disable Patterns
enabled_tools = []
disabled_tools = []
# MCP Server Configuration
[[mcp_servers]]
name = "fetch_server"
transport = "stdio"
command = "uvx"
args = ["mcp-server-fetch"]
[[mcp_servers]]
name = "my_http_server"
transport = "http"
url = "http://localhost:8000"
headers = { "Authorization" = "Bearer token" }
# Project Context
[project_context]
max_chars = 40000
max_files = 1000
max_depth = 3
timeout_seconds = 2.0
# Session Logging
[session_logging]
enabled = true
save_dir = "~/.vibe/logs/session"
session_prefix = "session"Loading Configuration
Main Load Method
# config.py:564-569
@classmethod
def load(cls, agent: str | None = None, **overrides: Any) -> VibeConfig:
cls._migrate() # Apply migrations
agent_config = cls._get_agent_config(agent) # Load agent config if specified
init_data = {**(agent_config or {}), **overrides}
return cls(**init_data)Migration
# config.py:542-562
@classmethod
def _migrate(cls) -> None:
"""Apply configuration migrations."""
if not CONFIG_FILE.exists():
return
config = tomllib.load(CONFIG_FILE.open("rb"))
needs_save = False
# Example migration: update auto_compact_threshold default
if config.get("auto_compact_threshold") == 100_000:
config["auto_compact_threshold"] = 200_000
needs_save = True
if needs_save:
cls.dump_config(config)Agent Configuration
# config.py:528-539
@classmethod
def _get_agent_config(cls, agent: str | None) -> dict[str, Any] | None:
if agent is None:
return None
agent_config_path = (AGENT_DIR / agent).with_suffix(".toml")
try:
return tomllib.load(agent_config_path.open("rb"))
except FileNotFoundError:
raise ValueError(f"Config '{agent}.toml' not found in {AGENT_DIR}")Validation
API Key Validation
# config.py:411-421
@model_validator(mode="after")
def _check_api_key(self) -> VibeConfig:
active_model = self.get_active_model()
provider = self.get_provider_for_model(active_model)
api_key_env = provider.api_key_env_var
if api_key_env and not os.getenv(api_key_env):
raise MissingAPIKeyError(api_key_env, provider.name)
return selfBackend Compatibility
# config.py:423-442
@model_validator(mode="after")
def _check_api_backend_compatibility(self) -> VibeConfig:
active_model = self.get_active_model()
provider = self.get_provider_for_model(active_model)
MISTRAL_API_BASES = ["https://codestral.mistral.ai", "https://api.mistral.ai"]
is_mistral_api = any(provider.api_base.startswith(base) for base in MISTRAL_API_BASES)
if (is_mistral_api and provider.backend != Backend.MISTRAL) or \
(not is_mistral_api and provider.backend != Backend.GENERIC):
raise WrongBackendError(provider.backend, is_mistral_api)
return selfModel Alias Uniqueness
# config.py:477-486
@model_validator(mode="after")
def _validate_model_uniqueness(self) -> VibeConfig:
seen_aliases: set[str] = set()
for model in self.models:
if model.alias in seen_aliases:
raise ValueError(f"Duplicate model alias: '{model.alias}'")
seen_aliases.add(model.alias)
return selfSaving Configuration
# config.py:493-526
@classmethod
def save_updates(cls, updates: dict[str, Any]) -> None:
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
current_config = TomlFileSettingsSource(cls).toml_data
def deep_merge(target: dict, source: dict) -> None:
for key, value in source.items():
if key in target and isinstance(target[key], dict) and isinstance(value, dict):
deep_merge(target[key], value)
elif key in target and isinstance(target[key], list) and isinstance(value, list):
if key in {"providers", "models"}:
target[key] = value # Replace lists
else:
target[key] = list(set(value + target[key])) # Merge
else:
target[key] = value
deep_merge(current_config, updates)
cls.dump_config(current_config)
@classmethod
def dump_config(cls, config: dict[str, Any]) -> None:
with CONFIG_FILE.open("wb") as f:
tomli_w.dump(config, f)Environment Variables
API Keys
Loaded from ~/.vibe/.env:
# config.py:44-49
def load_api_keys_from_env() -> None:
if GLOBAL_ENV_FILE.is_file():
env_vars = dotenv_values(GLOBAL_ENV_FILE)
for key, value in env_vars.items():
if value:
os.environ.setdefault(key, value)Config Overrides
Environment variables with VIBE_ prefix override config values:
VIBE_ACTIVE_MODEL→active_modelVIBE_AUTO_COMPACT_THRESHOLD→auto_compact_threshold- etc.
Helper Methods
# config.py:372-386
def get_active_model(self) -> ModelConfig:
"""Get the currently active model configuration."""
for model in self.models:
if model.alias == self.active_model:
return model
raise ValueError(f"Active model '{self.active_model}' not found")
def get_provider_for_model(self, model: ModelConfig) -> ProviderConfig:
"""Get the provider for a given model."""
for provider in self.providers:
if provider.name == model.provider:
return provider
raise ValueError(f"Provider '{model.provider}' not found")Source File References
| File | Key Lines | Description |
|---|---|---|
paths/global_paths.py:10-16 |
GlobalPath |
Lazy path resolver class |
paths/global_paths.py:28-40 |
Constants | All global path constants |
paths/config_paths.py:12-17 |
ConfigPath |
Trust-locked path resolver |
paths/config_paths.py:20-31 |
_resolve_config_path() |
Trust-aware config resolution |
paths/config_paths.py:58-60 |
unlock_config_paths() |
Unlock after trust check |
config.py:44-49 |
load_api_keys_from_env() |
API key loading |
config.py:91-114 |
TomlFileSettingsSource |
TOML source implementation |
config.py:117-125 |
ProjectContextConfig |
Project context settings |
config.py:128-143 |
SessionLoggingConfig |
Session logging settings |
config.py:151-156 |
ProviderConfig |
Provider configuration |
config.py:241-247 |
ModelConfig |
Model configuration |
config.py:297-354 |
VibeConfig |
Main configuration class |
config.py:388-409 |
settings_customise_sources() |
Source priority |
config.py:411-442 |
Validators | API key and backend validation |
config.py:493-526 |
save_updates() |
Config persistence |
config.py:564-569 |
load() |
Main load method |