- README: add v3.7.0 highlights (first-run UX, estimation lifecycle, enriched domain events, correction attempts, devcontainer handler, A2A ValueError mapping); add What's New section; add doc links - CHANGELOG: merge 'Unreleased (pre-3.7.0)' into v3.7.0 as a subsection; clear [Unreleased] block - docs/reference/architecture_overview.md (new): high-level system layers, core abstractions, key services, protocols (A2A/MCP/LSP), estimation lifecycle, TUI architecture, server mode, observability - docs/reference/estimation_lifecycle.md (new): EstimationResult model reference, configuration, PLAN_ESTIMATION_COMPLETE event, plan.cost_estimate_usd, writing a custom estimation actor - docs/reference/correction_attempts.md (new): CorrectionAttemptRecord schema, state machine, CorrectionAttemptRepository API, DDL, CorrectionDryRunReport migration guide (removed redundant fields) - docs/reference/tui.md: add first-run experience section (ActorSelectionOverlay, is_first_run, create_default_persona_for_actor), session export/import TUI section, persona export/import TUI section; update architecture module table with first_run.py and actor_selection_overlay.py entries - mkdocs.yml: add Architecture Overview to top-level nav ISSUES CLOSED: #1310 #1087 #1242 #1241 #891 #996 #1001
13 KiB
TUI — Text User Interface
The CleverAgents TUI is a Textual-based interactive terminal application
launched with agents tui. It provides a full-screen chat-style interface
with persona management, slash commands, @-reference resolution, and
context-sensitive help.
ADRs: ADR-044, ADR-045, ADR-046
Installation
Textual is an optional dependency:
pip install 'cleveragents[tui]'
Without Textual, agents tui raises a RuntimeError with installation
instructions. The --headless flag works without Textual and is useful for
CI startup checks.
Launching
# Full interactive TUI
agents tui
# Headless startup check — prints JSON diagnostics and exits
agents tui --headless
The --headless output includes:
{
"textual_available": true,
"persona_count": 2,
"default_persona": "default",
"help": "Commands: /persona, /session, /help"
}
Keybindings
| Key | Action |
|---|---|
F1 |
Toggle context-sensitive help panel |
Escape |
Close current overlay / return to prompt |
Enter |
Submit prompt |
Tab |
Cycle to next persona |
Ctrl+Tab |
Cycle to next argument preset |
Ctrl+Q |
Quit TUI immediately |
Input Modes
The prompt auto-detects the input mode from the first character:
| Prefix | Mode | Behaviour |
|---|---|---|
| (none) | Normal | Expands @-references and sends to the active actor |
/ |
Command | Opens slash command overlay; dispatches on Enter |
! or $ |
Shell | Executes the remainder as a shell command |
Mode detection is handled by InputModeRouter.detect_mode().
Normal mode — @ references
Type @ followed by a partial name to open the Reference Picker overlay.
Candidates are drawn from a filesystem-backed catalog (projects, plans,
resources, actors, tools, skills) with a 5-second TTL cache. Fuzzy matching
uses prefix → substring → path-component → difflib scoring with recency
boosting.
@my-proj # resolves to project:local/my-project
@main-repo # resolves to resource:local/main-repo
Resolved references are expanded inline before the prompt is submitted.
Command mode — / slash commands
Type / to open the Slash Command overlay. Start typing to filter the 67
available commands across 14 groups. Press Enter to dispatch.
See Slash Commands for the full catalog.
Shell mode — ! passthrough
! git status
! ls -la
The command is executed in a subprocess with a configurable timeout (default: 30 seconds). Output is displayed in the transcript.
Slash Commands
67 commands across 14 groups. All commands follow the group:action naming
convention.
| Group | Commands |
|---|---|
| Session | session:create, session:list, session:show, session:switch, session:close, session:delete, session:rename, session:export, session:import |
| Persona | persona:list, persona:set, persona:create, persona:edit, persona:delete, persona:export, persona:import |
| Scope | scope:add, scope:remove, scope:clear, scope:show |
| Plan | plan:use, plan:list, plan:status, plan:tree, plan:execute, plan:apply, plan:cancel, plan:diff, plan:correct, plan:resume, plan:revert, plan:rollback, plan:explain, plan:errors, plan:artifacts, plan:inspect |
| Project | project:list, project:create, project:show, project:delete, project:inspect, project:context:show |
| Actor | actor:list, actor:show, actor:set-default |
| Resource | resource:list, resource:show, resource:tree, resource:inspect |
| Config | config:list, config:get, config:set |
| Tool | tool:list, tool:show |
| Skill | skill:list, skill:show |
| Invariant | invariant:list, invariant:add, invariant:remove |
| Profile | profile:list, profile:show |
| Context | context:inspect, context:set |
| Utility | help, clear, exit |
Widgets
CleverAgentsTuiApp
The root Textual application. Composes:
Header— application title barPersonaBar— active persona name, icon, and preset indicatorPromptInput— multi-mode prompt with mode indicatorSlashCommandOverlay—/-triggered command pickerReferencePickerOverlay—@-triggered reference pickerHelpPanelOverlay—F1-toggled context-sensitive helpFooter— keybinding hints
PersonaBar
Displays the active persona's icon and name plus the current argument
preset name. Updates automatically when the persona or preset changes.
PromptInput
Single-line input widget. Displays a mode indicator (> normal, / command,
! shell) and emits InputSubmittedEvent on Enter.
SlashCommandOverlay
Fuzzy-filtered list of all 67 slash commands. Keyboard-navigable. Dismissed
by Escape.
ReferencePickerOverlay
Fuzzy-filtered list of catalog entries. Dismissed by Escape or on
reference selection.
HelpPanelOverlay
Context-sensitive help panel. Content varies by current mode:
- Main Screen — global keybindings + main-screen shortcuts
- Slash Commands — slash command navigation hints
- Reference Picker — reference picker navigation hints
- Shell Prompt — shell mode usage notes
Source Locations
src/cleveragents/tui/
├── app.py # CleverAgentsTuiApp, SessionView, _FallbackCleverAgentsTuiApp
├── commands.py # TuiCommandRouter, run_tui()
├── slash_catalog.py # SLASH_COMMAND_SPECS, SlashCommandSpec
├── cleveragents.tcss # Textual CSS stylesheet
├── input/
│ ├── modes.py # InputMode, InputModeRouter, ModeResult
│ ├── reference_parser.py # ReferenceMatch, ReferenceParseResult, parse_references()
│ └── shell_exec.py # ShellResult, run_shell_command()
├── persona/
│ ├── schema.py # Persona, PersonaPreset
│ ├── registry.py # PersonaRegistry
│ └── state.py # PersonaState
├── search/
│ └── fuzzy.py # FuzzyCandidate, score_match(), rank_candidates()
└── widgets/
├── help_panel_overlay.py # HelpPanelOverlay, resolve_help_context()
├── persona_bar.py # PersonaBar
├── prompt.py # PromptInput
├── reference_picker.py # ReferencePickerOverlay
└── slash_command_overlay.py # SlashCommandOverlay
Running Tests
Behave (BDD unit tests)
nox -s unit_tests -- features/tui_persona.feature
nox -s unit_tests -- features/tui_slash_commands.feature
nox -s unit_tests -- features/tui_input_modes.feature
Robot Framework (smoke / integration tests)
nox -s integration_tests -- robot/tui_smoke.robot
Related
Inline Permission Questions
For single-file permission requests, the TUI renders a
PermissionQuestionWidget inline in the conversation stream rather than
pushing the full PermissionsScreen.
Permission Required
The actor wants to write to:
src/api/main.py
❯ a Allow once
A Allow always (this session)
r Reject once
R Reject always (this session)
Press v to open full PermissionsScreen with diff view
Key bindings:
| Key | Action |
|---|---|
a |
Allow once |
A |
Allow always (this session) |
r |
Reject once |
R |
Reject always (this session) |
↑ / ↓ |
Navigate options |
Enter |
Confirm highlighted option |
v |
Open full PermissionsScreen with diff view |
A PermissionDecisionEvent is emitted when the user makes a decision.
See Permission Question Widget for the full API reference.
Permissions Screen
When an actor requests a write operation on a resource, the TUI raises the
PermissionsScreen overlay. The user reviews the proposed changes in a
split-pane layout (file list left, diff view right) and decides whether to
allow or reject the operation.
Key bindings inside PermissionsScreen:
| Key | Action |
|---|---|
a |
Allow this request once |
A |
Allow all requests from this tool for the session |
r |
Reject this request once |
R |
Reject all requests from this tool for the session |
d |
Cycle diff mode: unified → side-by-side → context |
See tui_permissions.md for the full API reference.
Actor Thought Blocks
Actors that support extended thinking (e.g. Claude extended thinking mode)
emit reasoning traces that are rendered as ThoughtBlockWidget instances
in the conversation stream. Thought blocks are displayed with muted styling
and can be expanded or collapsed with Space.
See tui_thought_block.md for the full API reference.
First-Run Experience
On the first launch (when no personas are configured), the TUI displays the Actor Selection Overlay — a guided setup screen that lets the user pick an actor to get started.
┌─────────────────────────────────────────────────────┐
│ Welcome to CleverAgents │
│ │
│ Select an actor to get started: │
│ │
│ > anthropic/claude-4-sonnet │
│ anthropic/claude-4-opus │
│ openai/gpt-4o │
│ openai/o3 │
│ google/gemini-2 │
│ │
│ Search: ___________ │
│ [Enter] Select [Esc] Cancel │
└─────────────────────────────────────────────────────┘
After the user selects an actor, a "default" persona is automatically
created and persisted to the persona registry. Subsequent launches
restore the last-used persona and skip the first-run overlay.
First-run detection: is_first_run(registry) returns True when
PersonaRegistry.list_personas() returns an empty list.
Module: cleveragents.tui.first_run
| Function | Description |
|---|---|
is_first_run(registry) |
Returns True when no personas are configured |
create_default_persona_for_actor(registry, actor) |
Creates and persists a "default" persona for the given actor |
Widget: cleveragents.tui.widgets.actor_selection_overlay
| Function | Description |
|---|---|
render_actor_selection(actors, selected_index, search_query) |
Renders the actor selection overlay content |
Default actors offered in the overlay:
anthropic/claude-4-sonnetanthropic/claude-4-opusopenai/gpt-4oopenai/o3google/gemini-2
Session Export / Import (TUI)
Sessions can be exported and imported directly from the TUI using slash commands or the Sessions Screen.
# Export the current session to JSON
/session:export
# Import a session from a JSON file
/session:import
Export produces a JSON file containing the full session transcript and metadata. Import restores a session from a previously exported file. Both operations are also available via the CLI:
agents session export --session-id <ID> --output session.json
agents session import --input session.json
Persona Export / Import (TUI)
Personas can be exported to YAML and imported from YAML files:
# Export a persona
/persona:export
# Import a persona
/persona:import
Export paths must be relative to the current working directory. Imported personas are validated against the persona schema before being saved to the registry.