From 033132a7355e2825d5e42c66a09a82edc7d42f12 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 02:40:07 +0000 Subject: [PATCH] =?UTF-8?q?fix(tui):=20#3032=20residuals=20=E2=80=94=20run?= =?UTF-8?q?ning-exec=20hint=20now=20says=20Ctrl+B=20backgrounds=20the=20co?= =?UTF-8?q?mmand;=20Ctrl+B=20documented=20in=20KEYBINDINGS.md=20and=20runb?= =?UTF-8?q?ook=20updated=20for=20menu=20removal;=20Cannot-background=20mes?= =?UTF-8?q?sage=20names=20the=20reason=20(interactive=20/=20non-shell=20to?= =?UTF-8?q?ol=20/=20nothing=20running)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude https://claude.ai/code/session_018zaP8vUfTAsrE38L6h6fw5 --- crates/tui/src/tui/history.rs | 4 ++-- crates/tui/src/tui/ui.rs | 22 +++++++++++++++++++++- docs/KEYBINDINGS.md | 1 + docs/OPERATIONS_RUNBOOK.md | 2 +- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/crates/tui/src/tui/history.rs b/crates/tui/src/tui/history.rs index 9ca7fb54..252e4288 100644 --- a/crates/tui/src/tui/history.rs +++ b/crates/tui/src/tui/history.rs @@ -957,7 +957,7 @@ impl ExecCell { )); } else if self.status == ToolStatus::Running && self.source == ExecSource::Assistant { lines.extend(wrap_plain_line( - " Ctrl+B opens shell controls.", + " Ctrl+B backgrounds this command.", Style::default().fg(palette::TEXT_MUTED), width, )); @@ -5075,7 +5075,7 @@ mod tests { assert!(text.contains("running line 1")); assert!(text.contains("running line 2")); - assert!(!text.contains("Ctrl+B opens shell controls")); + assert!(!text.contains("Ctrl+B backgrounds this command")); } #[test] diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index be17a2d9..ba25afb9 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -8771,10 +8771,30 @@ fn render_toast_stack_overlay( } pub(crate) fn request_foreground_shell_background(app: &mut App) { - if !app.is_loading || !active_foreground_shell_running(app) { + if !app.is_loading { app.status_message = Some("No foreground shell command to background".to_string()); return; } + if !active_foreground_shell_running(app) { + // #3032 AC3: name the reason backgrounding is unavailable — + // interactive execs and non-shell blocking tools are visibly running + // but cannot be detached, and a generic shrug reads like a bug. + let reason = if terminal_pause_has_live_owner(app) { + "the running command is interactive" + } else if app + .active_cell + .as_ref() + .is_some_and(|active| !active.is_empty()) + { + "the running tool is not a foreground shell command" + } else { + "no foreground shell command is running" + }; + app.status_message = Some(format!( + "Cannot background: {reason}. Press Ctrl+C to cancel the turn, or wait for completion." + )); + return; + } let Some(shell_manager) = app.runtime_services.shell_manager.clone() else { app.status_message = Some("Shell manager is not attached".to_string()); diff --git a/docs/KEYBINDINGS.md b/docs/KEYBINDINGS.md index 6782e4ee..e3710bc7 100644 --- a/docs/KEYBINDINGS.md +++ b/docs/KEYBINDINGS.md @@ -11,6 +11,7 @@ Bindings are not (yet) user-configurable — tracked for a future release (#436, | `F1` or `Ctrl-/` | Toggle the help overlay | | `Ctrl-K` | Open the command palette (slash-command finder) | | `Ctrl-C` | Cancel current turn / dismiss modal / arm-then-confirm quit | +| `Ctrl-B` | Background the running foreground shell command (turn continues; the command becomes a `/jobs` background job) | | `Ctrl-D` | Quit (only when the composer is empty) | | `Tab` | Cycle TUI mode: Plan → Agent → YOLO → Plan | | `Shift-Tab` | Cycle reasoning effort: off → high → max → off | diff --git a/docs/OPERATIONS_RUNBOOK.md b/docs/OPERATIONS_RUNBOOK.md index d53a8a45..8e98ac8a 100644 --- a/docs/OPERATIONS_RUNBOOK.md +++ b/docs/OPERATIONS_RUNBOOK.md @@ -28,7 +28,7 @@ Checks: 3. Confirm no local sandbox/permission deadlock in tool output Actions: -1. If a foreground shell command is running, press `Ctrl+B` and choose whether to background it or cancel the current turn. +1. If a foreground shell command is running, press `Ctrl+B` to move it to the background (the turn keeps running and the command becomes a background job under `/jobs`); use `Ctrl+C` instead if you want to cancel the turn. 2. If the command was started in the background, ask the assistant to cancel it with `exec_shell_cancel` and the returned task id. 3. Use `Esc` or `Ctrl+C` to interrupt the current turn when you want to stop the request itself. 4. Retry prompt; if still failing, restart TUI.