claude-hooks-bridge
v0.1.2
Published
Bring your Claude Code hooks (.claude/settings.json) to OpenCode — no rewrite, no migration. Only documents hooks that have been live-verified to actually fire, unlike prior attempts.
Maintainers
Readme
claude-hooks-bridge
Bring your Claude Code hooks to OpenCode. No rewrite, no migration — your existing
.claude/settings.jsonjust works.
If you've already got UserPromptSubmit, PostToolUse, or Stop hooks wired up for Claude Code, this plugin makes the exact same config file drive the exact same automation under OpenCode — memory sync, session logging, custom notifications, whatever you've built — with zero changes to your hook scripts.
Why this exists
There's one prior attempt at this: opencode-claude-hooks. It's a good idea, but two of its core mappings — UserPromptSubmit and Stop — are documented as supported and never actually implemented in the shipped code. Confirmed by reading the compiled source directly: no chat.message handler, no session.idle branch, anywhere. Filed as magarcia/opencode-claude-hooks#2.
claude-hooks-bridge only documents what's been live-verified against real OpenCode sessions — headless opencode run and persistent opencode serve sessions both — not just plausible API mappings. If a hook is listed below, it's because it was actually triggered and watched fire, repeatedly, with the correct payload.
Installation
npm install claude-hooks-bridgeAdd to opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["claude-hooks-bridge"]
}That's it — your existing .claude/settings.json is picked up automatically, same file, same format, no edits required.
Note: OpenCode resolves plugin names against the public npm registry, not local files — this only works once actually installed from npm.
npm linkand local.opencode/plugins/files don't work (see Known limitations).
Supported hooks
| Claude Code hook | OpenCode event | Notes |
|---|---|---|
| UserPromptSubmit | chat.message | Fires once per user message |
| PostToolUse | tool.execute.after | matcher: "Agent" also matches OpenCode's real tool name "task" — see below |
| Stop | event.type === "session.idle" | |
| SessionStart | event.type === "session.created" | |
| SessionEnd | event.type === "session.deleted" | |
Config is read from the same locations Claude Code uses, in priority order: .claude/settings.local.json, .claude/settings.json, ~/.claude/settings.json.
The Agent → task matcher translation
Claude Code's subagent-spawning tool is called Agent. OpenCode's equivalent tool is called task. A PostToolUse hook matching "Agent" — the natural way to write it for Claude Code — automatically also matches OpenCode's task tool. No config changes needed on your end; this is exactly the kind of detail that silently breaks a naive port and is the reason this plugin exists.
Not yet supported
PreToolUse (blocking/deny hooks), PermissionRequest, SubagentStart/SubagentStop, PreCompact, Notification, Setup — none of these have been tested against real OpenCode events yet. Rather than guess and risk another silently-broken mapping, they're simply absent for now. PRs that add one of these with a documented, reproducible verification are very welcome — see Contributing.
Hook command interface
Command hooks receive the same stdin JSON shape Claude Code sends, so existing hook scripts work unmodified:
{
"session_id": "...",
"transcript_path": "",
"cwd": "...",
"permission_mode": "default",
"hook_event_name": "PostToolUse",
"tool_name": "read",
"tool_input": { "...": "..." },
"tool_output": "..."
}transcript_path is always empty — OpenCode has no direct equivalent exposed to plugins, and this intentionally doesn't guess at a wrong path rather than fabricate one. Exit code 0 is treated as success; any other exit code is logged but doesn't block (no blocking hooks in this version — see Not yet supported).
Known limitations
These are upstream OpenCode behaviors, not bugs in this plugin — documented here so you don't lose time rediscovering them:
- Local plugin development hangs OpenCode's startup. Open upstream bug: anomalyco/opencode#30904. If you're developing a fork of this plugin, you'll need to actually publish a version to test it —
npm linkdoesn't work either, due to a separate Bun symlink-resolution bug (#11001, #10574). - Plugin loading is intermittently flaky under
opencode run. Consistent with other open reports of OpenCode's plugin loader hanging non-deterministically (e.g. #24418). When it loads, hooks fire correctly every time, verified across repeated runs — occasionally the whole run just hangs before anything fires. Retrying resolves it. - OpenCode caches resolved plugins under
~/.cache/opencode/packages/<name>@latest/. If you ran a project against an older version of this plugin, clear that directory to pick up a new release.
Contributing
This project's whole reason for existing is refusing to document a hook mapping that hasn't actually been watched fire. If you'd like to add support for one of the "not yet supported" hooks above:
- Write the handler.
- Show a reproducible test: real
opencode runoropencode serveinvocation, the exact config that triggers it, and the captured output proving it fired with the right payload. - Open a PR with that evidence included — not just "should work based on the docs."
Bug reports are equally welcome, especially anything that contradicts a claim made in this README — that's exactly the kind of gap this project exists to close.
License
MIT
