OC-201b · Module 3
Plugin Interface Design
4 min read
A plugin is a module that extends another module without modifying its source code. The host module defines extension points — hooks where plugins can inject behavior. The plugin registers itself at those hooks. When the host executes, it calls every registered plugin at the appropriate hook point. This is the architecture that lets OpenClaw evolve without rewriting. The humanizer plugin from ClawHub does not modify the message-sending code. It registers at the "before-send" hook and transforms the text before it leaves.
Designing good extension points requires thinking about where variability belongs. The host module handles the invariant logic — the steps that are always the same regardless of configuration. The plugins handle the variant logic — the steps that change based on user preference, domain, or use case. A content pipeline always has a draft step and a publish step. But the "polish" step between them varies: one user wants humanization, another wants SEO optimization, another wants translation. Each of those is a plugin registered at the same hook point.
// Host module defines hooks
type HookPoint = 'before-send' | 'after-receive' | 'before-store';
interface Plugin {
name: string;
hooks: Partial<Record<HookPoint, (data: any) => Promise<any>>>;
}
class PluginRegistry {
private plugins: Plugin[] = [];
register(plugin: Plugin) { this.plugins.push(plugin); }
async run(hook: HookPoint, data: any): Promise<any> {
let result = data;
for (const p of this.plugins) {
if (p.hooks[hook]) {
result = await p.hooks[hook]!(result);
}
}
return result;
}
}
// Plugin registers at a hook point
const humanizerPlugin: Plugin = {
name: 'humanizer',
hooks: {
'before-send': async (text: string) => {
return removeAIPatterns(text); // transforms text before delivery
},
},
};