diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c9387fae..74fde1e4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,13 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). warning overlay before proceeding. Patterns cover destructive filesystem operations, privilege escalation, network exfiltration, and more. (#1003) +- **TUI — Permission Question Widget**: A new inline `PermissionQuestionWidget` + renders permission requests directly in the conversation stream for single-file + operations. Users can allow/reject with single-key shortcuts (`a`/`A`/`r`/`R`), + navigate with arrow keys, confirm with `Enter`, or press `v` to open the full + `PermissionsScreen` with diff view. `PermissionDecisionEvent` is emitted on + resolution. (#997) + - **TUI — First-run experience with actor selection overlay**: On first launch (no personas configured), a centred `ActorSelectionOverlay` widget guides the user to select an actor from a curated list (`anthropic/claude-4-sonnet`, diff --git a/docs/api/tui.md b/docs/api/tui.md index 343fc1e07..7db1f0acc 100644 --- a/docs/api/tui.md +++ b/docs/api/tui.md @@ -203,6 +203,54 @@ from cleveragents.tui.widgets import ThoughtBlockWidget - Muted styling distinguishes thought blocks from regular messages - Backed by `ThoughtBlock` domain model with configurable `max_lines` (default: 10) +### `PermissionQuestionWidget` + +Inline permission question widget rendered directly in the conversation stream +for single-file permission requests. For multi-file operations the full +`PermissionsScreen` is pushed instead. + +```python +from cleveragents.tui.widgets import PermissionQuestionWidget +from cleveragents.domain.models.core.inline_permission_question import ( + InlinePermissionQuestion, + PermissionDecision, +) + +widget = PermissionQuestionWidget(question) +event = widget.handle_key("a") # returns PermissionDecisionEvent or None +``` + +| Method | Description | +|--------|-------------| +| `move_up()` | Move selection cursor up (wraps) | +| `move_down()` | Move selection cursor down (wraps) | +| `handle_key(key: str) → PermissionDecisionEvent \| None` | Process a key press; returns a decision event when resolved | + +**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 | + +**`PermissionDecisionEvent`** — emitted when the user makes a decision: + +```python +@dataclass +class PermissionDecisionEvent: + question: InlinePermissionQuestion + decision: PermissionDecision +``` + +**`render_permission_question(question, selected_index=0, *, show_diff=False) → str`** — pure rendering helper (testable without Textual). + +--- + ### `PermissionsScreen` Full-screen overlay for tool permission requests.