Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Model: claude-opus-4-6 (anthropic/claude-opus-4-6) Generated: 2026-04-03 Book: Claude Code VS OpenCode: Architecture, Design and The Road Ahead 章节: 第12章 — 解剖一个13万行代码的插件 Token Usage: ~120,000 input + ~6,200 output

12.6 配置分层

为什么配置系统重要?

41 个 hook、26 个工具、11 个智能体——如果没有好的配置系统,用户根本无法管理。想象 100 个按钮的遥控器没有说明书、没有分组、按错一个全部失灵。


三层配置模型

📁 文件说明:src/plugin-config.ts 配置加载实现。从多个位置读取、合并、校验,并处理配置损坏。

flowchart TD
    D["Layer 1: Defaults<br/>Code defaults + schema optionals"] --> U
    U["Layer 2: User Config<br/>~/.config/opencode/oh-my-opencode.jsonc"] --> P
    P["Layer 3: Project Config<br/>.opencode/oh-my-opencode.jsonc"]
    P --> Final["Final Effective Config"]
    U -->|"overridden by"| Final
    D -->|"fallback"| Final
层级类比作用域
默认值出厂设置兜底
用户级个人偏好所有项目
项目级项目特殊要求仅当前项目

OMO 三层 + OpenCode 宿主配置 = 共存:

graph TD
    subgraph OMO_Config["OMO 3 Layers"]
        L1["defaults"]
        L2["user config"]
        L3["project config"]
    end
    subgraph OC_Config["OpenCode Host Config"]
        H1["agents"]
        H2["tools"]
        H3["mcp"]
        H4["commands"]
        H5["permissions"]
        H6["providers"]
    end
    OMO_Config -->|"config hook<br/>injects into"| OC_Config

22 个 Schema 文件

📁 文件说明:src/config/schema/ 目录 22 个 Zod v4 schema 文件,按责任边界拆分。

graph TD
    Root["oh-my-opencode-config.ts"]

    Root --> Agents["Agent Schemas x4<br/>names, overrides, sisyphus..."]
    Root --> Tasks["Task Schemas x2<br/>background-task, babysitting"]
    Root --> Categories["categories.ts"]
    Root --> Hooks["hooks.ts - 41 hook names"]
    Root --> Skills["skills.ts"]
    Root --> Commands["commands.ts"]
    Root --> CC["claude-code.ts"]
    Root --> Exp["experimental.ts"]
    Root --> Tmux["tmux.ts"]
    Root --> Other["notification, git-master<br/>browser, websearch..."]

总 schema 结构:

📁 文件说明:src/config/schema/oh-my-opencode-config.ts 汇总 22 个子域的总 schema。

graph TD
    Config["Root Config"]

    Config --> Disable["Global Disable Lists"]
    Disable --> D1["disabled_mcps"]
    Disable --> D2["disabled_agents"]
    Disable --> D3["disabled_skills"]
    Disable --> D4["disabled_hooks"]
    Disable --> D5["disabled_commands"]

    Config --> Nested["Nested Sub-configs"]
    Nested --> N1["agents"]
    Nested --> N2["categories"]
    Nested --> N3["claude_code"]
    Nested --> N4["skills"]
    Nested --> N5["experimental"]

    Config --> Infra["Infrastructure"]
    Infra --> I1["background_task"]
    Infra --> I2["notification"]
    Infra --> I3["git_master"]
    Infra --> I4["tmux"]

JSONC 支持

配置支持 JSONC(JSON with Comments)——几十个开关的文件如果不能写注释,几个月后就忘了每个配置是什么意思。

{
  // Disable Atlas (not needed for this project)
  "disabled_agents": ["atlas"],

  // Limit concurrency (API quota limited)
  "background_task": {
    "defaultConcurrency": 3
  }
}

注释就是文档。OMO 通过 parseJsonc() 解析,优先查找 .jsonc 后缀。


部分回退(Partial Fallback)

💡 CS 术语:不是整个系统退回默认,而是只让出问题的部分降级。

flowchart TD
    Load["Read Config File"] --> Full["safeParse() - Full Validation"]
    Full --> OK{All Valid?}
    OK -->|Yes| Use["Use Full Config"]
    OK -->|No| Log["Log Error"]
    Log --> Partial["parseConfigPartially()"]

    Partial --> S1{agents OK?}
    S1 -->|Yes| K1["Keep agents"]
    S1 -->|No| SK1["Skip, use defaults"]

    Partial --> S2{hooks OK?}
    S2 -->|Yes| K2["Keep hooks"]
    S2 -->|No| SK2["Skip, use defaults"]

    Partial --> S3{experimental OK?}
    S3 -->|Yes| K3["Keep experimental"]
    S3 -->|No| SK3["Skip, use defaults"]

    K1 & SK1 & K2 & SK2 & K3 & SK3 --> Result["Merged Partially-valid Config"]

场景:experimental 里写了过时的字段名。没有部分回退时,整个配置文件无效,精心配置的 agents、hooks 全部丢失。有了部分回退,只跳过坏掉的 experimental,其他照常。


合并逻辑

flowchart TD
    subgraph Objects["Object Fields"]
        OB["agents, categories, claude_code"]
        OB --> DM["deepMerge()<br/>Project overrides specific fields"]
    end

    subgraph Lists["Disable Lists"]
        LS["disabled_agents, disabled_hooks..."]
        LS --> SA["Set union + dedup<br/>Both say disable = disabled"]
    end
字段类型合并方式原因
对象字段deepMerge项目级只覆盖部分字段
禁用列表Set 去重累加两层都说“不要“→最终“不要“

从配置到运行策略

配置不是读完就结束。OMO 把“静态配置“编译成“运行态策略“:

flowchart LR
    subgraph Static["Config File"]
        S1["tmux: main-vertical"]
        S2["disabled_hooks: atlas"]
        S3["defaultConcurrency: 3"]
    end
    subgraph Runtime["Runtime Objects"]
        R1["tmuxConfig object<br/>all defaults filled"]
        R2["isHookEnabled() fn<br/>one call = answer"]
        R3["ConcurrencyManager<br/>initialized with limit=3"]
    end
    Static -->|"compile"| Runtime

横向合并——跨生态资产

flowchart LR
    S1["Builtin Cmds"] --> M["applyCommandConfig()"]
    S2["Skill Cmds"] --> M
    S3["CC Cmds"] --> M
    S4["OC Cmds"] --> M
    S5["Plugin Cmds"] --> M
    M --> Final["Merged Command Registry"]

本节要点

  • 三层优先级:默认 → 用户级 → 项目级
  • 22 个 Schema:按责任边界拆分,类型严格
  • JSONC:注释是长期维护的基础设施
  • 部分回退:一个段落坏了不拖垮其他
  • 禁用列表累加:两层都说“不要“→最终“不要“
  • 配置到策略:静态文件被编译成运行态对象