Skip to content

fix(opencode): use cross-platform process API - #3330

Closed
lntutor wants to merge 1 commit into
rtk-ai:developfrom
lntutor:fix/opencode-execfile
Closed

lntutor wants to merge 1 commit into
rtk-ai:developfrom
lntutor:fix/opencode-execfile

Conversation

@lntutor

@lntutor lntutor commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • replace the Bun-only $ helper and Unix-only which probe with Node execFile
  • pass rewrite commands as literal subprocess arguments without invoking a shell
  • keep successful rewrite stdout even when the rewrite protocol exits nonzero
  • update OpenCode integration docs and add embedded-plugin regression coverage

Closes #3326

Test plan

  • cargo fmt --all --check
  • cargo clippy --all-targets
  • cargo test
  • Bundle the TypeScript plugin with Bun and invoke it against a fake rtk binary, verifying literal command arguments and nonzero-exit stdout handling

Signed-off-by: Loi Nguyen <vanloi999@gmail.com>
@amnesiaof

Copy link
Copy Markdown

Independent confirmation (Windows 11, OpenCode CLI, rtk 0.47.0)

I reproduced the silent failure from #3326 on OpenCode CLI on Windows — two stacked bugs:

  1. The plugin context provides no Bun $ shell, so $`which rtk` throws TypeError: $ is not a function, the plugin factory crashes silently, and no hooks are registered — nothing surfaced to the user.
  2. The which probe is Unix-only and doesn't exist on Windows.

After working around both, I hit a third subtlety that this PR already handles correctly: rtk rewrite uses non-zero exit codes as status signals (exit 3 with valid stdout such as rtk git status), so a naive try/catch treats successful rewrites as failures. The stdout must be recovered from the exception object.

I independently arrived at the same approach as this PR (Node child_process, literal arguments, no shell, stdout-from-exception, windowsHide) and it works reliably in daily use: hooks fire, commands get rewritten, rtk gain tracks savings.

My working plugin, matching this PR's approach:

import { execFileSync } from "node:child_process"
import type { Plugin } from "@opencode-ai/plugin"

const run = (args: string[], timeout = 5000): string | null => {
  try {
    return execFileSync("rtk", args, { encoding: "utf8", timeout, windowsHide: true })
  } catch (e) {
    // rtk uses non-zero exit codes as status signals; stdout may still be valid
    const out = (e as { stdout?: string | Buffer }).stdout
    return typeof out === "string" || Buffer.isBuffer(out) ? String(out) : null
  }
}

export const RtkOpenCodePlugin: Plugin = async () => {
  if (run(["--version"]) === null) return {}

  return {
    "tool.execute.before": async (input, output) => {
      const tool = String(input?.tool ?? "").toLowerCase()
      if (tool !== "bash" && tool !== "shell") return
      const args = output?.args
      if (!args || typeof args !== "object") return

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

      const rewritten = run(["rewrite", command])
      if (rewritten === null) return
      const trimmed = rewritten.trim()
      if (trimmed && trimmed !== command) {
        ;(args as Record<string, unknown>).command = trimmed
      }
    },
  }
}

Notes:

Would be great to see this merged so rtk init --opencode works out of the box on Windows.

@amnesiaof

Copy link
Copy Markdown

I opened #3837 targeting current develop — same root causes and fix as this PR, with the exit-code-3 stdout handling and a regression test. This comment is just to link the two; happy to have either merged, whichever is easier for maintainers.

@KuSh

KuSh commented Oct 10, 2026

Copy link
Copy Markdown
Collaborator

Closing this in favour of #4187, which covers both the default export the OpenCode loader needs (#2516) and the move from Bun's $ to node:child_process execFile (#3326), on top of the current rtk hook opencode plugin, with OpenCode 2.x support, a test suite and review already under way. Comments or review are welcome on #4187, or reply here if your scope differs.

@KuSh KuSh closed this Oct 10, 2026
@KuSh KuSh added the duplicate This issue or pull request already exists label Oct 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

duplicate This issue or pull request already exists

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rtk init --opencode plugin silently does nothing on Desktop ($ is not a function) and on Windows (which not found)

3 participants