OpenCode Integration
August 19, 2026 · View on GitHub
Last updated: 2026-08-19 (first-party plugin, issue #318)
OpenCode does not expose PreToolUse-style hook config
files the way Claude Code, Codex, or Gemini do. Its interception surface is a
plugin system: JavaScript/TypeScript modules loaded by OpenCode's Bun
runtime from ~/.config/opencode/plugins/ (global) or .opencode/plugins/
(per-project). A plugin that implements the "tool.execute.before" hook can
inspect every tool call before it runs and veto it by throwing an Error.
dcg ships a first-party plugin generator:
dcg install --opencode # user-level: ~/.config/opencode/plugins/dcg-guard.js
dcg install --opencode --project # repo-level: <repo>/.opencode/plugins/dcg-guard.js
dcg install --opencode --force # refresh a dcg-owned plugin in place
Restart OpenCode (start a new session) after installing — plugins are loaded at startup.
How the plugin works
The generated dcg-guard.js:
- Registers a
"tool.execute.before"handler and ignores every tool exceptbash. - Spawns the dcg binary (absolute path embedded at install time — never a
bare
PATHlookup, since agent-spawned processes often run with a reducedPATH) with the Claude-compatible hook envelope on stdin:{"tool_name":"Bash","tool_input":{"command":"…"}}, andOPENCODE=1in the environment so dcg identifies the calling agent. - Interprets dcg's stdout exactly like the other harnesses: empty stdout
means allow; a
hookSpecificOutput.permissionDecisionof"deny"— or"ask", since OpenCode has no operator-review state, so review requests fail closed — aborts the tool call by throwing anErrorcarrying dcg's full block message (reason, rule id, allow-once code, suggestions). - Fails open only on infrastructure errors (dcg binary missing or
unrunnable), with a
[dcg]notice on OpenCode's stderr. The safety evaluation itself keeps dcg's bounded fail-closed semantics.
Ownership and uninstall
The file carries a dcg-opencode-plugin marker comment. The installer refuses
to overwrite a dcg-guard.js that lacks the marker (it belongs to you, not
dcg), and uninstall.sh deletes only marker-carrying files.
install.sh configures OpenCode automatically when
${XDG_CONFIG_HOME:-~/.config}/opencode exists or opencode is on PATH
(status appears in the install summary). uninstall.sh removes the plugin
symmetrically.
Verifying coverage
dcg doctor includes an opencode_plugin check whenever OpenCode appears to
be in use (config directory present or an OPENCODE* environment variable
set). A missing plugin is an error — unlike Grok, OpenCode has no Claude
compatibility layer, so without the plugin its shell commands never reach dcg
at all. dcg doctor --fix installs it.
A green doctor proves wiring only. The end-to-end proof is a live refusal:
ask an OpenCode session to run a harmless guarded command (for example
git reset --hard in a scratch repository) and confirm the tool call aborts
with BLOCKED by dcg and the expected rule id.
Manual test of the plugin contract
# What the plugin sends and receives:
echo '{"tool_name":"Bash","tool_input":{"command":"git reset --hard"}}' | dcg
# → denial JSON on stdout (plugin throws)
echo '{"tool_name":"Bash","tool_input":{"command":"git status"}}' | dcg
# → empty stdout, exit 0 (plugin allows)
Limitations
- Plugins run inside OpenCode's own runtime; a model that writes a script to
disk and executes it in a later
bashcall is still evaluated (dcg sees the invocation), but content dcg cannot statically trace falls back to its bounded fail-closed rules like every other harness. - Project-level installs (
--project) require the repository to be trusted by OpenCode before plugins load, mirroring Grok's/hooks-trustflow.