diff --git a/packages/cli/src/commands/transcribe.test.ts b/packages/cli/src/commands/transcribe.test.ts index ea43a07fbb..3bfd142c80 100644 --- a/packages/cli/src/commands/transcribe.test.ts +++ b/packages/cli/src/commands/transcribe.test.ts @@ -39,6 +39,7 @@ describe("transcribe command", () => { new WhisperUnavailableError("whisper-cpp not found. Install: brew install whisper-cpp"), ); vi.spyOn(console, "log").mockImplementation(() => {}); + vi.spyOn(console, "error").mockImplementation(() => {}); }); afterEach(() => { @@ -65,6 +66,15 @@ describe("transcribe command", () => { expect(process.exitCode).toBe(0); expect(trackTranscribeUnavailable).toHaveBeenCalledWith({ optional: true }); expect(trackCommandFailure).not.toHaveBeenCalled(); + + const diagnostic = vi.mocked(console.error).mock.calls.at(-1)?.[0]; + expect(typeof diagnostic).toBe("string"); + if (typeof diagnostic !== "string") throw new Error("Expected stderr diagnostic"); + expect(JSON.parse(diagnostic)).toEqual({ + level: "warning", + reason: "whisper_unavailable", + message: "whisper-cpp not found. Install: brew install whisper-cpp", + }); }); it("imports an SRT and exports an SRT sidecar from transcript.json", async () => { diff --git a/packages/cli/src/commands/transcribe.ts b/packages/cli/src/commands/transcribe.ts index 0c4ec4405f..adbfae1906 100644 --- a/packages/cli/src/commands/transcribe.ts +++ b/packages/cli/src/commands/transcribe.ts @@ -319,6 +319,18 @@ async function transcribeAudio( if (isWhisperUnavailable(err)) { trackTranscribeUnavailable({ optional: opts.optional === true }); if (opts.json) { + // Keep the machine-readable result on stdout, but also surface the + // skipped prerequisite on stderr. Pipeline wrappers commonly discard + // successful stdout, so an optional exit 0 must not become silent. + if (opts.optional) { + console.error( + JSON.stringify({ + level: "warning", + reason: "whisper_unavailable", + message, + }), + ); + } console.log(JSON.stringify({ ok: false, skipped: true, reason: "whisper_unavailable" })); } else { spin?.stop(c.warn(`Captions skipped — ${message}`)); diff --git a/packages/cli/src/whisper/manager.test.ts b/packages/cli/src/whisper/manager.test.ts index 763ccdc0e5..75f4f0ff42 100644 --- a/packages/cli/src/whisper/manager.test.ts +++ b/packages/cli/src/whisper/manager.test.ts @@ -1,5 +1,9 @@ import { describe, it, expect } from "vitest"; -import { WhisperUnavailableError, isWhisperUnavailable } from "./manager.js"; +import { + WhisperUnavailableError, + getInstallInstructions, + isWhisperUnavailable, +} from "./manager.js"; describe("isWhisperUnavailable", () => { it("recognizes WhisperUnavailableError instances", () => { @@ -28,3 +32,12 @@ describe("isWhisperUnavailable", () => { expect(isWhisperUnavailable(undefined)).toBe(false); }); }); + +describe("getInstallInstructions", () => { + it("points Windows users to the prebuilt x64 archive before the source-build fallback", () => { + const instructions = getInstallInstructions("win32"); + + expect(instructions).toContain("whisper-bin-x64.zip"); + expect(instructions).toContain("cmake"); + }); +}); diff --git a/packages/cli/src/whisper/manager.ts b/packages/cli/src/whisper/manager.ts index 7985283382..8b821347e5 100644 --- a/packages/cli/src/whisper/manager.ts +++ b/packages/cli/src/whisper/manager.ts @@ -151,15 +151,15 @@ export function findWhisper(): WhisperResult | undefined { return findFromEnv() ?? findFromSystem() ?? findBuiltBinary(); } -export function getInstallInstructions(): string { - if (platform() === "darwin") { +export function getInstallInstructions(targetPlatform: NodeJS.Platform = platform()): string { + if (targetPlatform === "darwin") { return "brew install whisper-cpp"; } - if (platform() === "linux") { + if (targetPlatform === "linux") { return "Build from source: https://github.com/ggml-org/whisper.cpp#building (requires cmake and a C compiler)"; } - if (platform() === "win32") { - return "Build with cmake: https://github.com/ggml-org/whisper.cpp#building"; + if (targetPlatform === "win32") { + return "Download the prebuilt whisper-bin-x64.zip from https://github.com/ggml-org/whisper.cpp/releases/latest, or build with cmake: https://github.com/ggml-org/whisper.cpp#building"; } return "See https://github.com/ggml-org/whisper.cpp#building"; }