Skip to main content

Ouro AX

ax-checks

A tool's checks, such as a doctor or a self-check, declared once: progress while they run, the verdict first and the results grouped for a person, the same facts as --json for an agent, the time taken and the exit code. A problem comes with its fix: the exact command, or the thing to do by hand, and never a developer command or a repository path (ax-dev-commands) unless the fix is marked sourceCheckout: true.

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

It installs lib/ouro/ax/checks.ts, with ax-dev-commands, ax-exit-codes, ax-outcome, ax-terminal. Its tests are ax-checks-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

CheckDef

interface CheckDef<C> {
  id: string;
  label: string;
  needs?: readonly string[];
  run(context: C): CheckResult | Promise<CheckResult>;
}

CheckFix

A tool's checks, such as a doctor or a self-check, declared once: progress while they run, the verdict first and the results grouped for a person, the same facts as --json for an agent, the time taken and the exit code. A problem comes with its fix: the exact command, or the thing to do by hand, and never a developer command or a repository path (ax-dev-commands) unless the fix is marked sourceCheckout: true.

type CheckFix = NextStep | { byHand: string; sourceCheckout?: true };

CheckResult

What a check found. A warning or a failure carries its fix; a check that was not run says why, and kind names what it waits on, read after a count ("waiting on something you name", "by hand"), so the not-run line groups them.

type CheckResult =
  | { state: "passed"; detail?: string }
  | { state: "warned" | "failed"; detail: string; fix: CheckFix }
  | { state: "skipped"; detail: string; kind?: string; fix?: CheckFix };

CheckRow

interface CheckRow {
  id: string;
  label: string;
  state: CheckState;
  detail: string;
  fix: CheckFix | null;
  kind: string | null;
  durationMs: number;
}

Checks

interface Checks<C> {
  readonly def: ChecksDef<C>;
  run(context: C, options?: ChecksRunOptions): Promise<ChecksReport>;
}

CHECKS_VERSION

The version of the report's shape; --json carries it as ax, as an outcome or a wait does.

const CHECKS_VERSION: "checks/1"

ChecksDef

interface ChecksDef<C> {
  command: string;
  noun?: readonly [string, string];
  checks: readonly CheckDef<C>[];
}

ChecksReport

One run's facts, the same for the person's text and the agent's --json.

interface ChecksReport {
  ax: typeof CHECKS_VERSION;
  verdict: Verdict;
  summary: string;
  durationMs: number;
  counts: Record<CheckState, number>;
  rows: CheckRow[];
  next: NextStep[];
  exitCode: number;
}

ChecksRunOptions

interface ChecksRunOptions {
  progress?: ProgressStream;
  env?: Readonly<Record<string, string | undefined>>;
  off?: boolean;
  timers?: Timers;
  next?: readonly NextStep[];
}

CheckState

errored is a check that threw before finishing, so the tool broke rather than found something.

type CheckState = "passed" | "warned" | "failed" | "errored" | "skipped";

checksText

The report for a person: the verdict on the first line, then the results grouped (what failed, what warned, what was not run, and what passed with verbose), each problem with its fix, then the next steps. On success it prints the verdict and what is left to act on, and with verbose every check and its time. Off a terminal the marks are words and there is no colour, so the first line reads FAIL 1 of 12 checks failed: ….

function checksText(report: ChecksReport, style: TerminalStyle, options?: { verbose?: boolean | undefined; command?: string | undefined; }): string

defineChecks

Declares a tool's checks once: from this one definition come the progress while they run, the verdict and the grouped results for a person, the --json for an agent, the timing and the exit code. Throws at definition time on an empty list, a repeated id, an empty label or a needs that names no earlier check, and at run time when a warning or failure has no fix, or its fix is the command just run, so a tool's own tests catch both. A fix or a next step that asks for a developer command or a repository path throws too, so a doctor never tells a person to run a repository script.

function defineChecks<C>(def: ChecksDef<C>) => Checks<C>

Example:

const doctor = defineChecks({
  command: "notes doctor",
  checks: [
    { id: "store", label: "Notes file", run: () => ({ state: "passed" }) },
    { id: "sync", label: "Last sync", needs: ["store"], run: () => ({ state: "warned", detail: "last synced 3 days ago", fix: { command: "notes sync", why: "Sync now" } }) },
  ],
});
const report = await doctor.run(context, { progress: process.stderr, env: process.env });

isChecksReport

Whether a value is a checks report: what the X-ray looks for in a read's --json.

function isChecksReport(value: unknown): value is ChecksReport

printChecks

Prints a report the way the kit's commands print a result: --json as one JSON document, otherwise the text for the stream it goes to, each with one trailing newline. Returns the exit code.

function printChecks(report: ChecksReport, io: { stdout: { write(s: string): unknown; isTTY?: boolean | undefined; }; env: Readonly<Record<string, string | undefined>>; }, options?: { json?: boolean | undefined; verbose?: boolean | undefined; command?: string | undefined; }): number

Verdict

type Verdict = "passed" | "warned" | "failed" | "errored";