</>Kurs OpenCode
Kurs/rozdział 0630 min

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.
opencode.jsonc — ładowanie pluginów
{
  "$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 }
    }
  ]
}
Zarządzanie globalnymi pluginami z CLI
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.0
ℹ
Info

Plik 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.

.opencode/plugins/example/index.ts
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.
Rejestracja narzędzia z inputem JSON Schema
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

Redaction sekretów z promptów i regulacja temperatury
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 }
    })
  },
})
⚠
Uwaga

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.
Ukryj modele droższe niż $20/M tokenów wyjścia
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.

✦
Wskazówka

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.