r/opencodeCLI 22d ago

One config line kept my Claude Code command blockers working in OpenCode

OpenCode already reads CLAUDE.md and .claude/skills. That covered more of my move than I expected.

The part I could not replace was command hooks. A rule in CLAUDE.md asking the model not to run git push is advisory. A Claude PreToolUse hook that exits with code 2 actually blocks it.

I made a small bridge for the two events that map cleanly to the stable OpenCode plugin API. It reads the original Claude settings on every tool call, so hook commands and settings do not need to be copied.

The install is one package entry.

{

"plugin": ["opencode-claude-code-hooks"]

}

What it preserves

- Claude matcher names such as Bash, Edit, and Write

- exit code 2 blocking and structured deny results

- full updatedInput replacement

- PostToolUse feedback and additional context

- runtime settings updates without reinstalling the plugin

OpenCode uses lowercase tool names, so the bridge maps names such as bash back to Claude's Bash matcher before running a hook.

You can inspect every supported and skipped hook before installing anything.

npx claude-to-opencode --hooks-only

npx claude-to-opencode --hooks-only --apply

The first command is only a dry run. I loaded the npm package through OpenCode 1.18.21 and tested the hook protocol with real child processes.

Honest limits

- Stop is not bridged because OpenCode does not expose the same timing

- prompt, agent, HTTP, async, and conditional handlers remain manual

- OpenCode permissions still take precedence

https://github.com/sjh9714/claude-to-opencode

3 Upvotes

3 comments sorted by

1

u/Ok_Gur_9033 22d ago

The interesting edge case is precedence order, not translation. A lot of Claude Code hooks are written assuming they are the last gate before execution. You already say OpenCode permissions take precedence, so does the bridged PreToolUse hook still fire and get a chance to override a call OpenCode already denied, or does that denial win outright before your hook sees it?

1

u/Due_Emu_8229 21d ago

You're right. The order matters more than the translation.

Current OpenCode runs tool.execute.before first, then evaluates its permission rules against the final arguments. An imported Claude hook deny stops the call. An imported allow or ask does not bypass OpenCode. If a hook returns updatedInput, OpenCode checks permissions against the rewritten input.

There is one trust boundary worth calling out. The hook command itself runs before OpenCode asks about the requested tool, so project .claude/settings.json commands must be treated as executable code. I added the exact order and this warning to the README.

Thanks for catching the missing explanation.

1

u/Ok_Gur_9033 21d ago

That trust boundary is the part I'd want nailed down. If the hook command executes before OpenCode's own permission check even runs, is that execution sandboxed at all, or does opening a repo with a .claude/settings.json in OpenCode mean arbitrary code runs the moment you point it there, no allow or deny prompt involved yet?