From 005e1d5fd1eb647f63ff7493ceadf0e461765305 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 18 Feb 2026 17:34:38 +0000 Subject: [PATCH] refactor(cli): share styled select prompt helper --- src/cli/update-cli/wizard.ts | 14 ++----- src/commands/reset.ts | 14 ++----- src/terminal/prompt-select-styled.test.ts | 50 +++++++++++++++++++++++ src/terminal/prompt-select-styled.ts | 12 ++++++ 4 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 src/terminal/prompt-select-styled.test.ts create mode 100644 src/terminal/prompt-select-styled.ts diff --git a/src/cli/update-cli/wizard.ts b/src/cli/update-cli/wizard.ts index 597320e841..b5953fda66 100644 --- a/src/cli/update-cli/wizard.ts +++ b/src/cli/update-cli/wizard.ts @@ -1,4 +1,4 @@ -import { confirm, isCancel, select } from "@clack/prompts"; +import { confirm, isCancel } from "@clack/prompts"; import { readConfigFileSnapshot } from "../../config/config.js"; import { formatUpdateChannelLabel, @@ -7,7 +7,8 @@ import { } from "../../infra/update-channels.js"; import { checkUpdateStatus } from "../../infra/update-check.js"; import { defaultRuntime } from "../../runtime.js"; -import { stylePromptHint, stylePromptMessage } from "../../terminal/prompt-style.js"; +import { selectStyled } from "../../terminal/prompt-select-styled.js"; +import { stylePromptMessage } from "../../terminal/prompt-style.js"; import { theme } from "../../terminal/theme.js"; import { pathExists } from "../../utils.js"; import { @@ -19,15 +20,6 @@ import { } from "./shared.js"; import { updateCommand } from "./update-command.js"; -const selectStyled = (params: Parameters>[0]) => - select({ - ...params, - message: stylePromptMessage(params.message), - options: params.options.map((opt) => - opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) }, - ), - }); - export async function updateWizardCommand(opts: UpdateWizardOptions = {}): Promise { if (!process.stdin.isTTY) { defaultRuntime.error( diff --git a/src/commands/reset.ts b/src/commands/reset.ts index 76612fdfcf..6cd8ba3212 100644 --- a/src/commands/reset.ts +++ b/src/commands/reset.ts @@ -1,9 +1,10 @@ -import { cancel, confirm, isCancel, select } from "@clack/prompts"; +import { cancel, confirm, isCancel } from "@clack/prompts"; import { formatCliCommand } from "../cli/command-format.js"; import { isNixMode } from "../config/config.js"; import { resolveGatewayService } from "../daemon/service.js"; import type { RuntimeEnv } from "../runtime.js"; -import { stylePromptHint, stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js"; +import { selectStyled } from "../terminal/prompt-select-styled.js"; +import { stylePromptMessage, stylePromptTitle } from "../terminal/prompt-style.js"; import { resolveCleanupPlanFromDisk } from "./cleanup-plan.js"; import { listAgentSessionDirs, removePath } from "./cleanup-utils.js"; @@ -16,15 +17,6 @@ export type ResetOptions = { dryRun?: boolean; }; -const selectStyled = (params: Parameters>[0]) => - select({ - ...params, - message: stylePromptMessage(params.message), - options: params.options.map((opt) => - opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) }, - ), - }); - async function stopGatewayIfRunning(runtime: RuntimeEnv) { if (isNixMode) { return; diff --git a/src/terminal/prompt-select-styled.test.ts b/src/terminal/prompt-select-styled.test.ts new file mode 100644 index 0000000000..cfc2e4bf06 --- /dev/null +++ b/src/terminal/prompt-select-styled.test.ts @@ -0,0 +1,50 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const { selectMock, stylePromptMessageMock, stylePromptHintMock } = vi.hoisted(() => ({ + selectMock: vi.fn(), + stylePromptMessageMock: vi.fn((value: string) => `msg:${value}`), + stylePromptHintMock: vi.fn((value: string) => `hint:${value}`), +})); + +vi.mock("@clack/prompts", () => ({ + select: selectMock, +})); + +vi.mock("./prompt-style.js", () => ({ + stylePromptMessage: stylePromptMessageMock, + stylePromptHint: stylePromptHintMock, +})); + +import { selectStyled } from "./prompt-select-styled.js"; + +describe("selectStyled", () => { + beforeEach(() => { + selectMock.mockReset(); + stylePromptMessageMock.mockClear(); + stylePromptHintMock.mockClear(); + }); + + it("styles message and option hints before delegating to clack select", () => { + const expected = Symbol("selected"); + selectMock.mockReturnValue(expected); + + const result = selectStyled({ + message: "Pick channel", + options: [ + { value: "stable", label: "Stable", hint: "Tagged releases" }, + { value: "dev", label: "Dev" }, + ], + }); + + expect(result).toBe(expected); + expect(stylePromptMessageMock).toHaveBeenCalledWith("Pick channel"); + expect(stylePromptHintMock).toHaveBeenCalledWith("Tagged releases"); + expect(selectMock).toHaveBeenCalledWith({ + message: "msg:Pick channel", + options: [ + { value: "stable", label: "Stable", hint: "hint:Tagged releases" }, + { value: "dev", label: "Dev" }, + ], + }); + }); +}); diff --git a/src/terminal/prompt-select-styled.ts b/src/terminal/prompt-select-styled.ts new file mode 100644 index 0000000000..40c86285c1 --- /dev/null +++ b/src/terminal/prompt-select-styled.ts @@ -0,0 +1,12 @@ +import { select } from "@clack/prompts"; +import { stylePromptHint, stylePromptMessage } from "./prompt-style.js"; + +export function selectStyled(params: Parameters>[0]) { + return select({ + ...params, + message: stylePromptMessage(params.message), + options: params.options.map((opt) => + opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) }, + ), + }); +}