magus v0.4.3 is out. See what's new
¶ View generated markdown
5 min read

term

Terminal interaction: capability probes, an interactive picker, and styled output. Renders to stderr; pick raises rather than hanging when there is no terminal.

Naming convention: import the module under its bare name (import "term"), reach members with a backslash, and call methods in camelCase: term\someMethod.

Note

The examples below are reference-only. term performs real IO (filesystem, process, network, or environment access) that the in-browser playground's sandbox cannot provide, so it is not registered there and its examples have no Run button. Pure-compute modules such as strings and json run their examples live in the page.

Methods

isInteractive

Report whether this run can prompt at all: both standard input and standard error are terminals. Branch on it before calling pick - in CI, behind a pipe, or under a server this is false, and pick would raise. It is the one call that makes an interactive step safe to add to a target that also runs unattended.

Signature: term\isInteractive() -> bool - source

Returns: bool

wantsColor

Report whether styled output should be emitted: standard error is a terminal and the environment does not ask for plain text (NO_COLOR, TERM=dumb). colorize already consults this, so a caller needs it only to make a wider rendering choice - a box-drawing table versus a plain one.

Signature: term\wantsColor() -> bool - source

Returns: bool

size

Return the terminal's {width, height} in character cells. Both are 0 when there is no terminal to measure - piped output, no controlling terminal - so check width rather than expecting a raise. Use it to wrap or truncate output to the reader's actual window instead of assuming 80 columns.

Signature: term\size() -> TermSize - source

Returns: map[string]any

Example:

import "std";
import "term";

// width is 0 when there is no terminal to measure, so check it rather than
// catching an error.
final size = term\size();
final width = if (size.width > 0) size.width else 80;
std\print("wrapping to {width} columns");

colorize

Wrap s in the given style and close it again. Returns s UNCHANGED when the output is not a terminal or the environment asked for plain text, so a magusfile never has to guard the call and escape codes cannot leak into a CI log. A style of none is also pass-through, which lets a conditionally-computed style be passed without branching.

Signature: term\colorize(s, style) -> string - source

Parameter Type Optional Description
s string
style string

Returns: string

pick

Prompt the reader to choose one of items and return its index. Type to filter (matching every whitespace-separated token), arrow keys or Ctrl-N/Ctrl-P to move, Enter to choose. RAISES when there is no terminal to prompt on - guard with is_interactive - and raises when the reader aborts with ESC, Ctrl-C or Ctrl-D, so a cancel ends the run rather than quietly returning a choice nobody made. Renders to stderr.

Signature: term\pick(items, [prompt], [initial_filter], [initial], [max_rows]) -> int - source

Parameter Type Optional Description
items []string
prompt string yes
initial_filter string yes
initial int yes
max_rows int yes

Returns: int

Example:

import "std";
import "term";

// pick RAISES when there is no terminal, so an interactive step that also has to
// run unattended guards on isInteractive and declares its own default. It raises
// again when the reader aborts with ESC or Ctrl-C, which is why the call is
// caught rather than merely guarded: a cancel is not a choice.
final projects = ["console", "docs", "libs/gopherbuzz"];

fun choose() > str {
    if (!term\isInteractive()) {
        return projects[0];
    }
    try {
        return projects[term\pick(projects, prompt: "project")];
    } catch (e) {
        return projects[0];
    }
}

std\print(term\colorize(choose(), style: term\TermStyle.brightGreen));

notify

Raise a notification into the band magus pins at the bottom of the terminal, where it shows for a few seconds and then disappears on its own. Unlike log.info it does not join the scrolling transcript: it is for something worth GLANCING at during a long run, not for the record. Returns immediately - the message expires on its own clock - and never raises: it is DROPPED when there is no terminal to show it on, or when the band has no room, so a piped or CI run is never given a repainted view it cannot use and no caller has to guard a notification. Log the same fact if it also needs recording. ttl_ms defaults to 5000; a negative ttl_ms pins the notification until newer ones push it out.

Signature: term\notify(message, [level], [ttl_ms]) - source

Parameter Type Optional Description
message string
level string yes
ttl_ms int yes

Example:

import "term";

// A notification is something to GLANCE at during a long run, not a record of
// it. It is dropped when there is no terminal to show it on, so anything that
// must survive the run goes through log as well.
term\notify("docs regenerated");

// Severity picks the color; the default ttl is 5 seconds.
term\notify("cache stampede on go-build", level: term\LogLevel.warn);

// A longer ttl for something worth reading twice.
term\notify("deploy skipped: no credentials", level: term\LogLevel.error, ttl_ms: 15000);

// A negative ttl keeps it until newer notifications push it out of the band.
term\notify("server unreachable", level: term\LogLevel.error, ttl_ms: -1);

clearScreen

Erase the screen and move the cursor home, the repaint a full-screen refresh loop issues before redrawing. Scrollback is preserved, so a reader who scrolls up after the loop ends still sees what came before. A no-op when there is no terminal, so a watch loop needs no guard.

Signature: term\clearScreen() - source

generatedreference/buzz/termmodulestdlibmagusfile
Last updated (95680f58)
Earlier changes on this page (5)

Full history ↗ · Blame source ↗

Glossary

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. See targets.

Target

A named operation (build, test, ...) you invoke with magus run <target>; it may compose a spell's tool-native operations and depend on other targets. See targets.

Op

A single tool-native command a target composes (long form: operation); the middle of the work hierarchy (Spell to Op to Target). See operations.

Module

A magus stdlib namespace a magusfile imports for host capabilities: filesystem, exec, vcs, and more. See the module reference.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Cache

The content-addressed store magus consults before running a target, so unchanged work is skipped. See cache.

Sandbox

The restricted filesystem and environment a target runs in, so builds stay reproducible and side-effect-free. See sandbox.

Server

The background process a person starts with magus server start. It serves MCP, the console, background jobs and the warm knowledge graph, and adopts nested magus calls into one pool. See server.

CI

An ordinary magusfile-defined target you compose yourself with magus\needs - magus does not hardcode its stages. Magus.RunCI treats it specially only in that it strips the rw charm, it is the anchor magus affected ci keys off, and a selected scope with no project declaring it is a load error rather than a silent no-op. See targets.

Window

The terminal a command runs in. It keys fire-once notices for a caller no host gave a session, and is never recorded as a session.

Run

One target executing under one magus invocation, such as magus run test web or magus affected ci. A run keeps its captured output behind an output reference. Every magus run is a run whether or not any job asked for it; see Job for how the two relate.

Conventions

Admonitions

Call-outs are rendered from GitHub-style alert blockquotes and carry a colored accent per type:

Note

Context worth knowing, but not a warning.

Warning

Something that can bite you if ignored.

The types are NOTE, TIP, IMPORTANT, WARNING, and CAUTION.