From 01e4ebe2826d022d83994eb6fa54cd0b40879ff7 Mon Sep 17 00:00:00 2001 From: Semin Alkic Date: Wed, 4 Feb 2026 14:18:57 +0100 Subject: [PATCH] fix(clipboard): write OSC52 to /dev/tty instead of stdout Fixes clipboard copy failing in terminal multiplexers like Zellij, tmux, and screen. These tools intercept OSC52 escape sequences written to stdout but do not forward them to the outer terminal. Writing to /dev/tty (or SSH_TTY when connected via SSH) bypasses the multiplexer's PTY layer and sends OSC52 directly to the terminal emulator. Falls back to stdout if /dev/tty is unavailable (e.g., on Windows). Fixes #12082 Co-Authored-By: Claude Opus 4.5 --- .../opencode/src/cli/cmd/tui/util/clipboard.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts index 4be6787346d..7057aec4b00 100644 --- a/packages/opencode/src/cli/cmd/tui/util/clipboard.ts +++ b/packages/opencode/src/cli/cmd/tui/util/clipboard.ts @@ -4,11 +4,15 @@ import clipboardy from "clipboardy" import { lazy } from "../../../../util/lazy.js" import { tmpdir } from "os" import path from "path" +import fs from "fs" /** * Writes text to clipboard via OSC 52 escape sequence. * This allows clipboard operations to work over SSH by having * the terminal emulator handle the clipboard locally. + * + * Writes to /dev/tty directly to bypass terminal multiplexers + * (Zellij, tmux, screen) that intercept OSC52 on stdout. */ function writeOsc52(text: string): void { if (!process.stdout.isTTY) return @@ -16,7 +20,18 @@ function writeOsc52(text: string): void { const osc52 = `\x1b]52;c;${base64}\x07` const passthrough = process.env["TMUX"] || process.env["STY"] const sequence = passthrough ? `\x1bPtmux;\x1b${osc52}\x1b\\` : osc52 - process.stdout.write(sequence) + + // Write to /dev/tty directly to bypass terminal multiplexers. + // SSH_TTY is set when connected via SSH, otherwise use /dev/tty. + const ttyPath = process.env["SSH_TTY"] || "/dev/tty" + try { + const fd = fs.openSync(ttyPath, "w") + fs.writeSync(fd, sequence) + fs.closeSync(fd) + } catch { + // Fallback to stdout if /dev/tty is unavailable (e.g., Windows) + process.stdout.write(sequence) + } } export namespace Clipboard {