Claude Code hooks compatibility for OpenCode
This OpenCode plugin enables you to use your existing Claude Code hook configurations with OpenCode, providing ~80% compatibility for drop-in migration.
- ✅ Drop-in compatibility - Use your existing
.claude/settings.jsonfiles - ✅ All major hooks supported - SessionStart, SessionEnd, PreToolUse, PostToolUse, etc.
- ✅ Command and prompt-based hooks - Run shell scripts or use LLM-based decisions
- ✅ Multiple config locations - Project and global configurations
- ✅ Tool pattern matching - Target specific tools with regex patterns
npm install opencode-claude-hooks
# or
bun add opencode-claude-hooksAdd to your OpenCode configuration (~/.config/opencode/opencode.json):
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
"opencode-claude-hooks"
]
}The plugin loads hook configurations from these locations (in priority order):
Claude Code locations (for compatibility):
.claude/settings.local.json(project, not committed).claude/settings.json(project)~/.claude/settings.json(user global)
OpenCode locations:
4. .opencode/hooks.json (project)
5. ~/.config/opencode/hooks.json (user global)
All configurations are merged, with earlier files taking precedence.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash ./scripts/validate-command.sh",
"timeout": 5000
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo 'Session started!'"
}
]
}
]
}
}| Claude Code Hook | OpenCode Event | Compatibility |
|---|---|---|
SessionStart |
session.created |
✅ Full |
SessionEnd |
session.deleted |
✅ Full |
PreToolUse |
tool.execute.before |
✅ Full |
PostToolUse |
tool.execute.after |
✅ Full |
PostToolUseFailure |
tool.execute.after (error) |
✅ Full |
PermissionRequest |
permission.asked |
|
SubagentStart |
tool.execute.before (Task) |
|
SubagentStop |
tool.execute.after (Task) |
|
PreCompact |
experimental.session.compacting |
|
UserPromptSubmit |
message.updated (user) |
|
Stop |
session.idle |
|
Notification |
tui.toast.show |
|
Setup |
N/A | ❌ Not supported |
Execute shell commands with stdin/stdout:
{
"type": "command",
"command": "bash ./my-hook.sh",
"timeout": 5000
}Input via stdin:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/project/directory",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "ls" }
}Exit codes:
0- Success, continue2- Block/deny the action- Other - Non-blocking error
JSON output (optional):
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Dangerous command detected"
}
}Use LLM to make decisions:
{
"type": "prompt",
"prompt": "Check if this bash command is safe to execute"
}Use the matcher field to target specific tools:
"*"or""- Matches all tools"Bash"- Exact match"Edit|Write"- Regex pattern (matches Edit OR Write)"Read.*"- Regex with wildcards
See the examples/ directory for complete hook examples:
- session-start.sh - Log session starts with timestamp
- block-dangerous-bash.sh - Block dangerous bash commands
- pre-tool-use-logger.sh - Log all tool executions
- notify-on-write.sh - macOS notification when files are written
Hooks receive these environment variables:
CLAUDE_PROJECT_DIR- Project root directoryOPENCODE_COMPAT- Set to"true"(indicates OpenCode environment)
- Setup hook - No equivalent in OpenCode
- MCP tools - May not trigger plugin hooks
- Timing differences -
StopandPreCompactsemantics differ slightly - Permission model - OpenCode's permission system works differently
# Install dependencies
bun install
# Build
bun run build
# Type check
bun run typecheck
# Test hooks locally
bun run examples/test-runner.tsMIT
Contributions welcome! Please open an issue or PR.