ask

Source: src/ask.ts

Asking the user a question - the one place a workflow may block on a human.

This is a port, not an implementation: no pi, no terminal, no readline. The extension supplies a pi dialog, an example supplies readline, a test supplies a scripted array. That is the same rule as everywhere else here (a feature must work from a script before it is exposed in the TUI), applied to input rather than to display.

The shape is deliberately Claude Code’s: one question at a time, a handful of concrete options, free text always available, and a way to say “enough, get on with it”.

Answer

type

export type Answer = {
	/** The question as it was asked, so a transcript reads on its own. */
	question: string;
	/** A choice's `label`, or free text when `custom`. */
	answer: string;
	/** True when the user typed their own answer instead of picking one. */
	custom: boolean;
};

What came back: the question, the answer, and whether it was typed or picked.

Asking

type

export type Asking = {
	/**
	 * What the card takes. `open`, the default: one of the options, or a typed
	 * answer (`custom`). `closed`: one of the options only. `confirm`: yes or
	 * no, answered `"yes"` or `"no"`. `text`: a typed answer, `""` when left
	 * empty.
	 */
	readonly form?: "open" | "closed" | "confirm" | "text";
	/** Shown above the question, in order. */
	readonly context?: readonly Shown[];
	/** The visit path of whoever asks, when more than one may. */
	readonly visit?: string;
	/**
	 * The label of "that's enough". Absent: offered, in the card's own words.
	 * `false`: not offered, and declining is the run's stop.
	 */
	readonly enough?: string | false;
	/** Aborted when the question no longer stands, a timeout or a stop: the card closes, and its answer is not read. */
	readonly signal?: AbortSignal;
};

How a question is put, beyond the question itself: what a flow’s ask node says of its card. Nothing said is the interview’s card.

AskUser

type

export type AskUser = (question: Question, asking?: Asking) => Promise<Answer | undefined>;

Puts one question to the user.

Returning undefined is the person declining. Where “that’s enough” is offered, it is the submit: the user has decided there is enough to go on. It is not an error and not a cancellation of what came before - every answer already given still counts. Where it is not, it is the stop.

Choice

type

export type Choice = {
	/** What the user reads and picks. A few words, not a sentence. */
	label: string;
	/** What choosing it commits to, when the label alone leaves that open. */
	description?: string;
};

One proposed answer. description says what picking it implies.

Question

type

export type Question = {
	/** Very short label for the question - a chip, not a sentence. */
	header?: string;
	/** The question itself, asked whole - it is also what a transcript keeps. */
	question: string;
	/** Two to four concrete, mutually exclusive options. */
	options: Choice[];
};

One question, in the shape a card can draw and a script can answer.

Shown

type

export type Shown = {
	/** The address it was read from, as the section's title. */
	readonly name: string;
	/** What it holds: a text as it is, a typed value as JSON, `""` when there is nothing. */
	readonly body: string;
};

A value shown above a question, under its name, so the person answers knowing what it is about.