Add OpenCode plugin support - #15
Conversation
Register OpenCode entrypoints for each plugin, wire shared skill/MCP/context hooks, and document local OpenCode installation.
There was a problem hiding this comment.
Pull request overview
Adds OpenCode local plugin support by introducing a shared OpenCode adapter (common/opencode-plugin.js), generating per-plugin adapter copies + lightweight opencode-plugin.js entrypoints, and updating docs/tests/version metadata so OpenCode users can install plugins by pointing config at each plugin directory entrypoint.
Changes:
- Introduces
createOpenCodePlugin()adapter (shared + per-plugin generated copies) and adds per-pluginopencode-plugin.jsentrypoints. - Updates plugin documentation and repository docs to include OpenCode installation/config guidance.
- Extends repository integrity tests and sync tooling to cover OpenCode entrypoints and generate JS helpers with
//comment headers.
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
common/opencode-plugin.js |
Adds shared OpenCode adapter for skills/MCP/session context/guardrails/shell env. |
scripts/sync-plugin-common.py |
Generates // headers for .js/.ts common helper copies. |
tests/test_repository_integrity.py |
Adds integrity checks for OpenCode entrypoints + JS syntax/export validation. |
README.md |
Documents OpenCode installation flow via opencode-plugin.js entrypoints. |
CLAUDE.md |
Documents OpenCode entrypoint presence and OpenCode test guidance. |
.claude-plugin/marketplace.json |
Bumps plugin versions to include OpenCode support releases. |
PluginBase/opencode-plugin.js |
Adds OpenCode entrypoint for PluginBase. |
PluginBase/common/opencode-plugin.js |
Adds generated OpenCode adapter copy into PluginBase package. |
PluginBase/info.json |
Records PluginBase changelog entry for OpenCode adapter/template. |
PluginBase/.claude-plugin/plugin.json |
Bumps PluginBase version. |
XcodeBuildTools/opencode-plugin.js |
Adds OpenCode entrypoint for XcodeBuildTools. |
XcodeBuildTools/common/opencode-plugin.js |
Adds generated OpenCode adapter copy into XcodeBuildTools package. |
XcodeBuildTools/README.md |
Adds OpenCode installation instructions and changelog entry. |
XcodeBuildTools/info.json |
Records OpenCode support in version history. |
XcodeBuildTools/.claude-plugin/plugin.json |
Bumps XcodeBuildTools version. |
SwiftScaffolding/opencode-plugin.js |
Adds OpenCode entrypoint for SwiftScaffolding. |
SwiftScaffolding/common/opencode-plugin.js |
Adds generated OpenCode adapter copy into SwiftScaffolding package. |
SwiftScaffolding/README.md |
Adds OpenCode installation instructions and changelog entry. |
SwiftScaffolding/info.json |
Records OpenCode support in version history. |
SwiftScaffolding/.claude-plugin/plugin.json |
Bumps SwiftScaffolding version. |
MarvinOutputStyle/opencode-plugin.js |
Adds OpenCode entrypoint for MarvinOutputStyle. |
MarvinOutputStyle/common/opencode-plugin.js |
Adds generated OpenCode adapter copy into MarvinOutputStyle package. |
MarvinOutputStyle/README.md |
Adds OpenCode installation instructions and changelog entry. |
MarvinOutputStyle/info.json |
Records OpenCode support in version history. |
MarvinOutputStyle/.claude-plugin/plugin.json |
Bumps MarvinOutputStyle version. |
iOSSimulator/opencode-plugin.js |
Adds OpenCode entrypoint for iOSSimulator. |
iOSSimulator/common/opencode-plugin.js |
Adds generated OpenCode adapter copy into iOSSimulator package. |
iOSSimulator/README.md |
Adds OpenCode installation instructions and changelog entry. |
iOSSimulator/info.json |
Records OpenCode support in version history. |
iOSSimulator/.claude-plugin/plugin.json |
Bumps iOSSimulator version. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| copyOptionalFields(server, translated, ["cwd", "enabled", "timeout"], context); | ||
|
|
||
| const environment = isObject(server.environment) ? server.environment : server.env; | ||
| translated.environment = { |
There was a problem hiding this comment.
These compatibility values intentionally come after the source environment so plugin metadata cannot override host-owned paths. CLAUDE_PLUGIN_DATA stays outside the checkout, and CLAUDE_PROJECT_DIR follows the active OpenCode worktree or directory context.
| self.assertTrue(entrypoint.exists()) | ||
| self.assertEqual(expected, entrypoint.read_text(encoding="utf-8")) | ||
|
|
||
| def test_node_is_available_for_opencode_runtime_checks(self): |
There was a problem hiding this comment.
This explicit availability test is intentional. The runtime and export tests still skip defensively when run in isolation, but the repository-wide suite must fail when Node is missing so CI cannot report success without exercising the OpenCode checks.
lukelabonte
left a comment
There was a problem hiding this comment.
Additional context for three non-obvious compatibility and lifecycle decisions.
| "opencode.xcodebuildtools.pending-cleanups", | ||
| ); | ||
| const pendingSandboxCleanups = | ||
| globalThis[XCODE_SANDBOX_PENDING_CLEANUPS_KEY] instanceof Set |
There was a problem hiding this comment.
This process-wide registry survives module reloads so a replacement plugin instance can see cleanup detached by an older one. It supplements the on-disk cleanup marker, allowing the next stale sweep to retry a failed asynchronous removal without waiting for the owner grace period.
| pluginRoot, | ||
| pluginData: () => pluginDataDirectory(pluginRoot, state), | ||
| projectDir: | ||
| input?.worktree !== "/" || input?.project?.vcs === "git" |
There was a problem hiding this comment.
OpenCode uses / as the worktree sentinel for non-Git projects, but / can also be a valid Git worktree. Prefer directory only for the non-Git sentinel so ${CLAUDE_PROJECT_DIR} and MCP cwd stay scoped to the active project.
| if (!record) return Promise.resolve(); | ||
| if (!record.terminationPromise) { | ||
| record.terminating = true; | ||
| record.terminationPromise = terminateXcodeMcpApproval(record).finally(() => { |
There was a problem hiding this comment.
Keep the helper record mapped until this shared promise settles. session.deleted and dispose() can race; both must await the same SIGTERM → SIGKILL path, and removing the record on the child’s early exit would let disposal miss a still-live process group.
Overview
Adds local OpenCode support to each marketplace plugin without changing the existing Claude Code plugin format. OpenCode users can point their configuration at a plugin's
opencode-plugin.jsentry point, which adapts the package's existing skills, MCP servers, session context, command guardrails, and shell setup to OpenCode hooks.The implementation also hardens the XcodeBuildTools integration around sandbox containment, stale-session cleanup, bounded process termination, non-Git project paths, and prompt-time executable trust.
Technical Details
common/opencode-plugin.jsadapter, five synchronized package-local copies, and thin per-plugin entry points.CLAUDE_PLUGIN_ROOT,CLAUDE_PLUGIN_DATA, andCLAUDE_PROJECT_DIRvalues.SIGTERM→SIGKILLcleanup across session deletion and plugin disposal.