Plugin fails to load on OpenCode v2: v1 plugin API (default export is a function, not an object)
Plugin fails to load on OpenCode v2 (v1 plugin API)
@dietrichgebert/[email protected] cannot be loaded by OpenCode v2. The package ships
only a v1 plugin, and the v2 plugin module loader rejects it:
msg="loading plugin" id=@dietrichgebert/ponytail
entrypoint=.../node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs
failed to load plugin target=@dietrichgebert/ponytail
cause="PluginModule.LoadError: Plugin must export a default definition with an id
and an effect or setup function. (cause: SchemaError(Expected object at ["default"])))"Reproduced on opencode v2.0.18, Windows 11, Node v24.19.0. Adding the package to
opencode.json plugins makes opencode resolve and import the entrypoint, then
fail schema validation on every load.
Root cause
.opencode/plugins/ponytail.mjs default-exports an async function that returns a
hooks object:
export default async ({ client } = {}) => {
return {
config: async (config) => { /* pushes commands + skills */ },
'experimental.chat.system.transform': async (input, output) => { /* injects ruleset */ },
'command.execute.before': async (input) => { /* persists /ponytail mode */ },
};
};v2 requires a default-exported object with id + setup (or effect), which is
what Expected object at ["default"] reports. The v1 hook names are also gone in v2:
| v1 (ponytail) | v2 equivalent |
|---|---|
config hook pushing skills.paths |
ctx.skill.transform(...) |
config hook pushing config.command |
ctx.command.transform(...) |
experimental.chat.system.transform |
ctx.session.hook("context", ...) |
command.execute.before |
no direct equivalent |
package.json main and exports["."] both point at the v1 file, and the only other
plugin-shaped file is ponytail-frontmatter.cjs (a helper). There is no v2 entrypoint
to fall back to.
Also stale
The header comment in ponytail.mjs says:
// OpenCode loads this as a server plugin — add it to your opencode.json:
// { "plugin": ["@dietrichgebert/ponytail"] }plugin (singular) is the v1 key; v2 uses plugins (plural). Following the comment as
written is silently ignored by v2.
Suggested fix
Export a v2 definition, keeping the existing v1 export for older opencode:
export const ponytailV2 = {
id: "ponytail",
async setup(ctx) { /* ctx.session.hook("context"), ctx.command.transform, ctx.skill.transform */ },
}and point exports at a small dispatcher, or ship a v2 entrypoint selected by the
host's version. A working ~150-line v2 shim that reuses
hooks/ponytail-instructions.js and hooks/ponytail-config.js unchanged was enough to
restore system-prompt injection, the /ponytail <mode> command, and the six bundled
skills, so the port looks small.
Happy to open a PR if a v2 branch is preferred over a shim.
Source: DietrichGebert/ponytail