CtrlK
BlogDocsLog inGet started
Tessl Logo

punkdev/cc2oc

Add and ship OpenCode support for one Claude Code plugin at a time. Includes core migration to a reviewable `opencode-plugin/` adapter, maintainer-facing docs, and follow-up CI/versioning setup for package publishing or skill-copy drift checks.

92

1.25x
Quality

92%

Does it follow best practices?

Impact

97%

1.25x

Average score across 2 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

hook-porting.mdskills/migrate-plugin/references/

Hook Porting Reference

Use this when any Claude hook needs OpenCode parity analysis.

Mental Model

Claude hooks and OpenCode plugins are not one-to-one. Port by user-visible intent, not by event name or file path.

Claude hooks often run shell, HTTP, MCP, prompt, or agent handlers with event JSON and command exit conventions. OpenCode plugins are JavaScript/TypeScript modules that return handlers for OpenCode events.

Claude Hook Cadences

CadenceClaude eventsNotes
SessionSessionStart, SessionEndRuns when sessions begin, resume, or end. Useful for startup context and state.
TurnUserPromptSubmit, Stop, StopFailureRuns around a user turn. Some can block or continue behavior.
ToolPreToolUse, PostToolUse, PostToolUseFailure, PostToolBatchRuns around tool calls. PreToolUse can block.
PermissionPermissionRequest, PermissionDeniedTied to Claude permission flow. Semantics do not copy directly.
CompactionPreCompact, PostCompactHigh parity risk because timing and APIs differ.
Agent/taskSubagentStart, SubagentStop, TaskCreated, TaskCompletedPort only when equivalent OpenCode behavior is available and tested.

OpenCode Event Groups

High-value OpenCode events for Claude plugin ports:

OpenCode eventUse
tool.execute.beforeValidate, block, or mutate a tool call before execution.
tool.execute.afterObserve successful tool calls.
shell.envInject shell environment variables.
tui.command.executeImplement native command behavior.
tui.prompt.appendAppend content to prompt from TUI interactions.
tui.toast.showShow user feedback.
session.created, session.updated, session.compacted, session.idleMaintain session state; exact timing needs testing.
experimental.session.compactingInject or replace compaction context; treat as experimental.
permission.asked, permission.repliedObserve permission flow; test before enforcing policy.
file.edited, file.watcher.updatedReact to file changes when a Claude hook watched edits.
message.updated, message.part.updatedReact to conversation changes when needed.

Porting Map By Intent

Claude hook intentClaude mechanismOpenCode strategyParity risk
Add startup contextSessionStart stdout/JSONPrefer shared skill content; otherwise session event or tested context hookMedium
Track modes or user commandsUserPromptSubmit parses promptPrefer native OpenCode TUI command/event behaviorMedium
Block unsafe tool usePreToolUse exit/JSON decisiontool.execute.before with tested throw/mutate behaviorHigh
React after a tool succeedsPostToolUsetool.execute.after or file/message/session eventsMedium
Inject shell environmentShell wrapper or hookshell.envLow/medium
Persist stateHook script writes Claude config/dataOpenCode plugin state under OpenCode/XDG-safe pathsLow if tested
Register commandsClaude commands/hooksOpenCode TUI command behavior or command filesMedium
Persist compaction contextPreCompact/PostCompactexperimental.session.compacting only with version/test caveatsHigh
Prompt expansionUserPromptExpansionNative command or skill activation where possibleHigh
MCP tool hookClaude MCP hook typeReimplement with OpenCode SDK/plugin code if possibleHigh

MCP server configuration is separate from MCP hook behavior. For .mcp.json or plugin.json.mcpServers, use references/mcp-migration.md instead of treating it as hook porting.

Non-Equivalences To Call Out

  • Claude command hooks read JSON from stdin; OpenCode handlers receive JavaScript/TypeScript event inputs.
  • Claude hook handlers can use exit codes and hookSpecificOutput; OpenCode plugins do not use that command-exit contract.
  • Claude matcher and permission-rule syntax do not copy directly to OpenCode.
  • Claude hook stdout is model-visible only for selected events; OpenCode context injection needs OpenCode-specific mechanisms.
  • Permission blocking, compaction, prompt injection, prompt expansion, and MCP behavior need explicit tests before parity claims.

Required Hook Port Record

Before writing hook code, create a record using assets/hook-parity-record.md.

Do not claim a hook is ported until the record has an implementation strategy, tests, and parity caveats.

README.md

tile.json