Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c741d926d |
@@ -2,6 +2,74 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## v3.7.0 — TUI Implementation (2026-04-02)
|
||||
|
||||
This release delivers the full Text User Interface (TUI) milestone (M8) per
|
||||
ADR-044, ADR-045, and ADR-046. The TUI is built on Textual and provides an
|
||||
interactive terminal application with persona management, slash commands,
|
||||
context-sensitive help, and fuzzy reference resolution.
|
||||
|
||||
### Added
|
||||
|
||||
- **TUI application** (`agents tui`): Textual-based interactive terminal UI
|
||||
with `CleverAgentsTuiApp`, graceful fallback when Textual is not installed,
|
||||
and a `--headless` mode for startup diagnostics. (#695)
|
||||
- **Input mode routing**: Three distinct input modes — Normal (`@` references),
|
||||
Command (`/` slash commands), and Shell (`!` passthrough) — dispatched by
|
||||
`InputModeRouter` with mode auto-detection. (#695)
|
||||
- **Persona system**: YAML-backed persona registry (`PersonaRegistry`) with
|
||||
atomic file writes, file-lock safety, and per-session state tracking
|
||||
(`PersonaState`). Personas carry actor bindings, argument presets, scoped
|
||||
project/plan lists, and cycle ordering. (#695)
|
||||
- **Slash command catalog**: 67 slash commands across 14 groups (Session,
|
||||
Persona, Scope, Plan, Project, Actor, Resource, Config, Tool, Skill,
|
||||
Invariant, Profile, Context, Utility) defined in `SlashCommandSpec`
|
||||
descriptors. (#1002)
|
||||
- **Slash command overlay**: Interactive overlay widget (`SlashCommandOverlay`)
|
||||
with fuzzy filtering and keyboard navigation. (#695)
|
||||
- **Reference picker overlay**: `@`-triggered overlay (`ReferencePickerOverlay`)
|
||||
for resolving project/plan/resource/actor/tool/skill references with
|
||||
filesystem-backed catalog and 5-second TTL cache. (#695)
|
||||
- **Fuzzy search**: `score_match` and `rank_candidates` helpers with
|
||||
prefix/substring/path-component/difflib scoring and recency boosting. (#695)
|
||||
- **Context-sensitive help panel**: `F1`-toggled `HelpPanelOverlay` with
|
||||
mode-aware content (Main Screen, Slash Commands, Reference Picker, Shell
|
||||
Prompt) and global keybinding reference. (#1013)
|
||||
- **Persona bar widget**: `PersonaBar` status widget displaying active persona
|
||||
name, icon, and current argument preset. (#695)
|
||||
- **Prompt widget**: `PromptInput` with mode indicator and submit handling. (#695)
|
||||
- **TUI command router**: `TuiCommandRouter` dispatching `/persona` and
|
||||
`/session` slash commands with DI container integration. (#695)
|
||||
|
||||
### Changed
|
||||
|
||||
- `agents actor run` now takes positional `<NAME>` and `<PROMPT>` arguments,
|
||||
aligning with the specification. The previous `--prompt/-p` option is
|
||||
removed. (#901)
|
||||
- `SandboxManager.commit_all()` is now an all-or-nothing atomic operation per
|
||||
specification line 45938. Partial failures trigger LIFO rollback of already-
|
||||
committed sandboxes. (#925)
|
||||
- `resource_selection` decision type reclassified as phase-agnostic (valid in
|
||||
both Strategize and Execute phases), aligning with ADR-007 and ADR-033. (#931)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Execution environment resolution now honours project-level override
|
||||
(precedence level 2) per the 6-level precedence chain. (#1080)
|
||||
- `project context set` missing `--execution-env-priority` flag — setting is
|
||||
now persisted and displayed by `project context show`. (#1079)
|
||||
- `list_actions()` now queries the database when persistence is enabled so
|
||||
actions created by prior CLI invocations are visible. (#760)
|
||||
- `shell=True` subprocess usage in `cli_coverage_steps.py` replaced with
|
||||
`shlex.split()` and `shell=False` for command injection prevention. (#734)
|
||||
|
||||
### Security
|
||||
|
||||
- Template rendering hardened with sandboxed Jinja2 renderer denying attribute
|
||||
access, function calls, and filters. (#319)
|
||||
- `CleverAgentsError` base class introduced with structured error types and
|
||||
secret redaction in error details. (#320)
|
||||
|
||||
- Added a context-sensitive TUI help panel overlay toggled by `F1`, with
|
||||
help content that varies for main-screen, slash-command, reference, and
|
||||
shell prompt modes. Updated Behave and Robot coverage for help-panel
|
||||
|
||||
@@ -6,16 +6,32 @@ CleverAgents is a Python-first automation platform. It provides a unified `agent
|
||||
|
||||
- Unified CLI entry points: `cleveragents` and `agents`
|
||||
- Fast Typer/Click-based interface with parity for help/version behavior
|
||||
- **Interactive TUI** (`agents tui`) built on Textual with persona management, slash commands, and fuzzy reference resolution
|
||||
- Behavior-driven coverage via Behave and Robot Framework
|
||||
- Nox automation for linting, typing, testing, docs, builds, and benchmarks
|
||||
- MkDocs-powered documentation with CleverAgents branding
|
||||
|
||||
## Feature Overview
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Plan lifecycle** | Full strategize → execute → apply pipeline with LLM actors |
|
||||
| **Actor system** | YAML-defined actors compiled to LangGraph graphs |
|
||||
| **Skill registry** | Composable skill bundles with MCP, file, git, and inline tools |
|
||||
| **Resource system** | DAG-based resource registry with 30+ built-in types (git, fs, container, LSP) |
|
||||
| **Automation profiles** | Named profiles (manual, cautious, supervised, auto, ci, full-auto) controlling autonomy |
|
||||
| **Sandbox & checkpoint** | Copy-on-write, overlay-fs, git-worktree, and transaction sandbox strategies |
|
||||
| **ACMS** | Advanced Context Management System with UKO ontology and 10-stage assembly pipeline |
|
||||
| **TUI** | Textual terminal UI with personas, slash commands, and `@`-reference picker |
|
||||
| **A2A / Server** | Agent-to-Agent protocol facade; Kubernetes Helm chart for server deployment |
|
||||
| **Observability** | LangSmith tracing, structured logging via structlog, audit event bus |
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# clone the CleverAgents core repository
|
||||
git clone https://git.cleverthis.com/cleveragents/core.git
|
||||
cd core
|
||||
git clone https://git.cleverthis.com/cleveragents/cleveragents-core.git
|
||||
cd cleveragents-core
|
||||
|
||||
# install dependencies
|
||||
python -m venv .venv
|
||||
@@ -30,6 +46,55 @@ agents --help
|
||||
agents --version
|
||||
```
|
||||
|
||||
### First plan
|
||||
|
||||
```bash
|
||||
# initialise the local database
|
||||
agents init
|
||||
|
||||
# register a project
|
||||
agents project create local/my-project --description "My first project"
|
||||
|
||||
# add a git resource
|
||||
agents resource add git-checkout local/my-repo --url https://github.com/example/repo.git
|
||||
|
||||
# link resource to project
|
||||
agents project link-resource local/my-project local/my-repo
|
||||
|
||||
# create an action
|
||||
agents action create --config examples/actions/simple.yaml
|
||||
|
||||
# start a plan
|
||||
agents plan use local/my-action --project local/my-project
|
||||
|
||||
# execute the plan (requires an LLM API key)
|
||||
agents plan execute
|
||||
```
|
||||
|
||||
### Interactive TUI
|
||||
|
||||
```bash
|
||||
# install the optional Textual dependency
|
||||
pip install 'cleveragents[tui]'
|
||||
|
||||
# launch the TUI
|
||||
agents tui
|
||||
|
||||
# headless startup check (no Textual required)
|
||||
agents tui --headless
|
||||
```
|
||||
|
||||
Inside the TUI:
|
||||
|
||||
| Key / Prefix | Action |
|
||||
|---|---|
|
||||
| `F1` | Toggle context-sensitive help panel |
|
||||
| `/` | Open slash command overlay (67 commands) |
|
||||
| `@` | Open reference picker (projects, plans, resources, …) |
|
||||
| `!` | Shell passthrough mode |
|
||||
| `Tab` | Cycle to next persona |
|
||||
| `Ctrl+Q` | Quit |
|
||||
|
||||
## Developing
|
||||
|
||||
Pre-commit hooks run automatically on every `git commit` (formatting, linting, type
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
# 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/ADR-044-tui-architecture-and-framework.md),
|
||||
[ADR-045](../adr/ADR-045-tui-persona-system.md),
|
||||
[ADR-046](../adr/ADR-046-tui-reference-and-command-system.md)
|
||||
|
||||
## Installation
|
||||
|
||||
Textual is an optional dependency:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
# Full interactive TUI
|
||||
agents tui
|
||||
|
||||
# Headless startup check — prints JSON diagnostics and exits
|
||||
agents tui --headless
|
||||
```
|
||||
|
||||
The `--headless` output includes:
|
||||
|
||||
```json
|
||||
{
|
||||
"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](#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 bar
|
||||
- `PersonaBar` — active persona name, icon, and preset indicator
|
||||
- `PromptInput` — multi-mode prompt with mode indicator
|
||||
- `SlashCommandOverlay` — `/`-triggered command picker
|
||||
- `ReferencePickerOverlay` — `@`-triggered reference picker
|
||||
- `HelpPanelOverlay` — `F1`-toggled context-sensitive help
|
||||
- `Footer` — 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)
|
||||
|
||||
```bash
|
||||
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)
|
||||
|
||||
```bash
|
||||
nox -s integration_tests -- robot/tui_smoke.robot
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [Persona System](tui_persona.md) — detailed persona management reference
|
||||
- [ADR-044](../adr/ADR-044-tui-architecture-and-framework.md) — TUI architecture decisions
|
||||
- [ADR-045](../adr/ADR-045-tui-persona-system.md) — persona system design
|
||||
- [ADR-046](../adr/ADR-046-tui-reference-and-command-system.md) — reference and command system
|
||||
@@ -0,0 +1,119 @@
|
||||
# TUI Persona System
|
||||
|
||||
Personas are named configurations that bind a TUI session to a specific actor,
|
||||
argument preset, and project/plan scope. They are stored as YAML files under
|
||||
`~/.config/cleveragents/personas/` and managed by `PersonaRegistry`.
|
||||
|
||||
**ADR**: [ADR-045](../adr/ADR-045-tui-persona-system.md)
|
||||
|
||||
## Persona Schema
|
||||
|
||||
```yaml
|
||||
name: my-persona # required; no path separators or control chars
|
||||
description: "..." # optional human-readable description
|
||||
icon: "🤖" # optional display icon
|
||||
actor: openai/gpt-4o # required; must be namespaced (namespace/name)
|
||||
color: "#4CAF50" # optional hex color for UI theming
|
||||
base_arguments: # default CLI argument overrides
|
||||
format: json
|
||||
log_level: warning
|
||||
scoped_projects: # project names pre-loaded into session scope
|
||||
- local/my-project
|
||||
scoped_plans: # plan IDs pre-loaded into session scope
|
||||
- 01JXYZ...
|
||||
argument_presets: # named override bundles (Tab cycles through these)
|
||||
- name: default
|
||||
display: "default"
|
||||
overrides: {}
|
||||
- name: verbose
|
||||
display: "verbose"
|
||||
overrides:
|
||||
log_level: debug
|
||||
format: rich
|
||||
cycle_order: 0 # sort order for Tab cycling (lower = earlier)
|
||||
greeting: "Hello!" # optional greeting shown on persona activation
|
||||
```
|
||||
|
||||
### Field validation
|
||||
|
||||
| Field | Constraint |
|
||||
|-------|-----------|
|
||||
| `name` | Non-empty; no `/`, `\`, `..`, null bytes, or control characters |
|
||||
| `actor` | Must contain `/` (namespaced reference) |
|
||||
| `argument_presets` | Auto-populated with a `default` preset if empty |
|
||||
|
||||
## PersonaRegistry
|
||||
|
||||
`PersonaRegistry` manages YAML files in `~/.config/cleveragents/personas/`
|
||||
(overridable via `CLEVERAGENTS_CONFIG_DIR`).
|
||||
|
||||
### Key methods
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `list_personas()` | Return all valid personas sorted by `cycle_order` then `name` |
|
||||
| `get(name)` | Return a `Persona` by name, or `None` if not found |
|
||||
| `save(persona)` | Atomically write persona YAML (temp-file + rename) |
|
||||
| `delete(name)` | Remove persona YAML file |
|
||||
| `ensure_default()` | Create and return the built-in `default` persona if absent |
|
||||
| `get_last_persona()` | Return the last-active persona name from `tui-state.yaml` |
|
||||
| `set_last_persona(name)` | Persist the last-active persona name |
|
||||
|
||||
File writes use `fcntl.LOCK_EX` for concurrent-access safety and an atomic
|
||||
temp-file rename pattern to prevent partial writes.
|
||||
|
||||
## PersonaState
|
||||
|
||||
`PersonaState` tracks the active persona and argument preset per TUI session
|
||||
in memory. It is not persisted across TUI restarts (the last-active persona
|
||||
name is persisted separately via `PersonaRegistry.set_last_persona()`).
|
||||
|
||||
### Key methods
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `active_name(session_id)` | Return the active persona name for a session |
|
||||
| `active_persona(session_id)` | Return the full `Persona` object |
|
||||
| `set_active_persona(session_id, name)` | Switch active persona; raises `ValueError` if unknown |
|
||||
| `current_preset(session_id)` | Return the active preset name |
|
||||
| `cycle_preset(session_id)` | Advance to the next preset in the persona's list |
|
||||
|
||||
## Slash commands
|
||||
|
||||
Personas are managed via `/persona:*` slash commands in the TUI:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/persona:list` | Display all personas |
|
||||
| `/persona:set <name>` | Switch active persona |
|
||||
| `/persona:create` | Create a new persona |
|
||||
| `/persona:edit <name>` | Edit an existing persona |
|
||||
| `/persona:delete <name>` | Delete a persona |
|
||||
| `/persona:export <name>` | Export persona YAML to stdout |
|
||||
| `/persona:import` | Import persona YAML from stdin |
|
||||
|
||||
## Default persona
|
||||
|
||||
If no personas exist, `PersonaRegistry.ensure_default()` creates a built-in
|
||||
`default` persona bound to the globally configured default actor. This ensures
|
||||
the TUI always has at least one usable persona on first launch.
|
||||
|
||||
## Source Locations
|
||||
|
||||
```
|
||||
src/cleveragents/tui/persona/
|
||||
├── schema.py # Persona, PersonaPreset (Pydantic models)
|
||||
├── registry.py # PersonaRegistry (YAML file management)
|
||||
└── state.py # PersonaState (in-memory session tracking)
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
nox -s unit_tests -- features/tui_persona.feature
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [TUI Overview](tui.md) — full TUI reference
|
||||
- [ADR-045](../adr/ADR-045-tui-persona-system.md) — persona system design decisions
|
||||
Reference in New Issue
Block a user