- docs/reference/tui.md: new TUI reference covering launch, layout, key bindings, input modes, help panel (F1), persona bar, full slash command catalog (67 commands / 14 groups), persona YAML schema, and module architecture table - docs/reference/persona.md: new persona system reference covering storage layout, YAML schema, validation rules, argument presets, PersonaRegistry and PersonaState Python APIs, and TUI slash commands - CHANGELOG.md: add [3.7.0] section summarising TUI, persona system, session management, server mode, A2A integration, and key fixes - README.md: extend Highlights with TUI, persona, session, server mode, and A2A; add TUI quick-start, session management, and server mode usage examples
4.6 KiB
Persona System
The persona system is a TUI-specific feature that lets users define named
identities, each binding an actor, argument presets, and project/plan scope
references. Personas are stored as YAML files and managed through the TUI or
the PersonaRegistry Python API.
Overview
A persona answers the question: "Who am I talking to, and with what configuration?" Each persona captures:
- Actor — the LLM actor to use (e.g.
openai/gpt-4o) - Base arguments — default arguments merged into every request
- Argument presets — named override sets cycled with
Ctrl+Tin the TUI - Scope references — projects and plans always in context
- Display metadata — name, description, icon, color, greeting
Storage
Personas are stored as YAML files in:
~/.config/cleveragents/personas/<name>.yaml
Override the directory with CLEVERAGENTS_CONFIG_DIR.
The last-used persona name is persisted to:
~/.config/cleveragents/tui-state.yaml
Persona Schema
name: my-persona # Required. No path separators or control chars.
actor: openai/gpt-4o # Required. Must be namespaced (namespace/name).
description: "My persona" # Optional description.
icon: "" # Optional display icon character.
color: null # Optional terminal color string.
base_arguments: {} # Arguments merged into every request.
scoped_projects: # Projects always in scope for this persona.
- local/my-project
scoped_plans: [] # Plan IDs always in scope.
cycle_order: 1 # Position in Ctrl+T cycle (0 = excluded from cycle).
greeting: "Hello!" # Optional greeting shown on activation.
argument_presets:
- name: default # Required. Must have empty overrides.
display: default
overrides: {}
- name: concise
display: Concise
overrides:
max_tokens: 256
temperature: 0.3
Validation rules
| Field | Rule |
|---|---|
name |
No /, \, .., null bytes, or ASCII control characters |
actor |
Must contain / (namespaced) |
argument_presets |
Must contain exactly one default preset with empty overrides |
cycle_order |
Must be unique across all personas when greater than zero |
Argument Presets
Presets provide named argument override sets. The default preset is always
present and has empty overrides. Additional presets shallow-merge their
overrides on top of base_arguments when computing effective arguments.
persona.effective_arguments("concise")
# → {**base_arguments, **concise.overrides}
In the TUI, press Ctrl+T to cycle through presets in cycle_order sequence.
Python API
PersonaRegistry
from cleveragents.tui.persona.registry import PersonaRegistry
from pathlib import Path
registry = PersonaRegistry() # uses ~/.config/cleveragents
registry = PersonaRegistry(config_dir=Path("/custom/dir"))
# List all personas
personas = registry.list_personas()
# Get a single persona
persona = registry.get("my-persona") # returns None if not found
# Save (create or update)
registry.save(persona)
# Delete
registry.delete("my-persona") # returns True if deleted
# Export / import
registry.export_persona("my-persona", Path("export.yaml"))
imported = registry.import_persona(Path("export.yaml"))
# Ensure a default persona exists
registry.ensure_default(actor="openai/gpt-4o")
PersonaState
PersonaState tracks the active persona and current preset per TUI session:
from cleveragents.tui.persona.state import PersonaState
from cleveragents.tui.persona.registry import PersonaRegistry
state = PersonaState(registry=PersonaRegistry())
# Get active persona for a session
persona = state.active_persona("session-123")
# Switch persona
state.set_active_persona("session-123", "my-persona")
# Get / cycle preset
current = state.current_preset("session-123")
next_preset = state.cycle_preset("session-123")
# Compute effective arguments
args = state.effective_arguments("session-123")
TUI Slash Commands
| Command | Description |
|---|---|
/persona:list |
List all personas |
/persona:set |
Switch the active persona |
/persona:create |
Create a new persona |
/persona:edit |
Edit an existing persona |
/persona:delete |
Delete a persona |
/persona:export |
Export persona YAML to a file |
/persona:import |
Import persona YAML from a file |