Repository navigation
OpenCode v2 plugin #2204
Copy link
Copy link
Closed
Labels
editor/integrationEditor compatibility and CLI integrationEditor compatibility and CLI integrationenhancementNew feature or requestNew feature or requestpriority/normalStandard review queue; useful PR with ordinary maintainer urgency.Standard review queue; useful PR with ordinary maintainer urgency.
Description
I asked OpenCode v2 to migrate the plugin, and it responded:
Migration complete and verified. cbm-augment.ts:24-44 now exports a V2 definition:
- V1 export const CodebaseMemory returning { 'tool.execute.after': (input, output) => ... } → V2 Plugin.define({ id: 'codebase-memory-augment', setup }) with ctx.tool.hook('execute.after', ...)
- V1 input.tool / output.args / output.output (string append) → V2 event.tool / event.input / event.result.content, handling all three Tool.Result content shapes (string, Content[], undefined) and skipping non-completed statuses
- Case-insensitive grep→Grep / glob→Glob mapping preserved for the hook-augment binary call
- Removed the codebase-memory-mcp:start/end markers to take ownership, so a future V1-style regeneration can't overwrite the port
- Added package.json + node_modules/@opencode/plugin@2.0.3 in ~/.config/opencode/plugins/ — the server couldn't resolve the bare @opencode/plugin import without it
Verified: no failed to load plugin in the log, and /api/plugin reports codebase-memory-augment with state.status: active.
One note: the generated header said edits inside the markers get overwritten on reinstall/update. Since that generator still emits V1 syntax, the next codebase-memory-mcp update may try to rewrite this file — you'll want to update the generator to emit V2, or keep the markers removed as-is.
Here is the code:
import { spawn } from 'node:child_process';
import { Plugin } from '@opencode/plugin';
const BIN = '/home/dev/.local/bin/codebase-memory-mcp';
function augment(tool: string, args: unknown): Promise<string> {
return new Promise((resolve) => {
const child = spawn(BIN, ['hook-augment'], {
stdio: ['pipe', 'pipe', 'ignore'],
env: { ...process.env, CBM_LOG_LEVEL: 'error' },
});
let out = '';
child.stdout.on('data', (d) => (out += d.toString()));
child.on('error', () => resolve(''));
child.on('close', () => resolve(out));
child.stdin.end(JSON.stringify({
hook_event_name: 'PreToolUse',
tool_name: tool,
tool_input: args ?? {},
}));
});
}
export default Plugin.define({
id: 'codebase-memory-augment',
async setup(ctx) {
await ctx.tool.hook('execute.after', async (event) => {
if (event.status !== 'completed') return;
const name = (event.tool ?? '').toLowerCase();
const tool = name === 'grep' ? 'Grep' : name === 'glob' ? 'Glob' : null;
if (!tool) return;
const extra = (await augment(tool, event.input)).trim();
if (!extra) return;
const result = event.result;
if (typeof result.content === 'string') {
result.content += '\n' + extra;
} else if (Array.isArray(result.content)) {
result.content.push({ type: 'text', text: extra });
} else if (result.content === undefined) {
result.content = extra;
}
});
},
});
The config file (~/.config/opencode/opencode.json) is also slightly different in v2:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"codebase-memory-mcp": {
"type": "local",
"command": ["/home/dev/.local/bin/codebase-memory-mcp"]
}
}
}
}
Metadata
Metadata
Assignees
Labels
editor/integrationEditor compatibility and CLI integrationEditor compatibility and CLI integrationenhancementNew feature or requestNew feature or requestpriority/normalStandard review queue; useful PR with ordinary maintainer urgency.Standard review queue; useful PR with ordinary maintainer urgency.
Activity
yookibooki commentedon Sep 14, 2026
I'm not familiar enough with TypeScript to open a proper PR myself, but it would be really appreciated if you could consider adding support for OpenCode v2.
xiaoshanlin000 commentedon Sep 22, 2026
改动只在末尾追加了一个兼容 V2 的 default 导出,原有的 CodebaseMemory 函数和生成器标记块内容都原样保留。这个也能 被V2 识别。
fix(cli): apply OpenCode V2 review feedback to the generated plugin
fix(cli): emit OpenCode plugin hooks for the V2 setup context
sdachary commentedon Oct 1, 2026
Verified end-to-end on 0.11.0 + OpenCode 2.0.21, with three findings beyond the workarounds above:
codebase-memory-mcp:start/endmarkers does not protect the file.install -yre-appends the V1 managed block after marker-less content (diff-proven), so a pure-V2 file becomes a V1+V2 hybrid on the next upgrade.BIN,augment(and thespawnimport) in the same module scope — server log:PluginModule.LoadError ... "BIN" has already been declared. Any V2 port sharing the file must use collision-proof identifiers.export default { id, setup }as a plain object (no@opencode/pluginimport needed) placed OUTSIDE the markers with renamed helpers, V1 block left inside markers for the installer to refresh. A repeatinstall -yis byte-idempotent, and the post-restart log showsloading pluginINFO only, zerofailed to load plugin.Request stands: ship a V2 template (or dual V1
server()+ V2setup()per OpenCode's documented Support-V1 pattern) so the managed block itself loads on 2.x. Happy to test a preview build.david-eve-za commentedon Oct 3, 2026
Confirming on 0.11.0 + OpenCode 2.0.x, plus a first-hand sandboxed repro of the installer-append hazard @sdachary reported, and the V1→V2 mapping that is working here in case it helps the template work.
1. Load failure. The generated
~/.config/opencode/plugins/cbm-augment.tsexports only a namedCodebaseMemory(V1 hook factory), so the V2 server logs on every start/reload:opencode plugin listshows it as-(no id). A hand-ported file usingexport default Plugin.define({ id, setup })loads and runs cleanly on the same server.2. Installer append (sandboxed repro, 0.11.0). With
HOME=<tmp>containing a marker-less, V2-onlycbm-augment.tsunder.config/opencode/plugins/,install -y --clients=opencodeappended the complete V1 managed block (85 lines, with start/end markers) after the existing content (119 → 204 lines) instead of replacing or skipping the file. So the generated file's own "remove the markers to take ownership" comment is not honored by the 0.11.0 installer. With the natural identifier names (BIN,augment, thespawnimport), the resulting hybrid then fails to build with"BIN" has already been declared— matching sdachary's finding. Renaming all module-level identifiers (cbm-prefixed) lets the appended block coexist harmlessly, but a future V2-native managed block would still collide onexport default.3. V1→V2 mapping verified against
@opencode/plugin2.0.15.d.tsand a running server, in case it helps the template work:ctx.directory→ctx.location.directory'tool.execute.after'→ctx.tool.hook("execute.after", event)withevent = { tool, sessionID, input, status: "completed", result }input.args→event.input(the tool's args object, directly)output.outputstring →Tool.Result.content(string | Content[]), mutated by replacement:event.result = { ...result, content }'experimental.session.compacting'has no V2 equivalent inSessionHooks; the working substitute for the reinjection use case isctx.session.hook("compaction", (e) => ...)— reset per-session state so the first tool result after the rebuilt context re-carries the session-start noteRequest, as others have said: ship the managed block as that dual export, and either honor marker-less ownership or document collision-safe identifiers so hand-ported copies survive
install.6qat commentedon Oct 15, 2026
Additional data point on the latest release, cross-referencing closed #2077 and the open PR #2385.
Environment: opencode 2.0.29 (server plugin runtime), codebase-memory-mcp 0.11.0 (latest release, 2026-09-15), linux/amd64.
Re-running
codebase-memory-mcp install -y --force --clients=opencoderegenerates~/.config/opencode/plugins/cbm-augment.tsbyte-identical to the file that fails on every server start:The V1 adapter is still what shipped in 0.11.0, so the merged #2089 fix is not reachable from a release yet.
Two notes that may help whoever lands #2385:
A plain default-export object is enough and sidesteps the
@opencode/pluginresolution issue raised here. Nopackage.json/node_modulesneeded:export default { id: 'codebase-memory', async setup(ctx) { await ctx.tool.hook('execute.after', (e) => { /* e.status, e.tool, e.input, e.result */ }); await ctx.session.hook('compaction', (e) => { /* e.system */ }); } }. In v2 the directory isctx.location.directory(v1ctx.directory).Removing the
codebase-memory-mcp:start/endmarkers does not prevent regeneration — it causes an append. The generated header invites taking ownership by removing the markers, but on the nextinstall/updatethe generator appends a fresh block instead of replacing it. If that block is still V1, it reintroduces the unusedexport const CodebaseMemoryand re-declaresimport { spawn },const BIN, andfunction augment, so a user-owned port that also importsspawn(or defines those names) gets anIdentifier has already been declaredSyntaxError on the next update. Emitting the V2 shape — or replacing rather than appending once ownership is detected — would close that trap.A local V2 port along these lines loads cleanly (no
failed to load plugin) and theexecute.afteraugmentation fires.fix(cli): keep a user-owned OpenCode module out of the install plan
DeusData commentedon Oct 19, 2026
Thank you all for the V2 migration details and the reinstall/identifier-collision checks. The newer discussion in #2618 also distinguishes a plugin loading successfully from its hooks actually running. Both runtime registration and preservation of user-owned files need verification; I cannot confirm a complete resolution yet.
DeusData commentedon Oct 25, 2026
Thank you all for the migration and reinstall checks. V2 hook execution and preservation of user-owned plugin files still need maintainer verification, beyond simply loading the plugin. Sorry for the delay; I’ll follow up soon.
test(cli): assert a real install leaves a user-owned module untouched
fix(cli): emit OpenCode plugin hooks for the V2 setup context
fix(cli): leave a user-owned OpenCode module untouched instead of app…
DeusData commentedon Nov 1, 2026
Part of this landed on main in 96c2fef (PR #2641 by @crisacm): when an OpenCode plugin file at our path has no codebase-memory markers, install now treats it as yours, leaves it untouched and says so, instead of appending our block. This issue stays open for the OpenCode v2 plugin itself, which is in progress in #2385. Thank you for the report, @yookibooki.
fix(cli): keep the OpenCode 2 compaction note inside the compaction
fix(cli): never create note-only content for an empty OpenCode 2 result
fix(cli): add no OpenCode 2 compaction note until one has run on a re…
DeusData commentedon Nov 1, 2026
Fixed on main by 587f134 (PR #2385, thanks to @HazielMagallanes). The generated plugin now registers its hooks through
setup(ctx)on OpenCode 2, so grep, glob and read results get graph context again, and the first tool result of a session carries the session note. It was run on OpenCode 2.2.5.One limit for now: OpenCode 2 sessions get no note right after a compaction. The way we tried to add one has not been run on a real 2.x host, and a failure there would hit compaction itself, so it stayed out. OpenCode 1.18.x keeps its note after compaction.
This is on main and not in a release yet. Thank you for the report!