Files
cleveragents-core/docs/reference/output_rendering.md
T
HAL9000 18d00c04c4
CI / lint (pull_request) Failing after 1m15s
CI / quality (pull_request) Successful in 1m21s
CI / typecheck (pull_request) Successful in 1m34s
CI / security (pull_request) Successful in 1m37s
CI / coverage (pull_request) Has been skipped
CI / unit_tests (pull_request) Failing after 1m37s
CI / docker (pull_request) Has been skipped
CI / build (pull_request) Successful in 33s
CI / helm (pull_request) Successful in 26s
CI / push-validation (pull_request) Successful in 19s
CI / e2e_tests (pull_request) Successful in 3m20s
CI / integration_tests (pull_request) Successful in 4m32s
CI / status-check (pull_request) Failing after 3s
fix(skills): implement multi-scope agent skill discovery for global, project, and local tiers
Implements AgentSkillDiscovery class to support discovering Agent Skills from
multiple configured directories across three scopes (global, project, local).
Handles name collisions with precedence: local > project > global.

Adds comprehensive BDD test coverage for multi-scope discovery scenarios including:
- Global-only, project-only, and local-only discovery
- Combined discovery from all scopes
- Name collision resolution with proper precedence
- Non-existent and empty scope directory handling
- Multiple skills in same scope discovery

ISSUES CLOSED: #9369
2026-05-06 19:55:22 +00:00

5.2 KiB

Output Rendering Framework

The output rendering framework decouples command output data from its visual presentation. Every CLI command produces structured output through a common abstraction layer, and the active format determines how that output is rendered to the terminal or piped to external consumers.

Architecture

All CLI output flows through a five-stage reactive pipeline:

  1. Command Logic opens an OutputSession and creates typed element handles.
  2. OutputSession coordinates handles, tracks their lifecycle, and emits events to the active materialisation strategy.
  3. ElementHandles are the producer-facing API — thread-safe, format-agnostic write interfaces.
  4. MaterializationStrategy decides when and how to render content.
  5. Terminal / Pipe receives the final byte stream.

Quick Start

from cleveragents.cli.output import OutputSession

with OutputSession(format="plain") as session:
    panel = session.panel("Project Details")
    panel.set_entry("Name", "local/api-service")
    panel.set_entry("Status", "active")
    panel.close()

    table = session.table(columns=["Resource", "Type"])
    table.add_row({"Resource": "api-repo", "Type": "git-checkout"})
    table.close()

OutputSession

The OutputSession is the central coordinator. It is used as a context manager and provides factory methods for each element type:

  • session.panel(title)PanelHandle
  • session.table(columns=[...])TableHandle
  • session.status(message)StatusHandle
  • session.progress(label)ProgressHandle

The session can be created with:

  • format — the desired output format (rich, color, table, plain, json, yaml)
  • command — the command name for metadata
  • strategy — an explicit materialisation strategy (overrides format auto-selection)

Element Handles

PanelHandle

Key-value pair output with a title.

panel = session.panel("Details")
panel.set_entry("Name", "my-project")
panel.set_entries({"A": "1", "B": "2"})
panel.remove_entry("A")
panel.close()

TableHandle

Tabular data with typed columns.

table = session.table(columns=["Name", "Status", "Count"])
table.add_row({"Name": "svc-1", "Status": "up", "Count": "42"})
table.add_rows([{"Name": "svc-2", "Status": "down", "Count": "0"}])
table.set_summary({"total": "42"})
table.set_sort_key("Name")
table.close()

StatusHandle

Single-line status messages with level indicators.

status = session.status("Processing...", level="info")
status.set_message("Done")
status.set_level("ok")
status.set_detail("All 42 items processed")
status.close()

ProgressHandle

Progress bars and spinners with throttled updates (max 10 FPS in rich mode).

prog = session.progress("Uploading", total=100)
prog.set_progress(50, 100)
prog.tick()
prog.increment(10)
prog.set_label("Finalising")
prog.set_indeterminate()
prog.set_step_status("validate", "done")
prog.close()

Materialisation Strategies

Format Strategy Behaviour
rich RichMaterializer In-place terminal updates via Rich library
color ColorMaterializer ANSI codes, scrolling output (no cursor moves)
table TableMaterializer ASCII box-drawing for tables
plain PlainMaterializer ASCII-only, no ANSI, no Unicode box characters
json JsonMaterializer Accumulate-then-serialize as JSON
yaml YamlMaterializer Accumulate-then-dump as YAML

Automatic Fallback

When terminal capabilities are insufficient, the framework automatically falls back:

  • richcolorplain (based on TTY, ANSI, cursor support)
  • colorplain (when no ANSI support)
  • tableplain (when not a TTY)
  • json / yaml — always available (no terminal needed)

Use --format <value> to explicitly set a format and skip fallback.

Error Envelope

JSON and YAML materializers support a unified error envelope:

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Resource not found",
    "details": {"id": "abc-123"}
  }
}

Producer Integration Guide

Writing Format-Agnostic Commands

Commands should create handles and write data without knowing which format is active:

def my_command(session: OutputSession, items: list[Item]) -> None:
    panel = session.panel("Summary")
    panel.set_entry("Total", str(len(items)))
    panel.close()

    table = session.table(columns=["Name", "Status"])
    for item in items:
        table.add_row({"Name": item.name, "Status": item.status})
    table.close()

Thread Safety

Multiple handles can be written concurrently from different threads. The session ensures elements are rendered in declaration order even when handles close out of order.

Backward Compatibility

The existing format_output() function in cleveragents.cli.formatting continues to work unchanged. A new format_output_session() function is also available that routes through the OutputSession framework.