Skip to content

yggdrasil.cli.style

style

Shared ANSI styling, logo, and animations for all ygg CLIs.

No external dependencies — pure sys.stdout ANSI escape sequences. Auto-disables color when stdout is not a TTY.

Spinner

Spinner(
    text: str = "",
    frames: tuple[str, ...] = _BRAILLE,
    interval: float = 0.08,
    color: str = "36",
)

Animated spinner that runs in a background thread.

set_progress

set_progress(current: int, total: int) -> None

Drive the inline progress bar (current of total). Renders on the next animation frame; safe to call from the main thread mid-spin.

ProgressBar

ProgressBar(
    total: "int | None" = None,
    label: str = "",
    *,
    width: int = 24,
    color: str = "36"
)

A live single-line progress-bar component.

Determinate when a fraction is known (update(frac=0.4) or update(current, total)) — a filled bar with a percentage; otherwise indeterminate — an animated sweep, so it's useful even when the total isn't known yet. TTY-only (off a terminal it stays quiet, like the spinner). Drives :func:track, the bridge from an :class:~yggdrasil.dataclasses.awaitable.Awaitable.

with ProgressBar(total=len(files), label="indexing") as bar:
    for i, f in enumerate(files, 1):
        index(f); bar.update(i)

LiveDisplay

LiveDisplay()

Render a block of lines that updates in place (a live dashboard).

Each :meth:update rewrites the block: the cursor jumps back to its top (CSI nF), every line is cleared (CSI 2K) and redrawn, so a fleet of agents shows live status without scrolling the transcript. TTY-only — off a terminal the escapes are meaningless, so updates are dropped and the caller's final summary (plain out) carries the result instead.

LogFormatter

LogFormatter(*, show_name: bool = True, datefmt: str = '%H:%M:%S')

Bases: Formatter

Format log records in the ygg CLI look::

12:34:56  ●  yggdrasil.databricks.cli  seeding workspace

A dim clock, a level-colored glyph, the muted logger name, then the message — mirroring :func:event. Honors the module's color gate (:func:force_color / NO_COLOR / TTY autodetect), so a non-ANSI sink (file, pipe) degrades to plain text. Tracebacks / stack info append in the standard form. Set show_name=False to drop the logger name for terse single-app output.

force_color

force_color(enabled: bool = True) -> None

Override color autodetection (NO_COLOR still wins when disabling).

Used by CLIs whose output is consumed in ANSI-rendering surfaces — a terminal or a Databricks job / notebook panel — so color shows even off a TTY.

strip

strip(text: str) -> str

text with ANSI color escapes removed.

set_title

set_title(text: str) -> None

Set the terminal title bar (OSC 2) — a static status line that updates in place instead of scrolling the transcript.

Used by the Loki REPL to keep live token/cost KPIs pinned in the terminal "head" rather than reprinting a usage line after every turn. TTY-only (the escape is meaningless and would be junk in a redirected log); the title is stripped of any ANSI so it shows clean in the window/tab chrome.

logo(suffix: str = '') -> str

Render the full combined CLI logo for suffix (YGGLOKI, YGGDBKS, YGGAWS, …).

Each CLI gets ONE full ygg+service wordmark — never the bare YGG art with a text subtitle tacked underneath. An unknown suffix falls back to the plain YGG mark (no subtitle).

track

track(awaitable: object, label: str = '', *, interval: float = 0.1) -> object

Drive an :class:~yggdrasil.dataclasses.awaitable.Awaitable to completion behind a live :class:ProgressBar — a real bar when it reports a fraction (awaitable.progress()), an animated sweep while it doesn't.

The bridge from any awaitable — a SQL statement, a Databricks job run, an agent batch — to the terminal styling::

style.track(dbc.sql.execute(query), "running query…")
style.track(batch, "fetching shards…")

Returns the (now-finished) awaitable; surfaces its failure like wait.

progress

progress(current: int, total: int, label: str = '') -> None

Render/refresh a download-style progress bar in place on the current line (TTY only — a redirected log would fill with \r junk otherwise).

Drives long downloads (an Ollama pull) so the bytes-so-far show live. Call :func:clear_line (or print a newline) when the work finishes.

pulse_text

pulse_text(text: str, duration: float = 1.5, cycles: int = 2) -> None

Fade text between dim and bright.

event

event(icon: str, text: str, code: str = '36') -> str

A timestamped, glyph-led log line: HH:MM:SS ● text (the glyph colored, the clock dimmed).

hr

hr(width: int = 46) -> str

A dim horizontal rule.

install_logging

install_logging(
    level: "int | str" = logging.INFO,
    *,
    logger: "logging.Logger | None" = None,
    show_name: bool = True,
    force: bool = False
) -> logging.Handler

Install the CLI-styled :class:LogFormatter on a stream handler.

Attaches a single StreamHandler (stderr, so it never tangles with the spinner / structured lines on stdout) carrying :class:LogFormatter to logger (the root logger by default) and sets the level on both. The preferred replacement for logging.basicConfig(...) in ygg CLIs.

Idempotent: a handler this function previously installed is reused (its level + formatter refreshed) rather than stacked, so repeated calls across entrypoints don't double every line. force=True first removes any other handlers on logger (e.g. a stray basicConfig default) so the styled stream is the only sink. Returns the handler.