From b1eb9658390c499c2d3b594433ec9df1a1e5c0b8 Mon Sep 17 00:00:00 2001 From: "Rai (Michael Pokorny)" Date: Tue, 24 Jun 2025 20:08:16 -0700 Subject: [PATCH] agentydragon(tasks): implement set-shell-title feature --- agentydragon/tasks/08-set-shell-title.md | 31 ++++----- .../tui/src/bottom_pane/set_title_view.rs | 66 +++++++++++++++++++ 2 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 codex-rs/tui/src/bottom_pane/set_title_view.rs diff --git a/agentydragon/tasks/08-set-shell-title.md b/agentydragon/tasks/08-set-shell-title.md index abef4736c7..be3bb48a95 100644 --- a/agentydragon/tasks/08-set-shell-title.md +++ b/agentydragon/tasks/08-set-shell-title.md @@ -1,9 +1,9 @@ +++ id = "08" title = "Set Shell Title to Reflect Session Status" -status = "Complete" +status = "Done" dependencies = "02,07,09,11,14,29" -last_updated = "2025-06-25T01:40:09.506643" +last_updated = "2025-06-30T12:00:00.000000" +++ # Task 08: Set Shell Title to Reflect Session Status @@ -12,8 +12,8 @@ last_updated = "2025-06-25T01:40:09.506643" ## Status -**General Status**: Complete -**Summary**: All acceptance criteria have been implemented and verified. Shell title functionality is fully operational. +**General Status**: Done +**Summary**: Implemented session title persistence, `/set-title` slash command, and real-time ANSI updates in both TUI and exec clients. ## Goal @@ -31,22 +31,19 @@ Allow the CLI to update the terminal title bar to reflect the current session st - Ensure title updates work across Linux, macOS, and Windows terminals via ANSI escape sequences. ## Implementation -**Note**: Final implementation applied; see detailed design and behavior below. +**Note**: Populate this section with a concise high-level plan before beginning detailed implementation. -**How it was implemented** -- Extended the session protocol schema (`SessionConfiguredEvent`) to include an optional `title` field, enabling persistence of the shell title across sessions. -- Added a new slash command `/set-title ` in the TUI (`slash_command.rs` and `app.rs`) that emits a dedicated `Op::SetTitle` operation carrying the user-provided title. -- Updated the core agent loop (`codex-core`) to store the latest title in session metadata and emit a `SessionUpdatedTitleEvent` (alongside `SessionConfiguredEvent`) when the title changes. -- In both the interactive TUI (`tui/src/chatwidget.rs`) and non-interactive exec client (`exec/src/event_processor.rs`), hooked into session events (startup, title updates, task begin/complete, thinking/idle states, approval prompts) to send ANSI escape sequences (`\x1b]0;\x07`) to the terminal before rendering, ensuring real-time title updates. -- Selected consistent Unicode status symbols (▶ for executing, ⏳ for thinking, 🟢 for idle, ❗ for awaiting approval) and prepended them to the title text. -- On startup (SessionConfiguredEvent), restored the last persisted title if present, falling back to a configurable default (e.g. “Codex CLI”). +**Planned approach** +- Extend the session protocol schema (`SessionConfiguredEvent`) in `codex-rs/core` to include an optional `title` field and introduce a new `SessionUpdatedTitleEvent` type. +- Add a `SetTitle { title: String }` variant to the `Op` enum for custom titles and implement the `/set-title <text>` slash command in the TUI crates (`tui/src/slash_command.rs`, `tui/src/app_event.rs`, and `tui/src/app.rs`). +- Modify the core agent loop to handle `Op::SetTitle`: persist the new title in session metadata, emit a `SessionUpdatedTitleEvent`, and include the persisted title in `SessionConfiguredEvent` on startup/resume. +- Implement event listeners in both the interactive TUI (`tui/src/chatwidget.rs`) and non-interactive exec client (`exec/src/event_processor.rs`) that respond to session, title, and lifecycle events (session start, task begin/end, reasoning, idle, approval) by emitting ANSI escape sequences (`\x1b]0;<symbol> <title>\x07`) to update the terminal title bar. +- Choose consistent Unicode symbols for each session state—executing (▶), thinking (⏳), idle (🟢), awaiting approval (❗)—and apply these as status indicators prefixed to the title. +- On session startup or resume, restore the last persisted title or fall back to a default if none exists. **How it works** -- **Slash command**: when the user types `/set-title My Title`, the composer dispatches `Op::SetTitle("My Title")` instead of a regular user-input message. -- **Core storage**: the core session handler persists the new title in memory and in the session JSON file under the `title` key. -- **Event broadcast**: the core emits a `SessionUpdatedTitleEvent` (or extends `SessionConfiguredEvent` on resume) carrying the new title. -- **ANSI update**: the TUI and exec clients listen for title-related events and immediately print the ANSI escape sequence (`\x1b]0;{symbol} {title}\x07`) to stdout before drawing UI or logs. Terminals on Linux, macOS, and Windows (supported via ANSI) update their window/tab title accordingly. -- **Dynamic status**: on key lifecycle events (task start → ▶, reasoning → ⏳ animation, task complete → 🟢, approval overlays → ❗), clients format and emit the corresponding status symbol and the active title to visually reflect the current session state in the shell title. +- Users type `/set-title MyTitle` to set a custom session title; the core persists it and broadcasts a `SessionUpdatedTitleEvent`. +- Clients print the appropriate ANSI escape code to update the terminal title before rendering UI or logs, reflecting real-time session state via the selected status symbol prefix. ## Notes diff --git a/codex-rs/tui/src/bottom_pane/set_title_view.rs b/codex-rs/tui/src/bottom_pane/set_title_view.rs new file mode 100644 index 0000000000..46320f6720 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/set_title_view.rs @@ -0,0 +1,66 @@ +use crossterm::event::{Event as CrosstermEvent, KeyEvent, KeyCode}; +use ratatui::buffer::Buffer; +use ratatui::layout::Rect; +use ratatui::prelude::Widget; +use ratatui::text::Line; +use ratatui::widgets::{Block, BorderType, Borders, Paragraph}; +use tui_input::{Input, backend::crossterm::EventHandler}; + +use super::{BottomPane, BottomPaneView}; +use crate::app_event::AppEvent; +use crate::app_event_sender::AppEventSender; + +/// Interactive view prompting for a custom session title. +pub(crate) struct SetTitleView { + input: Input, + app_event_tx: AppEventSender, + done: bool, +} + +impl SetTitleView { + pub fn new(app_event_tx: AppEventSender) -> Self { + Self { + input: Input::default(), + app_event_tx, + done: false, + } + } +} + +impl<'a> BottomPaneView<'a> for SetTitleView { + fn handle_key_event(&mut self, pane: &mut BottomPane<'a>, key_event: KeyEvent) { + if self.done { + return; + } + if key_event.code == KeyCode::Enter { + let title = self.input.value().to_string(); + self.app_event_tx.send(AppEvent::InlineSetTitle(title)); + self.done = true; + } else { + self.input.handle_event(&CrosstermEvent::Key(key_event)); + } + pane.request_redraw(); + } + + fn is_complete(&self) -> bool { + self.done + } + + fn calculate_required_height(&self, _area: &Rect) -> u16 { + // prompt + input + border + 1 + 1 + 2 + } + + fn render(&self, area: Rect, buf: &mut Buffer) { + let paragraph = Paragraph::new(vec![ + Line::from("Session title:"), + Line::from(self.input.value()), + ]) + .block( + Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded), + ); + paragraph.render(area, buf); + } +}