From 13f3bff91fa6d18512947f8975a0da1fe56a49a6 Mon Sep 17 00:00:00 2001 From: Mariano Baragiola Date: Fri, 17 Jul 2026 11:47:50 -0300 Subject: [PATCH] fix(opencode): plain plugin spinner output outside TTY --- packages/opencode/src/cli/cmd/plug.ts | 15 ++++++++++++++- packages/opencode/test/plugin/install.test.ts | 18 +++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/cli/cmd/plug.ts b/packages/opencode/src/cli/cmd/plug.ts index 1529e9b71df3..0e09f37603f0 100644 --- a/packages/opencode/src/cli/cmd/plug.ts +++ b/packages/opencode/src/cli/cmd/plug.ts @@ -17,6 +17,19 @@ type Spin = { stop: (msg: string, code?: number) => void } +type Output = { + readonly isTTY?: boolean + write: (chunk: string) => unknown +} + +export function plugSpinner(output: Output = process.stdout): Spin { + if (output.isTTY) return spinner() + return { + start: (msg) => output.write(msg + "\n"), + stop: (msg) => output.write(msg + "\n"), + } +} + export type PlugDeps = { spinner: () => Spin log: { @@ -45,7 +58,7 @@ export type PlugCtx = { } const defaultPlugDeps: PlugDeps = { - spinner: () => spinner(), + spinner: () => plugSpinner(), log: { error: (msg) => log.error(msg), info: (msg) => log.info(msg), diff --git a/packages/opencode/test/plugin/install.test.ts b/packages/opencode/test/plugin/install.test.ts index 6dc9175be42a..bf2107c7338f 100644 --- a/packages/opencode/test/plugin/install.test.ts +++ b/packages/opencode/test/plugin/install.test.ts @@ -3,7 +3,7 @@ import fs from "fs/promises" import path from "path" import { parse as parseJsonc } from "jsonc-parser" import { Filesystem } from "@/util/filesystem" -import { createPlugTask, type PlugCtx, type PlugDeps } from "../../src/cli/cmd/plug" +import { createPlugTask, plugSpinner, type PlugCtx, type PlugDeps } from "../../src/cli/cmd/plug" import { tmpdir } from "../fixture/fixture" function deps(global: string, target: string | Error): PlugDeps { @@ -108,6 +108,22 @@ async function read(file: string) { }>(file) } +test("plugin spinner writes plain lines outside a TTY", () => { + for (const isTTY of [false, undefined] as const) { + const chunks: string[] = [] + const spin = plugSpinner({ + isTTY, + write: (chunk) => chunks.push(chunk), + }) + + spin.start("Installing plugin package...") + spin.stop("Plugin package ready", 1) + + expect(chunks.join("")).toBe("Installing plugin package...\nPlugin package ready\n") + expect(chunks.join("")).not.toContain("\x1b") + } +}) + describe("plugin.install.task", () => { test("writes both server and tui config entries", async () => { await using tmp = await tmpdir()