std/cli/render
Output helpers for .harn CLI subcommand scripts dispatched via the
harn-cli wedge (harn#2293 epic, harn#2296). This module is
intentionally a thin layer — most port-facing rendering primitives
already exist in the stdlib:
| Need | Use |
|---|---|
| Color + tty detection | std/ansi: ansi_color, ansi_bold, ansi_strip, ansi_enabled. Honors NO_COLOR and HARN_COLOR. |
| tty test | std/io: is_tty(fd). |
| Tables | std/table: render_table, render_markdown_table, render_kv_table. Column auto-width + alignment + per-cell truncation. |
| Diff rendering | std/diff (Myers). |
| Read stdin / passwords | std/io: read_line, read_password. |
What std/cli/render adds on top:
envelope(spec) -> dict— JSON envelope wrapper with pinned top-level key ordering (schemaVersion,apiStability, optionalwarnings, thenpayload). Snapshot-test friendly.write_envelope(env)— serialize and write to stdout, pretty when stdout is a tty, compact otherwise.mode()/json_mode()— read the dispatch wedge'sHARN_OUTPUT_JSONenv to decide between human and JSON output without re-parsing--json.
Surface
import { envelope, write_envelope, json_mode, mode } from "std/cli/render"
import { ansi_bold, ansi_color } from "std/ansi"
import { render_table } from "std/table"
fn main(harness: Harness) {
let result = {ok: true, items: [{provider: "anthropic", model: "claude-opus-4-7"}]}
if json_mode() {
write_envelope(envelope({
schema_version: 1,
api_stability: "stable",
payload: result,
}))
return
}
// Human mode.
__io_println(ansi_bold("Result", {}))
__io_println(render_table(result.items, {
headers: ["Provider", "Model"],
}))
}
Envelope contract
The envelope helper enforces a stable top-level key order so JSON
snapshot tests stay robust as new fields are added at the bottom:
schemaVersion— int, set by the callerapiStability—"stable"|"experimental"|"internal"warnings— list of strings; omitted when emptypayload— the actual result body
Consumers can switch on schemaVersion + apiStability without
parsing prose. Future additions go between apiStability and
payload so they never displace existing keys.