From 97b7804d02d0a8ffc9afdc64069069baf93ef392 Mon Sep 17 00:00:00 2001 From: Igor Soarez Date: Mon, 3 Aug 2026 21:45:52 +0100 Subject: Make /search-engine speak in print mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ctx.ui.notify is a no-op when hasUI is false, so `pi -p /search-engine` changed the stored engine and printed nothing — the command looked broken. Print mode now falls back to stdout. JSON mode deliberately does not: its stdout is a structured event stream and a stray line would corrupt it. --- src/index.ts | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/index.ts b/src/index.ts index 4462075..29cdaaf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -126,6 +126,26 @@ const engineTable = (): string => .map((e) => ` ${e.id.padEnd(11)} ${e.label.padEnd(14)} recency: ${describeRecencySupport(e).padEnd(24)} ${e.note}`) .join("\n"); +/** + * Say something from a command, in whichever mode pi is running. + * + * `ctx.ui.notify` is a no-op when `hasUI` is false, which means `pi -p + * "/search-engine"` would change the stored engine and print absolutely nothing + * — the command would look broken. Print mode falls back to stdout. JSON mode + * deliberately does not: stdout there is a structured event stream, and a stray + * line would corrupt it. + */ +const say = (ctx: { hasUI: boolean; mode: string; ui: { notify: (m: string, t?: "info" | "warning" | "error") => void } }, + message: string, + level: "info" | "warning" | "error" = "info", +): void => { + if (ctx.hasUI) { + ctx.ui.notify(message, level); + return; + } + if (ctx.mode === "print") console.log(message); +}; + export default function (pi: ExtensionAPI): void { // Nothing is dialled here. pi runs extension factories in invocations that // never start a session, so opening a socket from a factory would leave one @@ -146,24 +166,24 @@ export default function (pi: ExtensionAPI): void { const current = await describeConfiguredEngine(); if (raw === "") { - ctx.ui.notify( + say( + ctx, `castle_cdp_search engine: ${current.id} (${current.source})\n\n${engineTable()}\n\n` + `Set with: /search-engine Clear with: /search-engine default\n` + `Stored in ${storedEngineLocation()}; ${ENGINE_ENV_VAR} overrides it for one process.`, - "info", ); return; } if (raw.toLowerCase() === "default" || raw.toLowerCase() === "clear") { await clearStoredEngine(); - ctx.ui.notify(`castle_cdp_search engine reset to the built-in default (${DEFAULT_ENGINE}).`, "info"); + say(ctx, `castle_cdp_search engine reset to the built-in default (${DEFAULT_ENGINE}).`); return; } const id = parseEngineId(raw); if (!id) { - ctx.ui.notify(`Unknown search engine "${raw}". Expected one of: ${engineAliases().join(", ")}`, "error"); + say(ctx, `Unknown search engine "${raw}". Expected one of: ${engineAliases().join(", ")}`, "error"); return; } @@ -171,7 +191,8 @@ export default function (pi: ExtensionAPI): void { // Saying so explicitly, because the variable silently wins otherwise and // the user would reasonably assume the command had taken effect. const pinned = process.env[ENGINE_ENV_VAR]?.trim(); - ctx.ui.notify( + say( + ctx, pinned ? `Saved ${id}, but ${ENGINE_ENV_VAR}=${pinned} is set and overrides it for this process.` : `castle_cdp_search will use ${id}.`, -- cgit v1.3.1