/** * The prompt seam — the one module in this package that talks to * `@clack/prompts` (single-clack-prompt-seam/AC-1 … AC-9). * * `tty-prompt-guard` made the interactivity check *available* at all 16 prompt * call sites; it did not make it *unskippable*. The thirteenth prompt could * still forget, and the only thing standing in its way was a test that spawned * every registered command under a non-TTY stdin — proof that the guard held * that time, not that it cannot be skipped. * * Now every question in the CLI comes through here, and every function below * consults `isInteractive()` **before** touching clack. Opening an unanswerable * prompt therefore requires importing clack directly, which * `test/prompt-seam.test.ts` forbids structurally * (single-clack-prompt-seam/AC-2). * * ## What this module deliberately does not do * * It does not decide what a command does when nobody can answer. Those * behaviours legitimately differ: `init`'s cost-hooks consent adopts the yes * that `--non-interactive` already gives, `add skill` exits 1 so "you said no" * stays distinguishable from "I could not ask", `knowledge * install-merge-driver` fails closed after printing its plan, and `add action`'s * save confirm gets its own arm rather than joining `--non-interactive` (which * writes). A seam that picked one policy would silently change three of them. * So the seam reports `reason: 'no-tty'` and the call site keeps its tier * (single-clack-prompt-seam/AC-6). * * @module */ /** * The outcome of asking a question (single-clack-prompt-seam/AC-7). * * Three outcomes, not two: a real answer, a user cancellation, and *nobody was * there to ask*. The shape follows the `ok | reason` convention the cost * module's `ResolveResult` already set in this package, so a reader meets one * idiom rather than two. * * The predecessor was `string | typeof CANCEL` in `menu/prompt.ts`. It could not * express the third outcome without a second exported sentinel, and a union of * two sentinels is precisely what `ok | reason` exists to avoid. It also made * the dangerous case cheap to write: `if (answer)` reads as "they said yes" even * when `answer` is a sentinel. `!result.ok` cannot be mistaken for a truthy yes, * which matters most at the three sites that write to disk after a confirm. */ export type PromptResult = { ok: true; value: T; } | { ok: false; reason: PromptMiss; }; /** Why a question produced no answer. */ export type PromptMiss = 'cancelled' | 'no-tty'; export interface SelectOption { value: string; label: string; hint?: string; } export interface SelectRequest { message: string; options: SelectOption[]; /** Pre-highlighted option, as `init`'s template and scope pickers use. */ initialValue?: string; } export interface MultiselectRequest { message: string; options: SelectOption[]; /** `false` lets the user submit an empty set — `init`'s platform picker does. */ required?: boolean; /** * Options checked when the prompt opens. * * `init`'s roadmap-status picker preselects all five statuses, which is only * defensible because the whole list is on screen — a stage nobody saw is a stage * nobody declined (issue-tracker-in-init/AC-5). */ initialValues?: string[]; } export interface TextRequest { message: string; placeholder?: string; /** * Return a message to reject the value and re-prompt. * * The value is optional because clack passes `undefined` before the field has * been touched — which is exactly the empty case that must be rejected. */ validate?: (value: string | undefined) => string | undefined; } export interface ConfirmRequest { message: string; initialValue?: boolean; } /** * Everything the CLI does that touches a terminal * (single-clack-prompt-seam/AC-9). * * Injected by optional parameter defaulting to {@link defaultPrompter} — the * same shape `doctor/runner.ts` uses for `ConfirmFn` and the menu used for * `MenuDeps`. Deliberately not a mutable module-level singleton: a global the * tests swap is a cross-test coupling this suite does not need, and it would * reintroduce the "did someone reset it?" failure mode. */ export interface Prompter { select(req: SelectRequest): Promise>; multiselect(req: MultiselectRequest): Promise>; text(req: TextRequest): Promise>; confirm(req: ConfirmRequest): Promise>; /** * Output only. These write and return; they cannot block, so they have no * result type and need no guard. They live here anyway because leaving them on * a direct clack import would defeat AC-1 — `init.ts` would still import the * package (single-clack-prompt-seam/AC-9, D6). */ intro(message: string): void; outro(message: string): void; note(message: string, title?: string): void; /** Whether a prompt can be answered at all. */ interactive(): boolean; } export declare const defaultPrompter: Prompter; /** * Run `run` with every `defaultPrompter` call inside it answered by `p` * (operator-console/AC-42). The operator dashboard runs commands this way, so * `start`'s template select and `init`'s wizard ask in a form drawn over the * dashboard instead of in the scroll-back. * * Scoped, not swappable: the override lives for one call and is gone after it, * so it is not the mutable global the note on `Prompter` rejects — no test can * leave it set for the next. Threading a prompter through `buildProgram` and every * `register*` would touch a dozen files and miss the next one (design D-1). */ export declare function withPrompter(p: Prompter, run: () => Promise): Promise; /** * Reject empty or whitespace-only input for a required positional. * * clack's text prompt yields `''` — not the cancel symbol — when Enter is * pressed on an empty field with no default, so without this the menu would * dispatch a meaningless invocation the user cannot distinguish from a cancel. * Commander would reject a missing ``; this rejects it earlier, where the * user can still fix it. * * Moved here from `menu/prompt.ts` with the rest of the seam. */ export declare function requireNonEmpty(label: string): (value: string | undefined) => string | undefined; //# sourceMappingURL=prompt.d.ts.map