// @clack/prompts is ESM-only and currently imports node:process in a shape // that blocks a future `bun build`; this CLI intentionally executes TS in Bun. import * as p from "@clack/prompts"; import { stdin, stdout } from "node:process"; import type { Readable, Writable } from "node:stream"; import type { TerminalUi } from "./wizard-ui.ts"; interface ClackIo { input?: Readable; output?: Writable; } /** Interactive WizardUi driver. Callers gate this on TTY input and output. */ export function clackUi(io: ClackIo = {}): TerminalUi { const input = io.input ?? stdin; const output = io.output ?? stdout; const promptIo = { input, output }; let closed = false; let started = false; const start = (): void => { if (started) return; started = true; p.intro("omp-conductor setup", { output }); }; return { close: (message) => { if (closed) return; closed = true; if (started) p.outro(message ?? "Setup finished.", { output }); }, notify: (message, type = "info") => { start(); if (message.includes("\n")) { const title = type === "warning" ? "Warning" : type === "error" ? "Error" : undefined; p.note(message, title, { output }); return; } if (type === "warning") p.log.warning(message, { output }); else if (type === "error") p.log.error(message, { output }); else p.log.info(message, { output }); }, confirm: async (title, message) => { start(); const result = await p.confirm({ ...promptIo, message: `${title}\n${message}`, initialValue: false, }); return p.isCancel(result) ? undefined : result; }, input: async (title, placeholder) => { start(); const result = await p.text({ ...promptIo, message: title, placeholder, defaultValue: placeholder, }); return p.isCancel(result) ? undefined : result; }, select: async (title, options, dialogOptions) => { start(); const index = Math.max( 0, Math.min(dialogOptions?.initialIndex ?? 0, options.length - 1), ); const initialValue = options[index]?.label; const result = await p.select({ ...promptIo, message: title, options: options.map((option) => ({ value: option.label, label: option.label, hint: option.description, })), initialValue, }); return p.isCancel(result) ? undefined : result; }, }; }