<!-- Generated by scripts/ax-docs.mts from the JSDoc in registry/ouro/ax/. Do not edit; run `pnpm run docs`. -->

# 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.

```sh
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](ax.md) 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.

```ts
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.

```ts
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.

```ts
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".

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

### `Voice`

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

### `VOICE_MAX`

The longest voice line, in characters.

```ts
const VOICE_MAX: 72
```

### `VoiceDef`

```ts
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.

```ts
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.

```ts
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.

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

### `VoiceOptions`

```ts
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.

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