diff --git a/codex-rs/tui/docs.md b/codex-rs/tui/docs.md index bc90b7dc..e8cc8bcd 100644 --- a/codex-rs/tui/docs.md +++ b/codex-rs/tui/docs.md @@ -573,6 +573,15 @@ The ACP protocol sets CWD at session creation and it is immutable. However, when │ ChatWidget │ ─────────────────────────▶│ BottomPane │ └───────────────────┘ └────────────┘ ``` +*Transcript Activity Refresh:* + +In addition to CWD tracking, system info refreshes on transcript activity to catch branch changes that occur outside the agent's operations (e.g., user switching branches in another terminal): + +- **User message submission**: `submit_user_message()` sends `RefreshSystemInfoForDirectory(config.cwd)` when the user submits a new message +- **Task completion**: `on_task_complete()` sends `RefreshSystemInfoForDirectory(config.cwd)` when an agent turn finishes + +These triggers use the session CWD (not the effective CWD from tracking) since they detect changes made by the user rather than by agent operations. + *Git Worktree Detection:* `SystemInfo` includes an `is_worktree: bool` field that indicates whether the current directory is a git worktree (not the main repository). Detection works by comparing `git rev-parse --git-common-dir` with `--git-dir` - in a worktree, these paths differ. diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index e03b594f..2a21228d 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -619,6 +619,13 @@ impl ChatWidget { self.last_unified_wait = None; self.request_redraw(); + // Refresh system info (including git branch) on task completion. + // This catches any branch changes that occurred during the agent's turn. + self.app_event_tx + .send(AppEvent::RefreshSystemInfoForDirectory( + self.config.cwd.clone(), + )); + // If there is a queued user message, send exactly one now to begin the next turn. self.maybe_send_next_queued_input(); // Emit a notification when the turn completes (suppressed if focused). @@ -1996,6 +2003,14 @@ impl ChatWidget { // Track user message for session statistics self.session_stats.record_user_message(); + // Refresh system info (including git branch) on user message submission. + // This catches branch changes that happened between interactions + // (e.g., user switched branches in another terminal). + self.app_event_tx + .send(AppEvent::RefreshSystemInfoForDirectory( + self.config.cwd.clone(), + )); + // Check if there's a pending agent switch - if so, send the message through // the App to trigger the switch first if let Some(pending) = self.pending_agent.take() { diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 888dfeda..edc88f8e 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -3799,3 +3799,60 @@ mod strip_ansi_codes_tests { assert_eq!(strip_ansi_codes("\x1b[2Jtext\x1b[H"), "text"); } } + +/// Submitting a user message triggers a system info refresh to update the branch marker. +/// This ensures the branch marker in the footer is updated on every transcript activity, +/// catching branch changes that happened between interactions (e.g., user switched +/// branches in another terminal). +#[test] +fn user_message_submission_triggers_system_info_refresh() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(); + + // Drain any events from widget construction + drain_refresh_system_info_events(&mut rx); + + // Submit a user message + chat.bottom_pane + .set_composer_text("test message".to_string()); + chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); + + // Verify that RefreshSystemInfoForDirectory was sent + let refresh_dirs = drain_refresh_system_info_events(&mut rx); + assert!( + !refresh_dirs.is_empty(), + "expected RefreshSystemInfoForDirectory event after user message submission" + ); +} + +/// Task completion triggers a system info refresh to update the branch marker. +/// This ensures any branch changes that occurred during the agent's turn are reflected. +#[test] +fn task_complete_triggers_system_info_refresh() { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(); + + // Start a task + chat.handle_codex_event(Event { + id: "task-start".into(), + msg: EventMsg::TaskStarted(TaskStartedEvent { + model_context_window: None, + }), + }); + + // Drain any events from task start + drain_refresh_system_info_events(&mut rx); + + // Complete the task + chat.handle_codex_event(Event { + id: "task-complete".into(), + msg: EventMsg::TaskComplete(TaskCompleteEvent { + last_agent_message: Some("Done".to_string()), + }), + }); + + // Verify that RefreshSystemInfoForDirectory was sent + let refresh_dirs = drain_refresh_system_info_events(&mut rx); + assert!( + !refresh_dirs.is_empty(), + "expected RefreshSystemInfoForDirectory event after task completion" + ); +}