Centralizes configuration loading, validation, schema definitions, and runtime state management for the oh-my-opencode-slim plugin. This folder implements the configuration pipeline that merges user preferences, project overrides, and preset-based agent configurations, providing validated runtime configuration objects to the rest of the plugin.
The config system follows a layered architecture:
loadPluginConfig() creates the merged configuration objectpreset field| Abstraction | Purpose | Location |
|---|---|---|
PluginConfig |
Root configuration object with agents, presets, and feature flags | schema.ts |
AgentOverrideConfig |
Per-agent configuration (model, temperature, skills, MCPs) | schema.ts |
CouncilConfig |
Multi-LLM council configuration with presets and execution modes | council-schema.ts |
MultiplexerConfig |
Unified pane management configuration (tmux/zellij) | schema.ts |
1. Discovery Phase
├─ User config: $OPENCODE_CONFIG_DIR/oh-my-opencode-slim.{jsonc,json}
├─ Project config: <directory>/.opencode/oh-my-opencode-slim.{jsonc,json}
└─ Environment variable: OH_MY_OPENCODE_SLIM_PRESET (overrides preset field)
2. Parsing Phase
├─ JSONC support (comments, trailing commas) via stripJsonComments
├─ Environment variable interpolation: {env:VAR_NAME} → process.env.VAR_NAME
└─ Zod validation with detailed error reporting
3. Merging Phase
├─ User config (base) + Project config (override) → deep merge
├─ Preset resolution: preset → merge preset.agents with root agents
├─ Legacy tmux → multiplexer migration for backward compatibility
└─ Normalization: companion defaults, ACP agent defaults
4. Runtime Phase
├─ Active preset state persisted across plugin re-inits
└─ Previous preset tracked for reset diff computation
Agent-specific configuration lookup:
1. Check config.agents[agentName]
2. Check config.agents[legacyAlias] (e.g., "explore" → "explorer")
3. Return undefined if not found (use defaults)
MCP permission resolution:
1. Check agent override: config.agents[agentName]?.mcps
2. Fall back to DEFAULT_AGENT_MCPS[agentName]
3. Parse wildcard/exclusion syntax: ["*", "!context7"] → ["websearch", "gh_grep"]
Preset resolution flow:
1. Read config.preset (e.g., "default", "minimal")
2. Look up preset in config.presets[presetName]
3. Merge preset.agents with config.agents (root overrides take precedence)
4. Apply preset-specific agent overrides
5. Resolve preset model plans for manual agents (orchestrator, oracle, etc.)
| Module | Integration Point | Description |
|---|---|---|
src/index.ts |
loadPluginConfig() |
Main plugin entry point loads merged config |
src/agents/ |
Agent configuration | Agents use config for model selection and permissions |
src/council/ |
Council configuration | Council agent uses CouncilConfig for multi-LLM orchestration |
src/multiplexer/ |
Multiplexer configuration | Uses multiplexer config for pane layout and type |
src/cli/ |
Config file discovery | CLI tools use config paths for user/project config lookup |
The config folder re-exports its public API via src/config/index.ts:
export * from './constants';
export * from './council-schema';
export { deepMerge, loadAgentPrompt, loadPluginConfig } from './loader';
export * from './schema';
export { getAcpAgentNames, getAgentOverride, getCustomAgentNames } from './utils';
This allows consumers to import directly from src/config rather than individual files.
loadPluginConfig(directory, options?): Main entry point for configuration loading and mergingloadConfigFromPath(configPath, options?): Load and validate single config file (JSONC or JSON)findPluginConfigPaths(directory): Discover user and project config file pathsmergePluginConfigs(base, override): Deep merge two PluginConfig objectsdeepMerge(base, override): Recursively merge nested configuration objectsgetAgentOverride(config, name): Get agent-specific override with alias supportgetCustomAgentNames(config): List custom agents declared in config.agentsgetAcpAgentNames(config): List ACP agent names from config.acpAgentsloadAgentPrompt(agentName, preset?): Load custom prompt files for agentssetActiveRuntimePreset(name): Set currently active presetgetActiveRuntimePreset(): Get currently active presetgetPreviousRuntimePreset(): Get previously active presetsetActiveRuntimePresetWithPrevious(name): Set active with previous trackinggetAgentMcpList(agentName, config?): Resolve MCP permissions for an agentparseList(items, allAvailable): Parse wildcard and exclusion syntax in MCP listspreset: Active preset namesetDefaultAgent: Whether to set default agent modelautoUpdate: Enable automatic plugin updatespresets: Named preset groupings (map of presetName → AgentOverrideConfig)agents: Per-agent overrides (map of agentName → AgentOverrideConfig)disabled_agents: List of agents to disabledisabled_mcps: List of MCPs to disabledisabled_tools: List of tools to disabledisabled_skills: List of skills to disablemultiplexer: Unified pane management config (type, layout, sizes)tmux: Legacy tmux configuration (migrated to multiplexer)websearch: Websearch provider configurationinterview: Interview feature configurationbackgroundJobs: Background job configurationfallback: Failover/retry configurationcouncil: Council configuration with presets and execution modescompanion: Companion animation configurationacpAgents: ACP agent configurationsmodel: Model ID or array of model IDstemperature: Sampling temperature (0-2)variant: Model variant identifierskills: Skill allow/deny list ("*" = all, "!item" = exclude)mcps: MCP allow/deny list ("*" = all, "!item" = exclude)prompt: Custom agent prompt overrideorchestratorPrompt: Custom orchestrator prompt overrideoptions: Provider-specific model optionsdisplayName: Custom display name for the agentpresets: Named council presets (map of presetName → CouncillorConfig[])timeout: Council execution timeout in msdefault_preset: Default preset name to usecouncillor_execution_mode: "parallel" or "serial" executioncouncillor_retries: Number of retry attempts for empty responsestype: "auto", "tmux", "zellij", or "none"layout: Pane layout (main-horizontal, main-vertical, tiled, even-horizontal, even-vertical)main_pane_size: Percentage for main pane (20-80)zellij_pane_mode: "agent-tab" or "current-tab"{env:VAR_NAME}: Interpolated in config files during parsingOH_MY_OPENCODE_SLIM_PRESET: Overrides config.preset at runtimetmux.enabled is automatically migrated to multiplexer.type = 'tmux'councillors format in presets is automatically unwrappedmaster field in council config is accepted but ignored (council agent synthesizes directly)onWarning callback for programmatic handlingConfiguration loading is tested via:
src/config/loader.test.ts: Config file discovery, parsing, merging, and validationsrc/config/schema.test.ts: Schema validation and type inferencesrc/config/utils.test.ts: Agent configuration utilitiessrc/config/agent-mcps.test.ts: MCP permission resolutionsrc/config/council-schema.test.ts: Council configuration validation