One result, two renderings: --json prints the data, text prints what the command's text() makes of the same data. Long results are cut predictably, and lists page with a cursor, so an agent can read a long answer in pieces.
npx shadcn@latest add ourostack/ouro-ui/ax-output#v1.3.10It installs lib/ouro/ax/output.ts. Its tests are ax-output-tests. Every item runs on Node 22.18 or later as it is copied: the files import each other as ./<file>.ts. The Ouro AX guide shows how they fit together.
Exports
emptyText
The text for a list that came back empty: what is empty, by the filter that emptied it ("Nothing unread."), never "nothing held yet" when the filter is why, then the command to run next. A tool's voice may add its empty line after.
function emptyText(what: string, next?: { command: string; why: string; } | undefined): stringpage
One page of a list. The cursor is the next item's offset, so it is stable while the list is.
function page<T>(items: readonly T[], options: { limit: number; cursor?: string | null | undefined; }) => Page<T>Page
interface Page<T> {
items: T[];
total: number;
nextCursor: string | null;
}render
What stdout gets for a result: the JSON document, or the text. Always one trailing newline.
function render<R>(result: R, options: { json: boolean; text?: ((result: R) => string) | undefined; }) => stringtruncate
Cuts text to at most max characters at a line break where it can, and says so on its last line with how to get the rest.
function truncate(text: string, max: number, more?: string): TruncatedTruncated
interface Truncated {
text: string;
truncated: boolean;
total: number;
}