Relationships
#3340 repo init/upgrade emits OpenCode V1-API audit plugin that fails to load on OpenCode 2
Opened by elem103 · 10/13/2026· Shipped 10/13/2026
Summary
swamp repo init / swamp repo upgrade (OpenCode tool support) writes
<project>/.opencode/plugins/swamp-audit.ts against the OpenCode V1 plugin
API. OpenCode 2 removed the V1 API, so the generated file fails to load and
swamp's audit timeline silently stops receiving OpenCode bash invocations. The
file's own header says "managed file — it will be overwritten on swamp upgrade",
so every upgrade re-introduces the broken version.
Environment
- swamp
20261013.004531.0-sha.1373120f - OpenCode
v2.0.27 - macOS (darwin)
Steps to reproduce
swamp repo init --tool opencode(orswamp repo upgrade --tool opencode) in a git repo.- Start/restart OpenCode 2 in that repo.
- OpenCode logs a warning and skips the plugin instead of loading it:
timestamp=... level=WARN message="failed to load plugin"
target=<project>/.opencode/plugins/swamp-audit.ts
cause="Cause([Fail(PluginModule.LoadError: Plugin must export a default definition
with an id and an effect or setup function.
(cause: SchemaError(Missing key at [\"default\"])))])"No OpenCode bash entries reach swamp audit record --from-hook. No crash, but
audit data is silently incomplete.
Root cause
The generated file uses a named export of a V1 factory that returns string-keyed hooks:
import type { Plugin } from "@opencode-ai/plugin";
export const SwampAudit: Plugin = async ({ directory }) => {
return {
"tool.execute.before": async (input, output) => { ... },
"tool.execute.after": async (input) => { ... },
};
};OpenCode 2 requires a default-exported Plugin.define, importing Plugin from
@opencode/plugin, and registers tool hooks via
ctx.tool.hook("execute.before" | "execute.after", ...), with ctx.location.directory
replacing the { directory } context argument. Because there is no default
export, the module schema fails to load (hence
SchemaError(Missing key at ["default"])).
Expected behaviour
The generated plugin loads cleanly on OpenCode 2 and records bash invocations.
Suggested fix
Emit the V2 shape in the generator (OpenCode 2). If OpenCode 1 support must be
retained, emit an object that spreads Plugin.define(...) and also provides the
V1 server() entry point (supported by OpenCode 1.18.29+). Keep the "managed
file" header and re-run the writer on repo upgrade so existing projects get the
V2 file.
Working local V2 port (verified: msg="loading plugin" with no subsequent
failed to load plugin in opencode.log):
import { Plugin } from "@opencode/plugin";
const pendingCommands = new Map<string, string>();
export default Plugin.define({
id: "swamp.audit",
async setup(ctx) {
await ctx.tool.hook("execute.before", (event) => {
if (event.tool !== "bash") return;
const command = (event.input as { command?: unknown } | undefined)?.command;
if (typeof command === "string" && event.sessionID) {
pendingCommands.set(event.sessionID, command);
}
});
await ctx.tool.hook("execute.after", async (event) => {
if (event.tool !== "bash") return;
const command = pendingCommands.get(event.sessionID);
pendingCommands.delete(event.sessionID);
if (!command) return;
try {
const payload = JSON.stringify({
tool_name: "bash",
tool_input: { command },
cwd: ctx.location.directory,
session_id: event.sessionID,
});
const proc = Bun.spawn(
["swamp", "audit", "record", "--from-hook", "--tool", "opencode"],
{ stdin: new Blob([payload]) },
);
await proc.exited;
} catch {
// Must never throw — this is a hook
}
});
},
});Shipped
Click a lifecycle step above to view its details.
stack72 commented 10/13/2026, 3:26:49 AM
Thanks @elem103 for reporting this! We shipped: Make the managed OpenCode audit plugin record shell commands on OpenCode 1.3.4+ and OpenCode 2 from one file: keep the named V1 SwampAudit export, add a default export object with id, server (SwampAudit) and an OpenCode 2 setup that hooks execute.after on the shell tool, with a shared record helper and type-only imports. Supported floor OpenCode 1.3.4 (older 1.x cannot load any file OpenCode 2 accepts). Fix the changed-file path the upgrade reports, make the doctor flag a plugin that predates OpenCode 2 support with a version-aware hint, and document the range.. The fix has been merged and a release is on its way. We appreciate your contribution to swamp.
Sign in to post a ripple.