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:
@@ -0,0 +1,79 @@
|
||||
// RPC implementation + calling — template
|
||||
//
|
||||
// Docs: https://opencode.ai/v2/docs/build/plugins/rpc/
|
||||
//
|
||||
// Two files. The contract is importable without loading the implementation, so
|
||||
// other plugins and clients can depend on it.
|
||||
//
|
||||
// Needs templates/rpc.ts copied next to this file as src/rpc.ts, so the
|
||||
// doc's "./rpc.js" specifier resolves. An Effect plugin would import the
|
||||
// Effect-dialect contract instead — see rpc-effect.ts.
|
||||
|
||||
//
|
||||
// The docs use "@opencode/plugin" and "@opencode/plugin/effect". Install the
|
||||
// package at your OpenCode version and restart the service first — see the
|
||||
// PREREQUISITE block in promise-plugin.ts.
|
||||
import { Plugin } from "@opencode/plugin"
|
||||
import { Acme } from "./rpc.js"
|
||||
|
||||
export default Plugin.define({
|
||||
id: "acme-plugin",
|
||||
|
||||
async setup(ctx) {
|
||||
const registration = await ctx.rpc.register(Acme, {
|
||||
search: async (input, context) => {
|
||||
// JSON Schema input is `unknown` — narrow it.
|
||||
const { query } = input as { query: string }
|
||||
|
||||
// `signal` cancels with the request; `context.error` declares a failure.
|
||||
const text = await findText(query, { signal: context.signal })
|
||||
if (!text) return context.error("not_found", "Result not found", { query })
|
||||
|
||||
return { text }
|
||||
},
|
||||
})
|
||||
|
||||
// The same plugin can call its own RPC once registered.
|
||||
const acme = ctx.rpc(Acme)
|
||||
// Raw JSON Schema output arrives as `unknown` in the Promise flavor; the
|
||||
// docs say to narrow it.
|
||||
const result = (await acme.search({ query: "hello" })) as { text: string }
|
||||
|
||||
// Emit to subscribers.
|
||||
await registration.events.emit("updated", { itemID: "item-1", text: result.text })
|
||||
|
||||
// ---------------------------------------------------------------- events
|
||||
// Subscribe with the LOCAL name. The received `type` is prefixed
|
||||
// ("rpc.acme.updated"). Every event carries `data` and `location`.
|
||||
//
|
||||
// Subscriptions are LIVE ONLY: a disconnected subscriber misses events.
|
||||
// Plugin unload closes them.
|
||||
//
|
||||
// const unsubscribe = acme.events.on("updated", (event) => {
|
||||
// console.log(event.type, event.location.directory, event.data.text)
|
||||
// })
|
||||
// unsubscribe()
|
||||
//
|
||||
// for await (const event of acme.events.subscribe("updated")) {
|
||||
// console.log(event.data.text)
|
||||
// }
|
||||
|
||||
return () => {
|
||||
void registration.dispose()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// Calling from a client over HTTP:
|
||||
// import { OpenCode } from "@opencode/client"
|
||||
// import { Acme } from "opencode-acme-plugin/rpc"
|
||||
// const client = OpenCode.make({ baseUrl: "http://localhost:4096" })
|
||||
// const acme = client.rpc(Acme)
|
||||
// const result = await acme.search({ query: "hello" })
|
||||
|
||||
// Calling from another plugin (a TUI plugin uses `context.client`):
|
||||
// import { Plugin } from "@opencode/plugin/tui"
|
||||
// const acme = context.client.rpc(Acme)
|
||||
// const result = await acme.search({ query: "hello" })
|
||||
|
||||
declare function findText(query: string, options: { signal: AbortSignal }): Promise<string | undefined>
|
||||
Reference in New Issue
Block a user