Skip to content

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"
|
v
OpenCode: "I need to call shell tool"
|
v
Plugin 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 tool

From 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:

plugins/my-shell-override.ts
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