Evaluate permissions for OpenCode tool calls
OpenCode has no external binary hook protocol (see anomalyco/opencode#12472)
Instead it offers JS/TypeScript plugins that intercept tool.execute.before events (see https://opencode.ai/docs/plugins/#tool-events)
The plugin receives raw JS objects and can block a tool call by throwing an error. (See: https://opencode.ai/docs/plugins/#env-protection)
Proposal
Two components: an ultra-thin plugin and the Rust binary.
Plugin (~15 lines of JS)
- Receives
tool.execute.before with raw JS output.args
- Stringifies them to JSON
- Spawns
hook-rs <tool> --opencode and pipes the JSON to stdin
- Parses the JSON response
- Throws if
permissionDecision === "deny"
- Returns silently for
allow or ask (falls through to OpenCode's native prompt)
Rust binary
- New
--opencode flag on each subcommand (bash, read, glob, grep)
- When
--opencode is present, deserialise OpenCode's native arg shapes directly
- Map internally to the existing rule evaluation pipeline
- Return standard JSON output with
permissionDecision and permissionDecisionReason
Design principle
The plugin knows nothing about schemas. All field mapping, validation, and decision logic lives in Rust. The plugin is a pure JSON pipe.
Evaluate permissions for OpenCode tool calls
OpenCode has no external binary hook protocol (see anomalyco/opencode#12472)
Instead it offers JS/TypeScript plugins that intercept
tool.execute.beforeevents (see https://opencode.ai/docs/plugins/#tool-events)The plugin receives raw JS objects and can block a tool call by throwing an error. (See: https://opencode.ai/docs/plugins/#env-protection)
Proposal
Two components: an ultra-thin plugin and the Rust binary.
Plugin (~15 lines of JS)
tool.execute.beforewith raw JSoutput.argshook-rs <tool> --opencodeand pipes the JSON to stdinpermissionDecision === "deny"alloworask(falls through to OpenCode's native prompt)Rust binary
--opencodeflag on each subcommand (bash,read,glob,grep)--opencodeis present, deserialise OpenCode's native arg shapes directlypermissionDecisionandpermissionDecisionReasonDesign principle
The plugin knows nothing about schemas. All field mapping, validation, and decision logic lives in Rust. The plugin is a pure JSON pipe.