Skip to main content

Ouro AX

ax-voice

A tool's voice: an optional line of character after the facts, for a person at a terminal, never where an agent parses. The kit prints a voice line only when stdout is a terminal, not under --json or --quiet, and not when the CI environment variable is set; never on stderr and never in an error. Each line is one line of plain ASCII, at most 72 characters, and comes after the fact it follows. Variants rotate, and a process celebrates (done) at most once.

npx shadcn@latest add ourostack/ouro-ui/ax-voice#v1.3.10

It installs lib/ouro/ax/voice.ts, with ax-outcome. Its tests are ax-voice-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

checkVoice

Throws at definition time when a fixed voice line breaks the rules; done lines are checked as they are made.

function checkVoice(tool: string, voice: VoiceDef): void

checkVoiceLine

What is wrong with a voice line, or null when it is one line of printable ASCII, at most VOICE_MAX characters.

function checkVoiceLine(line: string): string | null

createVoice

One process's voice: it rotates the variants of a slot and gives at most one done line.

function createVoice(voice: VoiceDef, options: VoiceOptions): Voice

isCi

Whether the CI environment variable says this is a CI run: set, and not empty, "0" or "false".

function isCi(env: Readonly<Record<string, string | undefined>>): boolean

Voice

interface Voice {
  readonly on: boolean;
  line(slot: VoiceSlot, outcome?: Outcome): string | null;
}

VOICE_MAX

The longest voice line, in characters.

const VOICE_MAX: 72

VoiceDef

interface VoiceDef {
  banner?: readonly string[];
  done?: (outcome: Outcome) => readonly string[];
  summary?: readonly string[];
  empty?: readonly string[];
  healthy?: readonly string[];
}

voiceLines

The fixed voice lines (banner, summary, empty and healthy), as help --json lists them for a checker.

function voiceLines(voice: VoiceDef): string[]

VoiceMemory

Remembers the variant a slot last used, so a tool that keeps it between runs rotates across runs too.

interface VoiceMemory {
  last(slot: VoiceSlot): number | null;
  remember(slot: VoiceSlot, index: number): void;
}

voiceOn

Whether a voice line may print: stdout is a terminal, and not --json, --quiet or CI.

function voiceOn(options: Pick<VoiceOptions, "tty" | "json" | "quiet" | "env">): boolean

VoiceOptions

interface VoiceOptions {
  tty: boolean;
  json: boolean;
  quiet: boolean;
  env: Readonly<Record<string, string | undefined>>;
  memory?: VoiceMemory;
  start?: number;
}

VoiceSlot

A tool's voice: an optional line of character after the facts, for a person at a terminal, never where an agent parses. The kit prints a voice line only when stdout is a terminal, not under --json or --quiet, and not when the CI environment variable is set; never on stderr and never in an error. Each line is one line of plain ASCII, at most 72 characters, and comes after the fact it follows. Variants rotate, and a process celebrates (done) at most once.

type VoiceSlot = "banner" | "done" | "summary" | "empty" | "healthy";