# Config Module Codemap ## Responsibility The `src/config/` module is responsible for: 1. **Schema Definition**: Type-safe configuration validation using Zod schemas 2. **Configuration Loading**: Loading, validating, and merging plugin configuration from multiple sources (user config, project config, environment variables) 3. **Constants Management**: Centralizing agent names, default models, delegation rules, polling intervals, and timeouts ## Design ### Key Patterns **Multi-Source Configuration Merging** - User config: `~/.config/opencode/oh-my-opencode-slim.jsonc` (preferred) or `.json` - Project config: `/.opencode/oh-my-opencode-slim.jsonc` (preferred) or `.json` - Environment override: `OH_MY_OPENCODE_SLIM_PRESET` - Project config takes precedence over user config - Nested objects (`agents`, `tmux`, `fallback`) are deep-merged; arrays and primitives are replaced **Preset System** - Named presets contain agent configuration templates - Presets are merged with root-level agent config (root overrides) - Supports preset selection via config file or environment variable **Subagent Delegation Rules** - Explicitly defines which agents can spawn which subagents - `orchestrator`: can spawn all subagents (full delegation) - `fixer`: leaf node — prompt forbids delegation - `designer`: leaf node (cannot spawn subagents) - `explorer`, `librarian`, `oracle`: leaf nodes (cannot spawn subagents) ### Core Abstractions **Configuration Schema Hierarchy** ``` PluginConfig ├── preset?: string ├── setDefaultAgent?: boolean ├── scoringEngineVersion?: 'v1' | 'v2-shadow' | 'v2' ├── balanceProviderUsage?: boolean ├── manualPlan?: ManualPlan ├── presets?: Record ├── agents?: Record ├── disabled_mcps?: string[] ├── tmux?: TmuxConfig ├── background?: BackgroundTaskConfig └── fallback?: FailoverConfig AgentOverrideConfig ├── model?: string | ModelEntry[] ├── temperature?: number ├── variant?: string ├── skills?: string[] // "*" = all, "!item" = exclude └── mcps?: string[] // "*" = all, "!item" = exclude TmuxConfig ├── enabled: boolean ├── layout: TmuxLayout └── main_pane_size: number FailoverConfig ├── enabled: boolean ├── timeoutMs: number ├── retryDelayMs: number └── chains: Record ``` **Agent Names** - `ORCHESTRATOR_NAME`: `'orchestrator'` - `SUBAGENT_NAMES`: `['explorer', 'librarian', 'oracle', 'designer', 'fixer']` - `ALL_AGENT_NAMES`: `['orchestrator', 'explorer', 'librarian', 'oracle', 'designer', 'fixer']` - `AGENT_ALIASES`: Legacy name mappings (`{ explore: 'explorer' }`) **TypeScript Types** - `PluginConfig`: Main configuration object - `AgentOverrideConfig`: Per-agent configuration overrides - `TmuxConfig`: Tmux integration settings - `TmuxLayout`: Layout enum (`main-horizontal`, `main-vertical`, `tiled`, `even-horizontal`, `even-vertical`) - `Preset`: Named agent configuration presets - `AgentName`: Union type of all agent names - `McpName`: Union type of available MCPs (`'websearch'`, `'context7'`, `'grep_app'`) - `BackgroundTaskConfig`: Background task concurrency settings - `FailoverConfig`: Failover behavior configuration - `ModelEntry`: Normalized model entry with optional per-model variant (`{ id: string; variant?: string }`) - `ManualAgentName`: Union type for manual agent configuration - `ManualPlan`: Full manual planning configuration ## Flow ### Configuration Loading Flow ``` loadPluginConfig(directory) │ ├─→ Find user config path │ └─→ findConfigPath(~/.config/opencode/oh-my-opencode-slim) │ └─→ Prefers .jsonc over .json │ ├─→ Load user config with loadConfigFromPath() │ └─→ stripJsonComments() → JSON.parse() │ └─→ PluginConfigSchema.safeParse() │ └─→ Returns null if invalid/missing │ ├─→ Find project config path │ └─→ findConfigPath(/.opencode/oh-my-opencode-slim) │ ├─→ Load project config (same validation) │ ├─→ Deep merge configs (project overrides user) │ ├─→ Top-level: project replaces user │ └─→ Nested (agents, tmux, fallback): deepMerge() │ ├─→ Apply environment preset override │ └─→ OH_MY_OPENCODE_SLIM_PRESET takes precedence │ └─→ Resolve and merge preset ├─→ Find preset in config.presets[preset] ├─→ Deep merge preset agents with root agents └─→ Warn if preset not found ``` ### Deep Merge Algorithm ``` deepMerge(base?, override?) │ ├─→ If base is undefined → return override ├─→ If override is undefined → return base │ └─→ For each key in override ├─→ If both values are non-null, non-array objects │ └─→ Recursively deepMerge └─→ Otherwise → override replaces base ``` ### Prompt Loading Flow ``` loadAgentPrompt(agentName, preset?) │ ├─→ Validate preset name (alphanumeric + underscore/dash) │ ├─→ Build prompt search dirs │ ├─→ If preset is safe: │ │ 1) ~/.config/opencode/oh-my-opencode-slim/{preset} │ │ 2) ~/.config/opencode/oh-my-opencode-slim │ └─→ Otherwise: │ 1) ~/.config/opencode/oh-my-opencode-slim │ ├─→ Read first existing {agentName}.md from search dirs │ └─→ If found → replacement prompt │ └─→ Read first existing {agentName}_append.md from search dirs └─→ If found → append prompt ``` ## Integration ### Dependencies **External Dependencies** - `zod`: Runtime schema validation - `node:fs`, `node:path`: File system operations **Internal Dependencies** - `src/cli/config-io.ts` - JSONC comment stripping utility (`stripJsonComments`) - `src/cli/paths.ts` - Config directory resolution (`getConfigDir`) ### Consumers **Direct Consumers** - `src/index.ts` - Main plugin entry point (imports configuration) - `src/agents/index.ts` - Agent configuration and initialization - `src/cli/providers.ts` - CLI provider resolution ## File Organization ``` src/config/ ├── loader.ts # Config loading, merging, and prompt loading ├── schema.ts # Zod schemas and TypeScript types └── constants.ts # Agent names, defaults, timeouts, delegation rules ``` ## Constants Reference ### Polling Configuration - `POLL_INTERVAL_MS` (500ms): Standard polling interval - `POLL_INTERVAL_SLOW_MS` (1000ms): Slower polling for background tasks - `POLL_INTERVAL_BACKGROUND_MS` (2000ms): Background task polling ### Timeouts - `DEFAULT_TIMEOUT_MS` (2 minutes): Default operation timeout - `MAX_POLL_TIME_MS` (5 minutes): Maximum polling duration - `FALLBACK_FAILOVER_TIMEOUT_MS` (15 seconds): Failover timeout ### Stability - `STABLE_POLLS_THRESHOLD` (3): Number of stable polls before considering state settled ### Default Models | Agent | Default Model | |------------|-------------------------| | orchestrator | runtime-resolved | | oracle | openai/gpt-5.4 | | librarian | openai/gpt-5.4-mini | | explorer | openai/gpt-5.4-mini | | designer | openai/gpt-5.4-mini | | fixer | openai/gpt-5.4-mini | ### Delegation Rules | Parent Agent | Can Spawn | |--------------|-------------------------------| | orchestrator | explorer, librarian, oracle, designer, fixer | | fixer | (none - leaf node) | | designer | (none - leaf node) | | explorer | (none - leaf node) | | librarian | (none - leaf node) | | oracle | (none - leaf node) | ## Error Handling **Configuration Loading** - Missing config files: Returns empty config (expected behavior) - Invalid JSON/JSONC: Logs warning, returns null - Schema validation failure: Logs detailed Zod error format, returns null - File read errors (non-ENOENT): Logs warning, returns null **Prompt Loading** - Missing prompt files: Returns empty object (expected behavior) - File read errors: Logs warning, continues to next search path **Preset Resolution** - Invalid preset name (contains unsafe characters): Ignored, uses root config - Missing preset: Logs warning with available presets, continues without preset ## Extension Points **Adding New Agents** 1. Add to `SUBAGENT_NAMES` in `constants.ts` 2. Add default model to `DEFAULT_MODELS` 3. Add to `SUBAGENT_DELEGATION_RULES` 4. Add to schema in `schema.ts` if needed (ManualPlanSchema, FallbackChainsSchema) **Adding New MCPs** 1. Add to `McpNameSchema` enum in `schema.ts` **Adding New Configuration Options** 1. Add to `PluginConfigSchema` in `schema.ts` 2. Update deep merge logic in `loader.ts` if nested object