Skip to content

feat(opencode): add v2 plugin support (--opencode-v2) - #3464

Open
umi008 wants to merge 1 commit into
rtk-ai:developfrom
umi008:feat/opencode-v2-plugin
Open

feat(opencode): add v2 plugin support (--opencode-v2)#3464
umi008 wants to merge 1 commit into
rtk-ai:developfrom
umi008:feat/opencode-v2-plugin

Conversation

@umi008

@umi008 umi008 commented Aug 7, 2026

Copy link
Copy Markdown

Summary

  • Add hooks/opencode/rtk-v2.ts — OpenCode v2 plugin using Plugin.define + ctx.tool.hook('execute.before', ...) with child_process.spawn (v2 doesn't provide the bun-shell $ helper)
  • Add --opencode-v2 flag to rtk init, mutually exclusive with --opencode
  • Embed OPENCODE_V2_PLUGIN via include_str!; thread through all sub-functions

Test plan

  • cargo fmt --all && cargo clippy --all-targets && cargo test --all
  • Manual testing: plugin loads and registers hook with bun (opencode2 runtime)
  • Manual testing: rtk rewrite rewrites commands correctly (exit code 3 handled)
  • Manual testing: rtk init -g --opencode-v2 installs the v2 plugin

Important: All PRs must target the develop branch (not master).
See CONTRIBUTING.md for details.

- Add hooks/opencode/rtk-v2.ts — OpenCode v2 plugin using Plugin.define
  API with ctx.tool.hook('execute.before', ...)
- Add --opencode-v2 flag to rtk init CLI
- Embed OPENCODE_V2_PLUGIN via include_str! in installer
- Thread install_opencode_v2 through run(), run_default_mode(),
  run_hook_only_mode(), run_claude_md_mode(), run_opencode_only_mode()
- ensure_opencode_plugin_installed() now accepts content parameter
- Validate --opencode and --opencode-v2 are mutually exclusive
- Update READMEs for hooks/ and hooks/opencode/
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Uli seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@umi008 umi008 changed the title feat: add opencode v2 plugin support (opencode2) feat(opencode): add v2 plugin support (--opencode-v2) Aug 7, 2026
@Sn0wo2

Sn0wo2 commented Aug 16, 2026

Copy link
Copy Markdown

import { Plugin } from "@opencode-ai/plugin" fails to resolve in the v2 plugin runtime (even with the package installed next to the file). Dropping the import and exporting a plain { id, setup } object fixes it.
Plugin.define is just a type wrapper. Everything else works as-is (verified on next-17444):

import { spawn } from "child_process"

// RTK OpenCode v2 plugin — rewrites commands to use rtk for token savings.
// Requires: rtk >= 0.23.0 in PATH.
//
// OpenCode 2.0 uses a new plugin API (Plugin.define + ctx.tool.hook) and does
// not provide the v1 bun-shell `$` helper, so this variant spawns `rtk`
// directly. `rtk rewrite` exits non-zero even on success, so we read stdout
// from the child instead of relying on exit codes.
//
// This is a thin delegating plugin: all rewrite logic lives in `rtk rewrite`,
// which is the single source of truth (src/discover/registry.rs).
// To add or change rewrite rules, edit the Rust registry — not this file.

function rtkRewrite(command: string): Promise<string> {
  return new Promise((resolve) => {
    const proc = spawn("rtk", ["rewrite", command], { windowsHide: true })
    let out = ""
    proc.stdout.on("data", (d) => {
      out += d
    })
    proc.on("error", () => resolve(""))
    proc.on("close", () => resolve(out.trim()))
  })
}

export default {
  id: "rtk.rewrite",
  setup: async (ctx) => {
    await ctx.tool.hook("execute.before", async (event) => {
      const tool = String(event.tool ?? "").toLowerCase()
      if (tool !== "bash" && tool !== "shell") return
      if (!event.input || typeof event.input !== "object") return

      const command = (event.input as Record<string, unknown>).command
      if (typeof command !== "string" || !command) return

      const rewritten = await rtkRewrite(command)
      if (rewritten && rewritten !== command) {
        ;(event.input as Record<string, unknown>).command = rewritten
      }
    })
  },
}

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.

3 participants