- CHANGELOG: add [Unreleased] entries for TUI first-run experience (#1391), session Markdown export (#1004), and UKO provenance/temporal versioning (#891) - README: add first-run experience and Markdown transcript export bullets - docs/api/tui.md: new TUI API reference covering first-run, persona system, input routing, slash commands, session export/import, and widgets - docs/api/index.md: add TUI entry to module index - docs/api/resource.md: document DatabaseResourceHandler and DevcontainerHandler full protocol implementations with method tables and examples - docs/architecture.md: expand UKO section with provenance/temporal versioning details; update TUI design decision summary - mkdocs.yml: add TUI page to API Reference nav ISSUES CLOSED: #1391 #1004 #891
5.8 KiB
cleveragents.tui — Interactive Terminal UI
The tui package provides the full-screen Textual-based terminal user interface.
It requires the optional cleveragents[tui] extra.
See ADR-044 (TUI framework), ADR-045 (persona system), and ADR-046 (reference and command system) for design rationale.
Launching the TUI
pip install "cleveragents[tui]"
agents tui
First-Run Experience
is_first_run(registry: PersonaRegistry) → bool
Returns True when no personas are configured — used by app.on_mount() to
decide whether to show the actor selection overlay.
create_default_persona_for_actor(registry: PersonaRegistry, actor: str) → None
Creates and persists a "default" persona bound to actor after the user
completes the first-run selection flow.
ActorSelectionOverlay
from cleveragents.tui.widgets import ActorSelectionOverlay
A centred Textual widget displayed on first launch.
| Method | Description |
|---|---|
show() |
Make the overlay visible |
hide() |
Dismiss the overlay |
move_up() |
Move selection cursor up (wraps) |
move_down() |
Move selection cursor down (wraps) |
set_search(query: str) |
Apply fuzzy filter to actor list |
confirm() → str |
Confirm selection; returns actor name and hides overlay |
render_actor_selection() → str |
Pure rendering function (testable without Textual) |
Default actor list (in display order):
| Actor | Notes |
|---|---|
anthropic/claude-4-sonnet |
Recommended |
anthropic/claude-4-opus |
|
openai/gpt-4o |
|
openai/o3 |
|
google/gemini-2 |
Key bindings inside the overlay:
| Key | Action |
|---|---|
j / ↓ |
Move down |
k / ↑ |
Move up |
/ |
Enter fuzzy search |
Enter |
Confirm selection |
Persona System
Personas are YAML files stored in ~/.config/cleveragents/personas/. Each
persona binds an actor, optional argument presets, and scope references to a
named identity.
PersonaRegistry
from cleveragents.tui.persona import PersonaRegistry
registry = PersonaRegistry()
registry.load() # load all YAML files from config dir
persona = registry.get("default") # retrieve by name
registry.save(persona) # persist changes
registry.ensure_default(actor="openai/gpt-4o") # create default if absent
Persona
@dataclass
class Persona:
name: str
actor: str
presets: list[ArgumentPreset]
scope_refs: list[str]
Input Mode Routing
The prompt auto-detects three input modes from the first character:
| First character | Mode | Handler |
|---|---|---|
| (none / letter) | Normal | Message + @reference expansion |
/ |
Command | Slash command overlay |
! |
Shell | Subprocess passthrough |
InputModeRouter
from cleveragents.tui.routing import InputModeRouter
router = InputModeRouter(container)
await router.dispatch(input_text, session_id)
Slash Commands
67 slash commands across 14 groups are exposed via SlashCommandOverlay.
from cleveragents.tui.commands import SLASH_COMMAND_SPECS
# SLASH_COMMAND_SPECS: dict[str, list[SlashCommandSpec]]
# Keys are group names; values are lists of command specs.
Groups: Session, Persona, Scope, Plan, Project, Actor, Resource, Config, Tool, Skill, Invariant, Profile, Context, Utility.
Session commands (via TuiCommandRouter)
| Command | Description |
|---|---|
/session:export [--format json|md] [path] |
Export session to JSON or Markdown |
/session:import <path> |
Import a session from a JSON file |
# Export as Markdown transcript
/session:export --format md ~/my-session.md
# Export as canonical JSON (default)
/session:export ~/my-session.json
# Import a previously exported session
/session:import ~/my-session.json
Session Export / Import
Session.as_export_markdown() → str
Domain method on Session that renders a human-readable Markdown transcript.
The output is lossy (for sharing/documentation) and cannot be re-imported.
from cleveragents.domain.models.core.session import Session
session: Session = ...
md = session.as_export_markdown()
# Returns a Markdown string with:
# - Header block: session ID, actor, created_at, message count
# - Message history: role | timestamp | content
# - Linked plan references
CLI
# Export as canonical JSON (importable)
agents session export --session-id <ID> --output session.json
# Export as Markdown transcript (human-readable, not importable)
agents session export --session-id <ID> --output session.md --format md
# Import from JSON
agents session import --input session.json
Widgets
ThoughtBlockWidget
Renders actor reasoning traces inline in the conversation stream.
from cleveragents.tui.widgets import ThoughtBlockWidget
- Collapsed by default; press
Spaceto expand/collapse - Muted styling distinguishes thought blocks from regular messages
- Backed by
ThoughtBlockdomain model with configurablemax_lines(default: 10)
PermissionsScreen
Full-screen overlay for tool permission requests.
from cleveragents.tui.screens import PermissionsScreen
| Key | Action |
|---|---|
a |
Allow once |
A |
Allow always |
r |
Reject once |
R |
Reject always |
d |
Cycle diff display mode (unified → side-by-side → context) |
TUI State Persistence
The TUI persists minimal state to ~/.config/cleveragents/tui-state.yaml:
last_persona: "default"
This is loaded on startup to restore the last active persona.