Context
Jason raised a real architectural concern during #56's fix-up: .opencode-plugin/ is a full TypeScript reimplementation of the same capture/redaction/session-start logic that hooks/*.sh already implements for Claude Code and Codex, rather than one shared implementation with a thin per-harness adapter. That's a real maintenance cost — this session alone fixed two production bugs (#56) that were exactly this kind of reimplementation drift (a wrong naming-convention assumption, a wrong event-consumption model) that could not have happened if OpenCode ran the same shell scripts Claude Code and Codex do.
What's blocking a shell-out today
Researched via context7 + the upstream anomalyco/opencode repo:
anomalyco/opencode#12472 — "native Claude Code hooks compat," open, active (18 comments, last updated days ago), assigned to a maintainer. Proposes OpenCode natively read ~/.claude/settings.json-style hooks, translate tool names (bash→Bash, edit→Edit), and pipe the same JSON-on-stdin/exit-code protocol our shell scripts already speak — i.e. exactly what would let .opencode-plugin become a thin bridge instead of a reimplementation. Not shipped.
- A community member got frustrated enough to submit the adjacent piece directly —
#19519, tool.execute.after message injection — filed two weeks after the underlying issue (#17412) went unanswered by maintainers. Closed, unmerged. No committed timeline for either.
- A community bridge plugin already exists,
magarcia/opencode-claude-hooks — loads .claude/settings.json and runs its hooks as shell commands inside OpenCode's plugin system, real precedent for the shell-out pattern. Its own docs note "existing Claude Code hooks don't move to OpenCode due to different runtime and API requirements," so it's not a drop-in reference implementation, but it proves the pattern works.
- Our own
experimental.chat.system.transform usage (the only way to inject the HANDOFF.md/git-state block into context today) rests on the same missing primitive #17412 describes — plugin hooks can't inject AI-visible messages through any stable API yet. This is the correct tool available today, not a stopgap we chose poorly; it's just genuinely fragile until OpenCode ships something more stable, matching the plugin's own header comment.
Decision
Don't attempt a partial shell-out now — hand-rolling our own stdin/exit-code bridge ahead of any committed OpenCode compat surface would just be a second bespoke OpenCode integration, arguably worse than the current TypeScript port (which at least has full test coverage and doesn't depend on translating tool-call arg shapes across two independently-evolving protocols).
Trigger for revisiting: when anomalyco/opencode#12472 (or an equivalent native-hooks feature) ships, re-evaluate collapsing .opencode-plugin/src/hooks/*.ts down to a thin adapter that shells out to hooks/*.sh the same way Codex reuses them via its own convention — eliminating the version-drift and reimplementation-bug class entirely. Low priority until then; watch the upstream issue.
Context
Jason raised a real architectural concern during #56's fix-up:
.opencode-plugin/is a full TypeScript reimplementation of the same capture/redaction/session-start logic thathooks/*.shalready implements for Claude Code and Codex, rather than one shared implementation with a thin per-harness adapter. That's a real maintenance cost — this session alone fixed two production bugs (#56) that were exactly this kind of reimplementation drift (a wrong naming-convention assumption, a wrong event-consumption model) that could not have happened if OpenCode ran the same shell scripts Claude Code and Codex do.What's blocking a shell-out today
Researched via context7 + the upstream
anomalyco/opencoderepo:anomalyco/opencode#12472— "native Claude Code hooks compat," open, active (18 comments, last updated days ago), assigned to a maintainer. Proposes OpenCode natively read~/.claude/settings.json-style hooks, translate tool names (bash→Bash,edit→Edit), and pipe the same JSON-on-stdin/exit-code protocol our shell scripts already speak — i.e. exactly what would let.opencode-pluginbecome a thin bridge instead of a reimplementation. Not shipped.#19519,tool.execute.aftermessage injection — filed two weeks after the underlying issue (#17412) went unanswered by maintainers. Closed, unmerged. No committed timeline for either.magarcia/opencode-claude-hooks— loads.claude/settings.jsonand runs its hooks as shell commands inside OpenCode's plugin system, real precedent for the shell-out pattern. Its own docs note "existing Claude Code hooks don't move to OpenCode due to different runtime and API requirements," so it's not a drop-in reference implementation, but it proves the pattern works.experimental.chat.system.transformusage (the only way to inject the HANDOFF.md/git-state block into context today) rests on the same missing primitive #17412 describes — plugin hooks can't inject AI-visible messages through any stable API yet. This is the correct tool available today, not a stopgap we chose poorly; it's just genuinely fragile until OpenCode ships something more stable, matching the plugin's own header comment.Decision
Don't attempt a partial shell-out now — hand-rolling our own stdin/exit-code bridge ahead of any committed OpenCode compat surface would just be a second bespoke OpenCode integration, arguably worse than the current TypeScript port (which at least has full test coverage and doesn't depend on translating tool-call arg shapes across two independently-evolving protocols).
Trigger for revisiting: when
anomalyco/opencode#12472(or an equivalent native-hooks feature) ships, re-evaluate collapsing.opencode-plugin/src/hooks/*.tsdown to a thin adapter that shells out tohooks/*.shthe same way Codex reuses them via its own convention — eliminating the version-drift and reimplementation-bug class entirely. Low priority until then; watch the upstream issue.