Skip to content

fix(cli): emit the OpenCode plugin as a V2 default export - #2089

Open
AmirF194 wants to merge 1 commit into
DeusData:mainfrom
AmirF194:fix/2077-opencode-v2-plugin-shape
Open

fix(cli): emit the OpenCode plugin as a V2 default export#2089
AmirF194 wants to merge 1 commit into
DeusData:mainfrom
AmirF194:fix/2077-opencode-v2-plugin-shape

Conversation

@AmirF194

@AmirF194 AmirF194 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

OpenCode's V2 plugin loader only reads the default export and requires an id plus a setup() or effect() function. cbm_client_adapter_opencode still generated the old named export (export const CodebaseMemory = async (ctx) => {...}), so every server start failed with SchemaError: Missing key at ["default"], and the workaround gets overwritten on every install/update since the file is regenerated.

Wrapped the same hook logic in a default export with id: 'codebase-memory-augment' and async setup(ctx); the returned hooks object (tool.execute.after, experimental.session.compacting) is unchanged.

Added a test asserting the generated module has the default export shape and no longer contains the old named export. Ran scripts/test.sh --suites agent_clients on Ubuntu 24.04: the new test fails without the change and the suite is green on the branch (38 tests).

Fixes #2077

OpenCode's V2 loader validates only the module's default export,
expecting {id, setup()|effect()}. cbm_client_adapter_opencode still
emitted the V1 named export (export const CodebaseMemory = ...), so
every server start failed with SchemaError: Missing key at ["default"].

Wrap the existing hook logic in a default export with an id and a
setup(ctx) function; the returned hooks object is unchanged.

Fixes DeusData#2077

Signed-off-by: Amir Fathi <amirfathi.me@gmail.com>
@AmirF194
AmirF194 requested a review from DeusData as a code owner September 7, 2026 11:50
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

@DeusData

DeusData commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Thanks for taking #2077 on so quickly, and for the RED/GREEN evidence — the diagnosis is right: OpenCode's newer config loader validates default against { id, effect | setup }, so a default export is the way to go.

One thing turned up when running the generated module through OpenCode's own loaders, though. There are two of them, and the one that actually fires tool.execute.after is the server runtime (packages/opencode/src/plugin/index.tsreadV1Plugin in shared.ts). It reads default.server, not default.setup. With { id, setup } it throws Plugin … must default export an object with server(), and the V2 loader that accepts { id, setup } calls setup() but drops whatever it returns (packages/core/src/plugin/promise.ts:91) — its context has no tool domain yet. So on every 1.18.x checked (v1.18.18 / 25 / 29 and dev) this shape loads without an error line but registers zero hooks — the silent no-op #616 taught us to fear.

The shape that satisfies both loaders:

export default {
  id: 'codebase-memory-augment',
  // server runtime: reads default.server and dispatches the hooks it returns
  server: async (ctx) => { /* the existing hook object */ },
  // V2 config loader: requires id + setup|effect; no tool domain yet, so nothing to register
  setup() {},
};

Could you switch async setup(ctx) { to server: async (ctx) =>, add the empty setup() {}, and update the new test to pin server: and setup() {} (keeping the "no export const CodebaseMemory" check)? Also, #1933 lands first and changes output?.argsinput?.args in the same block, so after rebasing please keep input?.args inside your rewritten block — its three assertions will tell you if it slipped. Then this is a merge. The red test-windows-guards leg on your head is the known cold-storm daemon-spawn race, unrelated to this diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generated OpenCode plugin uses V1 export shape; V2 loader rejects it (SchemaError: Missing key at ["default"])

2 participants