Pluginy — rozszerzanie OpenCode kodem
TypeScript pakiety i lokalne pliki: nowe narzędzia, hooks na żądania, własne dostawcy i komendy.
Kiedy plugin, a kiedy skill/agent
Skill i agent zmieniają ZACHOWANIE przez instrukcje tekstowe. Plugin zmienia zachowanie przez KOD: rejestruje nowe narzędzia executowalne, modyfikuje żądania do modeli, dorzuca dostawców/komendy/uprawnienia, słucha zdarzeń i zapisuje stan. Jeśli potrafisz opisać procedurę markdownem — nie potrzebujesz pluginu.
Pluginy to moduły TypeScript/JS z domyślnym eksportem Plugin.define(...), ładowane po stronie usługi.
Skąd się ładują
- Automagicznie: pliki .ts/.js i katalogi pakietów w każdej znalezionej .opencode/plugins/ (oraz globalnie ~/.config/opencode/plugins/).
- Jawnie: tablica plugins w opencode.json(c) — nazwy npm (opcjonalnie z wersją), ścieżki ./ ../ / absolute, file://, pakiety scope'owane.
- Forma obiektu, gdy plugin przyjmuje opcje: { package, options } — opcje czytasz z ctx.options w setup.
- Kontrola: "-id" wyłącza plugin (wildcard *, prefix .*), późniejszy ID włącza ponownie.
{
"$schema": "https://opencode.ai/config.json",
"plugins": [
"opencode-acme-plugin",
"opencode-acme-plugin@1.2.0",
"@acme/opencode-plugin",
"./plugins/local",
{
"package": "@acme/opencode-plugin",
"options": { "agent": "reviewer", "strict": true }
}
]
}opencode plugin add opencode-acme-plugin@1.2.0
opencode plugin list
opencode plugin check # sprawdź aktualizacje
opencode plugin update # zaktualizuj wszystko
opencode plugin remove opencode-acme-plugin@1.2.0Plik w .opencode/plugins/ reloaduje się po zmianie (watch); plugin z npm instaluje się w tle przy starcie. Pluginy CLI (rozszerzające samo TUI) konfiguruje się osobno, w cli.json.
Minimalny plugin i cykl życia
setup(ctx) wołany jest przy załadowaniu; zwrócona funkcja to cleanup. ctx to w praktyce pełny klient serwera OpenCode + metody plugin-only: transform, hook, reload, options.
import { Plugin } from "@opencode/plugin"
export default Plugin.define({
id: "example",
async setup(ctx) {
await ctx.storage.set("loaded", true)
console.log(`wczytany w OpenCode ${ctx.app.version}`)
return () => console.log("plugin odłączony") // cleanup
},
})Nowe narzędzie z pluginu
- context.signal — AbortSignal; podaj go fetchowi, żeby przerwanie sesji przerywało żądanie.
- editor.namespace({ name, description }) grupuje narzędzia w Code Mode.
- editor.update("acme_greeting", …) / editor.remove(...) działają na efektywnej nazwie z namespace'em.
export default Plugin.define({
id: "acme.tools",
async setup(ctx) {
await ctx.tool.transform((editor) => {
editor.add({
name: "greeting",
description: "Utwórz powitanie",
input: {
type: "object",
properties: { name: { type: "string" } },
required: ["name"],
additionalProperties: false,
},
execute: async (input, context) => {
await context.progress({ status: "generowanie" })
return { content: `Hello ${(input as { name: string }).name}!` }
},
})
})
},
})Hooks — przechwytywanie żywych operacji
export default Plugin.define({
id: "acme.policy",
async setup(ctx) {
// 1) Zanim prompt trafi do sesji: cenzuruj i wzbogać
await ctx.session.hook("prompt", (event) => {
event.prompt.text = event.prompt.text
.replaceAll("company-secret", "[REDACTED]")
})
// 2) Tuż przed żądaniem do modelu: dopisz instrukcję, zmień opcje
await ctx.session.hook("context", (event) => {
event.system.push({
type: "text",
text: "Recenzuj tylko pod kątem poprawności.",
})
event.options.temperature = 0.2
})
// 3) Polityka ponowień dla 429
await ctx.session.hook("retry", (event) => {
if (event.error.status === 429)
event.decision = { retry: true, delay: 10_000 }
})
},
})Hook context zmienia WYJŚCIOWE żądanie, nie zapisaną historię. Hooki wielu pluginów lecą w kolejności rejestracji i widzą zmiany poprzedników.
Transformy — modyfikowanie rejestrów
Transform to synchroniczna edycja stanu domenowego: agentów, dostawców, modeli, komend, skilli, serwerów MCP, referencji, dostawców websearch, a nawet strategii worktree. Czytanie rejestrów (ctx.model.list()) widzi już wszystkie dotychczasowe rejestracje.
- Zewnętrzne dane wczytaj PRZED callbackiem, a po ich zmianie wołaj reload() — przechwyconych wartości nikt nie obserwuje sam z siebie.
- registration.dispose() wycofuje transform i odbudowuje rejestr z pozostałych.
- Pełna lista domen ctx: agent, provider, model, command, integration, mcp, reference, skill, session, permission, storage, tool, vcs, worktree, websearch, event, generate.
await ctx.model.transform((editor) => {
editor
.list()
.filter((m) => m.cost.some((tier) => tier.output > 20))
.forEach((m) => editor.remove(m.providerID, m.id))
})Skąd brać gotowe pluginy
Ekosystem kataloguje społeczna lista awesome-opencode (GitHub + awesome-opencode.com): pluginy pamięci trwałej, powiadomień OS, orkiestracji wielu agentów, integracji providerów, hooków zgodnych z Claude Code i dziesiątki innych. Instalacja sprowadza się zwykle do opencode plugin add <pakiet> albo wpisu w tablicy plugins.
Zasada bezpieczeństwa: plugin wykonuje kod po stronie usługi z Twoimi danymi. Przed instalacją przeczytaj repo (albo poproś OpenCode o review kodu pluginu!) i preferuj piny wersji.
Nie znalazłeś gotowca? Prompt do własnego pluginu: „Utwórz plugin OpenCode w .opencode/plugins/x/ który [cel], używając Plugin.define i ctx.tool.transform”. Agent zna API z dokumentacji, którą może pobrać webfetchem.