How OpenCode Plugin Override Works for Tools Like RTK
I kept seeing RTK mentioned in the OpenCode community. “Token compression for shell output.” “Wraps commands automatically.” But how does it actually intercept OpenCode’s built-in shell tool? I assumed it required some complex hook system or special API.
Turns out, it’s embarrassingly simple.
The Problem: I Wanted to Customize OpenCode’s Tools
I was working on a project where I wanted to add logging to every file write operation OpenCode performed. My first thought was to fork OpenCode and modify the write tool directly. Then I’d have to maintain my fork forever. Not ideal.
I started digging through RTK’s implementation after seeing this Reddit thread where someone mentioned RTK “overrides built-in tools.” That caught my attention.
The Answer: Name-Based Override
OpenCode plugins can override built-in tools by registering a tool with the same name. That’s it. No special registration flag. No hook API. Just match the name.
When OpenCode needs to call a tool, it checks the plugin registry first. If a plugin registered a tool with that name, the plugin’s handler runs instead of the built-in one.
How RTK Uses This
Here’s what happens when OpenCode wants to run a shell command:
User Request: "List files in current directory" | vOpenCode: "I need to call shell tool" | vPlugin Check: "Is there an override for 'shell'?" | +-- YES: RTK plugin intercepts | | | +-- Wraps command: "ls -la" -> "rtk ls -la" | | | v | Execute compressed command | | | v | Decompress and return output | +-- NO: Use built-in shell toolFrom the Reddit discussion, this is exactly how RTK works:
“it will compress our shell tool output. when opencode doing shell tool, this plugin will intercept and wrap with rtk to execute the command”
And another plugin developer confirmed this pattern works for other tools too:
“i did this with my hashline tools too (overriding the built in write, edit, patch). and this is why opencode is very flexible and im loving it”
Why This Design Works
The beauty of name-based override is that it’s:
Implicit - No special API to learn. Just name your tool the same as the built-in one.
Composable - Multiple plugins can override different tools simultaneously. RTK handles shell, hashline handles file operations, and they don’t conflict.
Maintainable - OpenCode upgrades don’t break your plugins (unless tool interfaces change dramatically).
Reversible - Disable the plugin in config, and the built-in tool takes over again.
Common Mistakes I Almost Made
Mistake 1: Looking for a “registerOverride” API
I spent way too long searching OpenCode’s documentation for an override registration method. There isn’t one. The override is implicit through naming.
Mistake 2: Forgetting to Return the Right Shape
When you override a tool, your return value must match what OpenCode expects. I initially returned a compressed string from my shell override, but OpenCode expected an object with specific fields. Other parts of the system depend on that structure.
Mistake 3: Replacing When I Should Wrap
RTK doesn’t replace the shell tool. It wraps it. The shell command still runs; RTK just compresses the output. If you want to extend behavior rather than replace it, you need to call through to the original (or reimplement it).
A Minimal Override Example
Here’s a simplified version of what a tool override looks like:
export const shell = { name: "shell", description: "Shell with logging",
async execute(params: { command: string }) { // Log before execution console.log(`[Override] Running: ${params.command}`);
// Option A: Call original implementation // const result = await originalShell.execute(params);
// Option B: Custom implementation const result = await customShellHandler(params.command);
// Modify or enhance the output return result; }};And in your opencode.json:
{ "plugins": { "my-override": { "path": "plugins/my-shell-override.ts", "enabled": true } }}Notice there’s no overrides: ["shell"] field needed. The override happens automatically because the tool is named shell.
When to Use Overrides
This pattern is useful for:
- Token optimization (like RTK does)
- Adding instrumentation or logging
- Validation before operations
- Transforming inputs or outputs
- Redirecting operations to different targets
When NOT to Use Overrides
Don’t use this for:
- Tools you just want to use (not modify)
- Changing behavior that other plugins depend on (unless you know what you’re doing)
- Breaking the tool contract (return types must be compatible)
The Bottom Line
OpenCode’s plugin override system is refreshingly simple: name your tool the same as a built-in tool, and yours wins. This enables ecosystem plugins like RTK to add token compression to shell operations, or hashline to enhance file writes, all without touching OpenCode’s core code.
The architecture recognizes that the simplest solution often works best. No complex hook system. No special registration API. Just match the name, implement the interface, and you’re done.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments