설정 시스템 (Configuration System)
관련 소스 파일
목적 및 범위
설정 시스템은 oh-my-opencode 플러그인에 대한 사용자 기본 설정의 로드, 검증, 병합 및 적용을 관리합니다. 이 시스템은 프로젝트 수준의 설정이 사용자 수준의 기본 설정보다 우선하는 2단계 오버라이드(Override, 덮어쓰기) 계층 구조를 구현하며, Zod를 사용한 스키마 검증(Schema validation)을 제공하여 타입 안정성(Type safety)을 보장하고 설정 오류를 조기에 발견합니다.
이 페이지에서는 설정 로드 메커니즘, 스키마 구조 및 병합 전략을 다룹니다. 특정 에이전트 구성에 대한 정보는 에이전트 설정을 참조하십시오. Claude Code 호환성 토글에 대해서는 설정 로딩을 참조하십시오.
설정 파일 위치
시스템은 플랫폼별 관례에 따라 다음 두 위치에서 설정 파일을 검색합니다.
| 우선순위 | 위치 | 플랫폼별 동작 |
|---|---|---|
| 1 (최고) | .opencode/oh-my-opencode.json |
현재 작업 디렉토리의 프로젝트 전용 설정 |
| 2 (기본) | ~/.config/opencode/oh-my-opencode.json |
사용자 수준 설정 (XDG를 통한 Linux/macOS) |
| 2 (기본) | %APPDATA%\opencode\oh-my-opencode.json |
사용자 수준 설정 (Windows) |
getUserConfigDir() 함수는 운영 체제에 따라 올바른 경로를 결정합니다.
Windows: process.env.APPDATA || %USERPROFILE%\AppData\Roaming
Unix-like: process.env.XDG_CONFIG_HOME || ~/.config
설정 로딩 파이프라인 (Configuration Loading Pipeline)
로드 및 병합 흐름도
flowchart TD
Start["loadPluginConfig(directory)"]
GetUserDir["getUserConfigDir()"]
UserPath["userConfigPath<br>~/.config/opencode/oh-my-opencode.json"]
ProjectPath["projectConfigPath<br>.opencode/oh-my-opencode.json"]
LoadUser["loadConfigFromPath(userConfigPath)"]
LoadProject["loadConfigFromPath(projectConfigPath)"]
UserConfig["userConfig: OhMyOpenCodeConfig | null"]
ProjectConfig["projectConfig: OhMyOpenCodeConfig | null"]
InitBase["config = userConfig ?? {}"]
CheckProject["projectConfig exists?"]
MergeStep["mergeConfigs(config, projectConfig)"]
FinalConfig["Final merged config"]
Start -.-> GetUserDir
GetUserDir -.-> UserPath
Start -.-> ProjectPath
UserPath -.-> LoadUser
ProjectPath -.-> LoadProject
LoadUser -.-> UserConfig
LoadProject -.-> ProjectConfig
UserConfig -.-> InitBase
InitBase -.-> CheckProject
ProjectConfig -.-> CheckProject
CheckProject -.->|"Yes"| MergeStep
CheckProject -.->|"No"| FinalConfig
MergeStep -.-> FinalConfig
로드 함수 구현
loadConfigFromPath() 함수는 각 설정 파일에 대해 네 가지 작업을 수행합니다.
- 파일 존재 확인:
fs.existsSync()를 사용하여 파일이 존재하는지 확인합니다. - JSON 파싱: 파일 내용을 읽고 파싱합니다.
- 에이전트 이름 정규화:
AGENT_NAME_MAP을 적용하여 에이전트 키를 표준화합니다. - 스키마 검증:
OhMyOpenCodeConfigSchema.safeParse()로 검증합니다.
어느 단계에서든 실패하면 함수는 null을 반환하고 오류를 기록합니다. 검증 오류에는 Zod의 상세한 이슈 정보가 포함됩니다.
설정 스키마 참조 (Configuration Schema Reference)
최상위 스키마 구조
OhMyOpenCodeConfigSchema는 일곱 개의 설정 도메인을 정의합니다.
flowchart TD
Schema["OhMyOpenCodeConfigSchema"]
DisabledMcps["disabled_mcps<br>string[]"]
DisabledAgents["disabled_agents<br>BuiltinAgentName[]"]
DisabledHooks["disabled_hooks<br>HookName[]"]
Agents["agents<br>AgentOverrides"]
ClaudeCode["claude_code<br>ClaudeCodeConfig"]
GoogleAuth["google_auth<br>boolean"]
OmoAgent["omo_agent<br>OmoAgentConfig"]
Schema -.-> DisabledMcps
Schema -.-> DisabledAgents
Schema -.-> DisabledHooks
Schema -.-> Agents
Schema -.-> ClaudeCode
Schema -.-> GoogleAuth
Schema -.-> OmoAgent
소스: src/config/schema.ts L108-L117
전체 스키마 분석
| 필드 | 타입 | 목적 | 기본값 |
|---|---|---|---|
$schema |
string (선택 사항) |
IDE 지원을 위한 JSON 스키마 참조 | - |
disabled_mcps |
McpName[] |
비활성화할 MCP 서버 목록 | [] |
disabled_agents |
BuiltinAgentName[] |
비활성화할 내장 에이전트 목록 | [] |
disabled_hooks |
HookName[] |
비활성화할 훅(Hook) 목록 | [] |
agents |
AgentOverrides |
에이전트별 설정 오버라이드 | {} |
claude_code.mcp |
boolean |
~/.claude/.mcp.json에서 MCP 설정 로드 여부 |
true |
claude_code.commands |
boolean |
~/.claude/commands/에서 명령 로드 여부 |
true |
claude_code.skills |
boolean |
~/.claude/skills/에서 스킬 로드 여부 |
true |
claude_code.agents |
boolean |
~/.claude/agents/에서 에이전트 로드 여부 |
true |
claude_code.hooks |
boolean |
~/.claude/settings.json에서 외부 훅 로드 여부 |
true |
google_auth |
boolean |
Google Antigravity 인증 플러그인 활성화 여부 | false |
omo_agent.disabled |
boolean |
OmO 오케스트레이터 에이전트 비활성화 여부 | false |
소스: src/config/schema.ts L108-L117
src/config/schema.ts L104-L106
에이전트 오버라이드 스키마
AgentOverrideConfigSchema는 에이전트 설정의 부분적인 오버라이드를 허용합니다.
| 필드 | 타입 | 제약 조건 | 설명 |
|---|---|---|---|
model |
string |
- | AI 모델 오버라이드 (예: "claude-opus-4-5") |
temperature |
number |
0.0 ~ 2.0 |
샘플링 온도 |
top_p |
number |
0.0 ~ 1.0 |
핵 샘플링(Nucleus sampling) 파라미터 |
prompt |
string |
- | 시스템 프롬프트 오버라이드 |
tools |
Record<string, boolean> |
- | 특정 도구 활성화/비활성화 |
disable |
boolean |
- | 이 에이전트를 완전히 비활성화 |
description |
string |
- | UI에 표시되는 에이전트 설명 |
mode |
"subagent" | "primary" | "all" |
- | 에이전트 가용성 모드 |
color |
string |
Hex 형식 #RRGGBB |
이 에이전트의 UI 색상 |
permission |
AgentPermissionSchema |
- | 세밀한 권한 제어 |
소스: src/config/schema.ts L67-L81
오버라이드 가능한 에이전트
AgentOverridesSchema는 다음 에이전트들에 대한 오버라이드를 지원합니다.
type OverridableAgents =
| "build" // OpenCode의 내장 빌드 에이전트
| "plan" // OpenCode의 내장 플랜 에이전트
| "OmO" // 기본 오케스트레이터
| "OmO-Plan" // OmO의 플래닝 변형
| "oracle" // 전문가 자문가
| "librarian" // 외부 리서치 에이전트
| "explore" // 코드베이스 탐색 에이전트
| "frontend-ui-ux-engineer" // UI/UX 구현 에이전트
| "document-writer" // 문서화 에이전트
| "multimodal-looker" // 시각적 분석 에이전트
소스: src/config/schema.ts L83-L94
훅 이름 (Hook Names)
HookNameSchema는 설정 가능한 19개의 모든 훅을 나열합니다.
type HookName =
| "todo-continuation-enforcer"
| "context-window-monitor"
| "session-recovery"
| "session-notification"
| "comment-checker"
| "grep-output-truncator"
| "tool-output-truncator"
| "directory-agents-injector"
| "directory-readme-injector"
| "empty-task-response-detector"
| "think-mode"
| "anthropic-auto-compact"
| "rules-injector"
| "background-notification"
| "auto-update-checker"
| "startup-toast"
| "keyword-detector"
| "agent-usage-reminder"
| "non-interactive-env"
| "interactive-bash-session"
소스: src/config/schema.ts L44-L65
병합 전략 (Merge Strategy)
설정 병합 알고리즘
mergeConfigs() 함수는 두 개의 OhMyOpenCodeConfig 객체를 필드별로 다른 전략을 사용하여 결합합니다.
flowchart TD
Base["base: OhMyOpenCodeConfig"]
Override["override: OhMyOpenCodeConfig"]
Spread1["Spread base properties"]
Spread2["Spread override properties"]
AgentsMerge["deepMerge(base.agents, override.agents)"]
DisabledAgentsUnion["Set union of disabled_agents"]
DisabledMcpsUnion["Set union of disabled_mcps"]
DisabledHooksUnion["Set union of disabled_hooks"]
ClaudeCodeMerge["deepMerge(base.claude_code, override.claude_code)"]
Result["Merged OhMyOpenCodeConfig"]
Base -.-> Spread1
Override -.-> Spread2
Spread1 -.-> Result
Spread2 -.-> Result
Base -.-> AgentsMerge
Override -.-> AgentsMerge
AgentsMerge -.-> Result
Base -.-> DisabledAgentsUnion
Override -.-> DisabledAgentsUnion
DisabledAgentsUnion -.-> Result
Base -.-> DisabledMcpsUnion
Override -.-> DisabledMcpsUnion
DisabledMcpsUnion -.-> Result
Base -.-> DisabledHooksUnion
Override -.-> DisabledHooksUnion
DisabledHooksUnion -.-> Result
Base -.-> ClaudeCodeMerge
Override -.-> ClaudeCodeMerge
ClaudeCodeMerge -.-> Result
필드 타입별 병합 동작
| 필드 타입 | 병합 전략 | 근거 |
|---|---|---|
스칼라 필드 (Scalar fields) (예: google_auth, omo_agent) |
오버라이드가 베이스를 대체 | 단순한 오버라이드 의미론 적용 |
중첩된 객체 (Nested objects) (agents, claude_code) |
deepMerge() |
전체 구조를 대체하지 않고 부분적인 오버라이드 허용 |
배열 (Arrays) (disabled_mcps, disabled_agents, disabled_hooks) |
[...new Set([...base, ...override])]를 통한 집합 합집합 |
두 설정 모두에서 비활성화된 항목을 누적 |
딥 머지(Deep Merge) 구현
deepMerge() 함수(src/shared 위치)는 재귀적 병합을 수행합니다.
- 일반 객체: 모든 키를 재귀적으로 병합합니다.
- 배열: 대체(병합 아님) - 오버라이드 배열이 우선합니다.
- 원시 타입(Primitives): 오버라이드 값이 우선합니다.
- undefined 값: 베이스 값이 유지됩니다.
이를 통해 다음과 같은 패턴이 가능해집니다.
// 사용자 설정 (User config)
{
"agents": {
"OmO": {
"model": "claude-opus-4-5",
"temperature": 1.0,
"prompt": "Base prompt..."
}
}
}
// 프로젝트 설정 (온도만 오버라이드)
{
"agents": {
"OmO": {
"temperature": 0.7
}
}
}
// 결과: model과 prompt는 유지되고, temperature만 오버라이드됨
에이전트 이름 정규화 (Agent Name Normalization)
에이전트 이름 매핑
AGENT_NAME_MAP은 대소문자를 구분하지 않는 키 정규화를 제공합니다.
const AGENT_NAME_MAP: Record<string, string> = {
omo: "OmO",
build: "build",
oracle: "oracle",
librarian: "librarian",
explore: "explore",
"frontend-ui-ux-engineer": "frontend-ui-ux-engineer",
"document-writer": "document-writer",
"multimodal-looker": "multimodal-looker",
}
normalizeAgentNames() 함수는 검증 전에 설정 키에 이 매핑을 적용하여, 사용자가 설정 파일에서 "OmO" 대신 "omo"라고 작성할 수 있도록 합니다.
플러그인 수명 주기에서의 설정 사용
설정 적용 파이프라인
flowchart TD
LoadConfig["loadPluginConfig(ctx.directory)"]
PluginConfig["pluginConfig: OhMyOpenCodeConfig"]
DisabledHooks["disabledHooks = new Set(pluginConfig.disabled_hooks)"]
IsEnabled["isHookEnabled(hookName)"]
CreateHooks["Create hooks conditionally<br>todoContinuationEnforcer<br>contextWindowMonitor<br>sessionRecovery<br>..."]
ConfigHandler["config: async (config) => {...}"]
CreateAgents["createBuiltinAgents(disabled_agents, agents, directory)"]
LoadClaudeCode["Load Claude Code configs<br>loadUserAgents()<br>loadMcpConfigs()<br>loadUserCommands()"]
MergeIntoConfig["Merge into config.agent<br>config.mcp<br>config.command"]
EventHandler["event: async (input) => {...}"]
ToolBefore["tool.execute.before"]
ToolAfter["tool.execute.after"]
HookExecution["Execute enabled hooks<br>on each lifecycle event"]
LoadConfig -.-> PluginConfig
PluginConfig -.-> DisabledHooks
DisabledHooks -.-> IsEnabled
IsEnabled -.-> CreateHooks
PluginConfig -.-> ConfigHandler
ConfigHandler -.-> CreateAgents
ConfigHandler -.-> LoadClaudeCode
CreateAgents -.-> MergeIntoConfig
LoadClaudeCode -.-> MergeIntoConfig
CreateHooks -.-> EventHandler
CreateHooks -.-> ToolBefore
CreateHooks -.-> ToolAfter
EventHandler -.-> HookExecution
ToolBefore -.-> HookExecution
ToolAfter -.-> HookExecution
훅 초기화 패턴
각 훅은 조건부 초기화 패턴을 따릅니다.
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer")
? createTodoContinuationEnforcer(ctx)
: null;
const contextWindowMonitor = isHookEnabled("context-window-monitor")
? createContextWindowMonitorHook(ctx)
: null;
비활성화된 훅은 null을 반환하며 수명 주기 핸들러에서 호출되지 않습니다. 이 패턴은 src/index.ts L185-L248 전반에 걸쳐 나타납니다.
설정을 통한 에이전트 초기화
config 수명 주기 핸들러는 pluginConfig를 사용하여 에이전트를 초기화합니다.
const builtinAgents = createBuiltinAgents(
pluginConfig.disabled_agents,
pluginConfig.agents,
ctx.directory,
);
createBuiltinAgents() 함수는 세 가지 변환을 적용합니다.
- 필터링:
disabled_agents배열에 포함된 에이전트를 제외합니다. - 환경 주입: OmO 및 librarian 프롬프트에
createEnvContext()를 추가합니다. - 오버라이드 병합:
mergeAgentConfig()를 사용하여agentOverrides를 적용합니다.
Claude Code 호환성 레이어
claude_code 토글은 ~/.claude/* 디렉토리에서의 로딩을 제어합니다.
const userAgents = (pluginConfig.claude_code?.agents ?? true)
? loadUserAgents()
: {};
const mcpResult = (pluginConfig.claude_code?.mcp ?? true)
? await loadMcpConfigs()
: { servers: {} };
const userCommands = (pluginConfig.claude_code?.commands ?? true)
? loadUserCommands()
: {};
모든 토글은 지정되지 않은 경우(?? true) 기본값이 true로 설정되어 하위 호환성을 보장합니다.
검증 및 오류 처리 (Validation and Error Handling)
Zod 스키마 검증
loadConfigFromPath() 함수는 OhMyOpenCodeConfigSchema.safeParse()를 사용합니다.
const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
if (!result.success) {
log(`Config validation error in ${configPath}:`, result.error.issues);
return null;
}
검증 실패 시:
- 상세한 오류 정보가
result.error.issues를 통해 기록됩니다. - 함수는
null을 반환합니다. - 플러그인은 남은 설정(사용자 설정 또는 빈 객체)으로 계속 진행합니다.
- 치명적인 오류가 발생하지 않으며, 우아한 성능 저하(Graceful degradation)를 유지합니다.
검증 오류 예시
Zod는 구조화된 오류를 제공합니다.
| 오류 유형 | 예시 | 오류 메시지 |
|---|---|---|
| 잘못된 타입 | {"temperature": "hot"} |
Expected number, received string |
| 범위를 벗어남 | {"temperature": 3.0} |
Number must be less than or equal to 2 |
| 잘못된 열거형(Enum) | {"disabled_hooks": ["fake-hook"]} |
Invalid enum value |
| 잘못된 형식 | {"color": "#XYZ"} |
Invalid color format |
소스: src/config/schema.ts L67-L81
설정-코드 엔티티 매핑 (Configuration-to-Code Entity Mapping)
주요 함수 및 데이터 구조
flowchart TD
AGENT_NAME_MAP["AGENT_NAME_MAP"]
allBuiltinAgents["allBuiltinAgents"]
OhMyOpenCodeConfigSchema["OhMyOpenCodeConfigSchema"]
AgentOverridesSchema["AgentOverridesSchema"]
HookNameSchema["HookNameSchema"]
isHookEnabled["isHookEnabled(hookName)"]
createBuiltinAgents["createBuiltinAgents(disabled, overrides, dir)"]
createEnvContext["createEnvContext(directory)"]
mergeAgentConfig["mergeAgentConfig(base, override)"]
mergeConfigs["mergeConfigs(base, override)"]
deepMerge["deepMerge(base, override)"]
getUserConfigDir["getUserConfigDir()"]
loadConfigFromPath["loadConfigFromPath(path)"]
normalizeAgentNames["normalizeAgentNames(agents)"]
loadPluginConfig["loadPluginConfig(directory)"]
subgraph Constants ["Constants"]
AGENT_NAME_MAP
allBuiltinAgents
end
subgraph subGraph3 ["Schema Types"]
OhMyOpenCodeConfigSchema
AgentOverridesSchema
HookNameSchema
end
subgraph subGraph2 ["Config Application"]
isHookEnabled
createBuiltinAgents
createEnvContext
mergeAgentConfig
end
subgraph subGraph1 ["Config Merging"]
mergeConfigs
deepMerge
end
subgraph subGraph0 ["Config Loading"]
getUserConfigDir
loadConfigFromPath
normalizeAgentNames
loadPluginConfig
end
설정 파일에서 런타임 설정으로의 흐름
flowchart TD
UserFile["~/.config/opencode/oh-my-opencode.json<br>(Raw JSON file)"]
ProjectFile[".opencode/oh-my-opencode.json<br>(Raw JSON file)"]
LoadUser["loadConfigFromPath(userConfigPath)<br>Returns: OhMyOpenCodeConfig | null"]
LoadProject["loadConfigFromPath(projectConfigPath)<br>Returns: OhMyOpenCodeConfig | null"]
Normalize["normalizeAgentNames(rawConfig.agents)<br>Applies: AGENT_NAME_MAP"]
Validate["OhMyOpenCodeConfigSchema.safeParse()<br>Returns: SafeParseResult"]
BaseConfig["config: OhMyOpenCodeConfig<br>(user config or empty object)"]
MergedConfig["mergedConfig: OhMyOpenCodeConfig<br>(after mergeConfigs)"]
DisabledHooks["disabledHooks: Set<HookName><br>new Set(config.disabled_hooks)"]
CreateHooksCall["createTodoContinuationEnforcer(ctx)<br>createContextWindowMonitorHook(ctx)<br>..."]
CreateAgentsCall["createBuiltinAgents(<br> disabled_agents,<br> agents,<br> directory<br>)"]
FinalAgents["config.agent = {...}<br>(merged into OpenCode config)"]
UserFile -.-> LoadUser
ProjectFile -.-> LoadProject
LoadUser -.-> Normalize
LoadProject -.-> Normalize
Normalize -.-> Validate
Validate -.-> BaseConfig
BaseConfig -.-> MergedConfig
LoadProject -.-> MergedConfig
MergedConfig -.-> DisabledHooks
MergedConfig -.-> CreateAgentsCall
DisabledHooks -.-> CreateHooksCall
CreateAgentsCall -.-> FinalAgents
설정 예시
모든 기능을 포함한 전체 예시
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/main/oh-my-opencode-config-schema.json",
"disabled_mcps": ["filesystem", "memory"],
"disabled_agents": ["multimodal-looker"],
"disabled_hooks": ["startup-toast", "auto-update-checker"],
"agents": {
"OmO": {
"temperature": 0.7,
"description": "Custom OmO orchestrator"
},
"explore": {
"model": "grok-code-2",
"temperature": 0.3
},
"oracle": {
"prompt": "Custom oracle prompt with additional guidelines..."
}
},
"claude_code": {
"mcp": true,
"commands": true,
"skills": false,
"agents": false,
"hooks": true
},
"google_auth": true,
"omo_agent": {
"disabled": false
}
}
소스: src/config/schema.ts L108-L117
설정 우선순위 규칙
오버라이드 우선순위 요약
- 프로젝트 설정 (
.opencode/oh-my-opencode.json)이 사용자 설정을 오버라이드합니다. - 사용자 설정 (
~/.config/opencode/oh-my-opencode.json)이 기본값을 제공합니다. - 설정이 존재하지 않을 경우 내장 기본값이 적용됩니다.
필드별 병합 의미론
- 원시 타입 오버라이드: 마지막에 적용된 것이 승리합니다 (프로젝트 > 사용자 > 기본값).
- 객체 오버라이드: 프로젝트 설정이 우선순위를 갖는 딥 머지가 수행됩니다.
- 배열 누적:
disabled_*배열에 대해 집합 합집합이 수행됩니다. - 도구 권한: 얕은 병합(Shallow merge)이 수행됩니다 (프로젝트 도구가 사용자 도구를 오버라이드).