/** * Prompt Utilities * * A thin celilo-facing wrapper over the prompt/message primitives in * `@celilo/cli-display`. Roughly 160 `log.*` call sites and every legacy * direct prompt route through this module, which is what made replacing * `@clack/prompts` a one-file change rather than a 160-file one (celilo#699). * * NOTE: new interactive decisions must NOT be added here. They belong on the * event-bus interview (`services/bus-interview`), which a headless responder * can answer; `test-integration/cli/headless-drivable.test.ts` enforces that * no command reaches for these directly. What remains is the terminal renderer * for a bus question, plus the legacy prompts that predate the interview. */ import { CANCEL, cancel, getActiveDisplay, intro, isCancel, log, note, outro, password, setActiveDisplay, text, confirm as uiConfirm, } from '@celilo/cli-display'; // Re-export so existing imports from `../cli/prompts` keep working. // The singleton itself lives in @celilo/cli-display so module code // running in-process (capability functions, hook scripts) can reach // it via @celilo/capabilities's re-export. export { getActiveDisplay, setActiveDisplay }; export { log }; /** * Interactive prompt wrapper with celilo branding */ export async function celiloIntro(title: string): Promise { intro(title); } export async function celiloOutro(message: string): Promise { outro(message); } /** * Prompt for text input with validation and help text. * * Displays the question, an input field with the placeholder, and any * validation error inline. A cancel (Ctrl-C / Escape) exits the process, which * is the behaviour every existing caller is written against: they treat the * return as an unconditional `string`. */ export async function promptText(options: { message: string; defaultValue?: string; placeholder?: string; validate?: (value: string | undefined) => string | Error | undefined; }): Promise { const result = await text(options); if (isCancel(result)) { cancel('Operation cancelled'); process.exit(0); } return result; } /** * Prompt for password/secret input with confirmation. * Asks the user to enter the value twice and verifies they match. */ export async function promptPassword(options: { message: string; validate?: (value: string | undefined) => string | Error | undefined; }): Promise { while (true) { const result = await password(options); if (isCancel(result)) { cancel('Operation cancelled'); process.exit(0); } const confirmation = await password({ message: `Confirm ${options.message}` }); if (isCancel(confirmation)) { cancel('Operation cancelled'); process.exit(0); } if (result === confirmation) { return result; } log.warn('Passwords do not match. Please try again.'); } } /** * Prompt for confirmation */ export async function promptConfirm(options: { message: string; initialValue?: boolean; }): Promise { const result = await uiConfirm(options); if (result === CANCEL) { cancel('Operation cancelled'); process.exit(0); } return result; } /** * Show note/information box */ export function showNote(message: string, title?: string): void { note(message, title); }