Version OpenCode skills and plugins

Repository sits at the OpenCode config root rather than inside skills/ or
plugins/. OpenCode scans exactly those two subdirectories, and a .git
directory inside either one is picked up as a plugin and fails to load:

  loading plugin id=.../plugins/.git entrypoint=.../plugins/.git/index
  failed to load plugin ... AggregateError: 2 errors building .../plugins/.git/index

The watcher.ignore config option does not help: it only suppresses filesystem
update events, not plugin discovery, and the "-" disable prefix only applies to
plugins listed in opencode.json(c), not auto-discovered ones.

.gitignore admits only skills/ and plugins/ (plus itself). Personal config
(opencode.jsonc, cli.json, service.json) and node_modules stay local.

skills/opencode-plugins  V2-only plugin authoring reference and 7 templates,
                         transcribed from the V2 docs with the URL on every file
plugins/searxng-search   searxng_search tool over a self-hosted SearXNG JSON API
This commit is contained in:
2026-10-08 13:12:10 +08:00
commit 15a1bbcee0
17 changed files with 3494 additions and 0 deletions
@@ -0,0 +1,78 @@
# Templates
Starter files transcribed from the OpenCode V2 doc pages. **Copy one and fill in
the blanks** rather than writing a plugin from scratch.
| Template | Use for | Source doc |
| -------- | ------- | ---------- |
| `promise-plugin.ts` | Server plugin, Promise flavor (default) | <https://opencode.ai/v2/docs/build/plugins> |
| `effect-plugin.ts` | Server plugin, Effect-native | <https://opencode.ai/v2/docs/build/plugins/effect/> |
| `rpc.ts` | RPC contract, Promise flavor (copy to `src/rpc.ts`) | <https://opencode.ai/v2/docs/build/plugins/rpc/> |
| `rpc-implementation.ts` | RPC registration + calls, Promise | <https://opencode.ai/v2/docs/build/plugins/rpc/> |
| `rpc-effect.ts` | RPC contract, Effect flavor | <https://opencode.ai/v2/docs/build/plugins/effect/rpc/> |
| `rpc-implementation-effect.ts` | RPC registration + calls, Effect | <https://opencode.ai/v2/docs/build/plugins/effect/rpc/> |
| `tui-plugin.tsx` | TUI / CLI plugin | <https://opencode.ai/v2/docs/build/plugins/cli/> |
| `opencode-plugin-config.jsonc` | `plugins` in `opencode.json(c)` | <https://opencode.ai/v2/docs/plugins> |
| `package.json` | Publishing a plugin package (strict JSON) | <https://opencode.ai/v2/docs/build/plugins> |
| `package-json.md` | Publishing guidance, peer deps, entrypoints | build/plugins, build/plugins/effect, build/plugins/cli |
| `README.md` | This index | — |
`package.json` is strict JSON with no comments, because Node, bun, npm, and
bundlers parse it with a strict parser. Publishing guidance therefore lives in
`package-json.md` rather than inside the manifest.
The two RPC template pairs are **not** interchangeable: same contract shape,
different schema dialect, handler signature, and registration entrypoint. See the
Promise-vs-Effect table in `../references/rpc.md`.
## Before you write code
1. Check the version: `opencode --version`, `opencode api get /api/info`.
2. Pick the flavor. Default to Promise. Effect only if the project already uses
Effect. TUI is a separate export. RPC is a contract plus a registration.
3. Open the source doc in the table above and keep it open.
4. Copy the template. Delete the sections you do not need.
5. Verify every field name against that page. Do not guess.
## Rules the templates encode
These are the constraints the pages state, not stylistic preferences. Note which
are type-level rather than documented runtime failures.
- A `transform` callback is typed `(editor) => void` in both flavors — no
Promise, no Effect.
- Tool `update` preserves name and namespace. **Assign** a new schema or options
object to replace one rather than mutating nested values. In-place edits inside
an `update` callback are fine — that is what the mutable draft is for.
- `reload()` replays transforms. It does **not** rerun `setup` / `effect`.
- Captured inputs are not watched. Load external data before the sync callback,
then call `reload()` when it changes.
- Use the **effective** tool name — including its namespace — for `update` and
`remove` (`example_greeting`, not `greeting`). Only `add` creates a tool.
- A `context` hook covers the agent loop only. Register each request kind you
actually need.
- Result shape is **per operation, not per flavor**: `storage.get` returns a
value, `model.list()` is enveloped. Copy the page's example for the call you
use.
- Effect plugins use `effect`, not `setup`. The two flavors do not have the same
context members or the same hook set.
- Relative plugin paths in config resolve from the **config file**, not the
working directory.
- Registering an event subscription without tearing it down leaks it.
`forkScoped` handles this in Effect; `AbortController` in Promise.
- `package.json` must be strict JSON — `//` comments there break `bun`, `npm`,
and every bundler. `opencode.jsonc` may use them.
## Anything not in a template
The pages carry more detail than fits here. When you need something not in these
files, go to the source doc rather than improvising:
- Every `Agent.Info`, `Provider.Info`, `Model.Info`, `Session.Info`,
`Tool.Content`, `Mcp.Server`, `Skill.Info`, `Vcs.Info`, `Reference.Info`,
`Integration.Info`, `Connection.Info`, `Form.Answer`, `Command.Info`,
`Plugin.Info`, `WebSearch.*`, `Worktree*` shape:
<https://opencode.ai/v2/docs/api>
- The exact contract of the running server: `/openapi.json` on that server.
- Hook event fields: the `Reference` code block at the end of each section on
the relevant page.