Asia/Shanghai
May 5, 2026

When Vibe Island called my OpenCode 'Claude'

Vibe Island 把我的 OpenCode 叫成 Claude 了

Mingjian Shao
When Vibe Island called my OpenCode 'Claude'
Three pieces of software all tried to bridge the same events on my Mac. The wrong one won.
I run OpenCode on macOS, with oh-my-openagent (formerly oh-my-opencode) for routing and skill loading, and Vibe Island in the notch as a unified agent dashboard.After a fresh setup, every OpenCode conversation showed up in Vibe Island like this:
  • Source label: Claude
  • Title: [Image 1] dashboard:f5ec2df1-2ae...
  • Click the card to jump back: nothing happens
None of these are accurate. I was running OpenCode, the title was the placeholder text from an image input, and the click should have switched me back to the right Ghostty tab.Each tool auto-configured itself on first launch. Individually fine. Together: a mess.Vibe Island ships a Claude Code hook bundle. On startup it writes entries into ~/.claude/settings.json like:
Json
{
  "hooks": {
    "UserPromptSubmit": [{
      "hooks": [{
        "command": "$HOME/.vibe-island/bin/vibe-island-bridge --source claude",
        "type": "command"
      }]
    }]
  }
}
Every Claude Code event then gets bridged to Vibe Island's Unix socket at ~/.vibe-island/run/vibe-island.sock, tagged with source=claude.oh-my-openagent ships a Claude Code compatibility layer for OpenCode. It reads ~/.claude/settings.json and replays those same hook commands inside OpenCode's plugin runtime, so your Claude Code hooks "just work" in OpenCode.Vibe Island also has a native OpenCode plugin at ~/.config/opencode/plugins/vibe-island.js — but it is only loaded if opencode.json lists it explicitly.When all three are present and opencode.json does NOT list the OpenCode plugin, the path that wins is:
Text
OpenCode event
  → oh-my-openagent compat layer
    → vibe-island-bridge --source claude
      → Vibe Island socket
        → recorded as source=claude
OpenCode events get re-tagged as Claude on the way to the notch. The native OpenCode plugin never runs. The records have no tty field because the bridge had no idea the event came from a different agent.Vibe Island's jump uses tty:bid:tab:split keys. The Claude hook bridge, when run inside OpenCode, has no Bun parent terminal — process.pid is the OpenCode server, not the opencode attach shell. The bridge sets tty=nil, and when you click, Vibe Island logs:
Yaml
jump: bid=nil iterm=nil tmux=false tty=nil ideWin=nil pid=<server-pid>
jump: done 0ms
A 0 ms jump that does nothing is the canonical "we have no idea where you came from" log line.Three small changes:1. Load the OpenCode native plugin, before oh-my-openagent.
Json
{
  "plugin": [
    "./plugins/vibe-island.js",
    "oh-my-openagent@latest"
  ]
}
2. Tell oh-my-openagent's compat layer to skip the Claude vibe-island-bridge inside OpenCode.
Json
{
  "disabledHooks": {
    "PreToolUse":       ["vibe-island-bridge"],
    "PostToolUse":      ["vibe-island-bridge"],
    "UserPromptSubmit": ["vibe-island-bridge"],
    "Stop":             ["vibe-island-bridge"],
    "PreCompact":       ["vibe-island-bridge"]
  }
}
This file goes at ~/.config/opencode/opencode-cc-plugin.json. It scopes the disable to the OpenCode side only — Claude Code's own hook bridge keeps working as Edward shipped it.3. The plugin itself does three real jobs.It walks the process tree to find the matching opencode attach terminal:
Js
const lines = ps.split(/\n/)
  .map((line) => line.match(/^(\d+)\s+(\d+)\s+(\S+)\s+(.+)$/))
  .filter(Boolean)
  .map((m) => ({ pid: +m[1], ppid: +m[2], tty: m[3], args: m[4] }))
  .filter((p) => p.tty !== "??" && /opencode\s+attach/.test(p.args));
const exact = key ? lines.filter((p) => p.args.includes(`--dir ${key}`)) : [];
const chosen = (exact.length ? exact : lines).sort((a, b) => b.pid - a.pid)[0];
It sanitizes prompt text before it ever becomes a notch title:
Js
raw
  .replace(/<auto-slash-command>[\s\S]*?<\/auto-slash-command>/gi, " ")
  .replace(/<command-instruction>[\s\S]*?<\/command-instruction>/gi, " ")
  .replace(/\[Image\s+\d+\]\s*(?:[^\s:]+:[A-Za-z0-9_-]+(?:\s+[A-Za-z0-9_-]+)*)?/gi, " ")
  .trim();
And it tags every payload with source: "opencode" and the resolved tty, so the socket server stops thinking it is talking to Claude Code.The most interesting failure modes in 2026's tooling are not bugs in any one tool — they are wiring conflicts between three tools that all assume they are the bridge.The whole stack is auto-configured, opinionated, and friendly when used in isolation. Stack them and the friendliness becomes ambiguity. Each tool installed itself with the best intentions. None of them coordinated. The user-visible result is that OpenCode "is Claude now."The fix is not clever code. It is making one of the three pieces shut up so the right one can speak.github.com/nxxxsooo/opencode-vibe-island-pluginMIT. Single file. Not affiliated with Vibe Island — Vibe Island is © Edward Luo. If they ship official OpenCode auto-config that detects oh-my-openagent and routes around it, this repo becomes redundant. That is a good outcome.
三个软件在我的 Mac 上同时想"桥接"同一组事件。结果是错的那个赢了。
我在 macOS 上跑 OpenCode,配 oh-my-openagent(前身 oh-my-opencode)做路由和 skill 加载,notch 上挂着 Vibe Island 当统一面板。新装好之后,每个 OpenCode 会话在 Vibe Island 上都长这样:
  • 来源标签:Claude
  • 标题:[Image 1] dashboard:f5ec2df1-2ae...
  • 点卡片跳转:什么也不发生
三个全错。我跑的是 OpenCode;标题是图片输入留下的占位符;点击本来应该把我切回对应的 Ghostty tab。每个工具首次启动都"贴心地"自动配好了自己。单个看都没问题。叠在一起就乱套。Vibe Island 自带 Claude Code hook 包,启动时会写进 ~/.claude/settings.json
Json
{
  "hooks": {
    "UserPromptSubmit": [{
      "hooks": [{
        "command": "$HOME/.vibe-island/bin/vibe-island-bridge --source claude",
        "type": "command"
      }]
    }]
  }
}
Claude Code 的所有事件就被桥到 Vibe Island 的 Unix socket(~/.vibe-island/run/vibe-island.sock),打上 source=claude 标签。oh-my-openagent 给 OpenCode 提供了 Claude Code 兼容层:它读 ~/.claude/settings.json,把那些 hook 命令搬到 OpenCode 的 plugin 运行时里再跑一遍,让你的 Claude Code hook 在 OpenCode 里也"开箱即用"。Vibe Island 还自带一个 OpenCode 原生插件~/.config/opencode/plugins/vibe-island.js),但只有在 opencode.json 显式声明它的时候才会加载。三者都在、opencode.json 又没列出原生插件时,事件实际走的路径是:
Text
OpenCode 事件
  → oh-my-openagent 兼容层
    → vibe-island-bridge --source claude
      → Vibe Island socket
        → 记成 source=claude
OpenCode 事件在去 notch 的路上被重新打上 Claude 标签。原生插件根本没跑。这些记录里也没有 tty 字段,因为 bridge 根本不知道事件来自另一个 agent。Vibe Island 的 jump 用 tty:bid:tab:split 作 key。Claude hook bridge 在 OpenCode 里被调用时,没有自己的 Bun 父终端 —— process.pid 是 OpenCode server,不是 opencode attach 那个 shell。bridge 把 tty 设成 nil,你点击之后 Vibe Island 写出标志性的一行:
Yaml
jump: bid=nil iterm=nil tmux=false tty=nil ideWin=nil pid=<server-pid>
jump: done 0ms
0 ms 就完成的 jump 本质上就是"我不知道你从哪儿来"。三件小事:1. 加载 OpenCode 原生插件,放在 oh-my-openagent 之前。
Json
{
  "plugin": [
    "./plugins/vibe-island.js",
    "oh-my-openagent@latest"
  ]
}
2. 让 oh-my-openagent 的兼容层在 OpenCode 内不要再跑 Claude 那套 vibe-island-bridge。
Json
{
  "disabledHooks": {
    "PreToolUse":       ["vibe-island-bridge"],
    "PostToolUse":      ["vibe-island-bridge"],
    "UserPromptSubmit": ["vibe-island-bridge"],
    "Stop":             ["vibe-island-bridge"],
    "PreCompact":       ["vibe-island-bridge"]
  }
}
这份文件放在 ~/.config/opencode/opencode-cc-plugin.json。它只对 OpenCode 这一侧生效,Claude Code 自己用 Edward 出厂那套 bridge 不受影响。3. 插件本身做三件实事。沿进程树找匹配的 opencode attach 终端:
Js
const lines = ps.split(/\n/)
  .map((line) => line.match(/^(\d+)\s+(\d+)\s+(\S+)\s+(.+)$/))
  .filter(Boolean)
  .map((m) => ({ pid: +m[1], ppid: +m[2], tty: m[3], args: m[4] }))
  .filter((p) => p.tty !== "??" && /opencode\s+attach/.test(p.args));
const exact = key ? lines.filter((p) => p.args.includes(`--dir ${key}`)) : [];
const chosen = (exact.length ? exact : lines).sort((a, b) => b.pid - a.pid)[0];
在 prompt 变成 notch 标题前清掉脚手架噪声:
Js
raw
  .replace(/<auto-slash-command>[\s\S]*?<\/auto-slash-command>/gi, " ")
  .replace(/<command-instruction>[\s\S]*?<\/command-instruction>/gi, " ")
  .replace(/\[Image\s+\d+\]\s*(?:[^\s:]+:[A-Za-z0-9_-]+(?:\s+[A-Za-z0-9_-]+)*)?/gi, " ")
  .trim();
每个事件都带上 source: "opencode" 和解析出来的 tty,让 socket server 不再以为对面是 Claude Code。2026 年最有意思的 bug 不是某一个工具里的代码错误 —— 而是三个工具凑在一起、它们都以为自己是那个"桥"。整个栈各自都自动配置、各自都贴心。叠在一起,贴心就变成歧义。每个工具都"为你考虑"地装好了自己,没有一个互相协调过。用户看到的就是"OpenCode 现在是 Claude 了"。修法不是写出多巧妙的代码,而是让三个里有一个闭嘴,让对的那个说话。github.com/nxxxsooo/opencode-vibe-island-pluginMIT 许可,单文件。本项目不隶属于 Vibe Island —— Vibe Island © Edward Luo。如果哪天官方的 OpenCode auto-config 能识别 oh-my-openagent 并自动绕开,这个仓库就过时了,那是好事。
Share this post:
Enjoy this post? Subscribe via RSS: English | 中文