From d7b29837507537085049009e8b389dacae238361 Mon Sep 17 00:00:00 2001 From: donglovejava <211940267+donglovejava@users.noreply.github.com> Date: Fri, 29 May 2026 06:33:10 +0800 Subject: [PATCH 1/2] fix(tools): eagerly load all exec_shell companion tools --- crates/tui/src/core/engine/tool_catalog.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/tui/src/core/engine/tool_catalog.rs b/crates/tui/src/core/engine/tool_catalog.rs index 65b194ce0..3a44619e7 100644 --- a/crates/tui/src/core/engine/tool_catalog.rs +++ b/crates/tui/src/core/engine/tool_catalog.rs @@ -34,7 +34,11 @@ pub(super) const DEFAULT_ACTIVE_NATIVE_TOOLS: &[&str] = &[ "apply_patch", "checklist_write", "edit_file", + "exec_interact", "exec_shell", + "exec_shell_interact", + "exec_shell_wait", + "exec_wait", "fetch_url", "file_search", "git_diff", @@ -46,6 +50,8 @@ pub(super) const DEFAULT_ACTIVE_NATIVE_TOOLS: &[&str] = &[ "task_create", "task_list", "task_read", + "task_shell_start", + "task_shell_wait", "update_plan", "web_search", "write_file", From 1624c072932ffa92e21b555ab212fc11ec320505 Mon Sep 17 00:00:00 2001 From: donglovejava <211940267+donglovejava@users.noreply.github.com> Date: Sat, 20 Jun 2026 21:07:24 +0800 Subject: [PATCH 2/2] fix(ui): reduce minimum terminal width for sidebar visibility The sidebar was only showing when terminal width >= 100 columns, which is too restrictive for many terminal setups. Reduced the minimum width to 60 columns to make the sidebar visible in more common terminal configurations. This fixes the issue where the sidebar would not appear in v0.8.62+ when using typical terminal sizes that are narrower than 100 columns. --- codewhale | 1 + crates/tui/src/tui/ui.rs | 2 +- fix-edit_file-fuzz.patch | 9 +++++++++ fix_engine.py | 39 +++++++++++++++++++++++++++++++++++++++ pr-body-agents.md | 33 +++++++++++++++++++++++++++++++++ pr-body-cjk.md | 27 +++++++++++++++++++++++++++ pr-body-fuzz.md | 9 +++++++++ pr-body-paste.md | 21 +++++++++++++++++++++ pr-body-rlm.md | 9 +++++++++ pr-body-voice.md | 32 ++++++++++++++++++++++++++++++++ pr-body.md | 30 ++++++++++++++++++++++++++++++ shell-live-progress.patch | 0 12 files changed, 211 insertions(+), 1 deletion(-) create mode 160000 codewhale create mode 100644 fix-edit_file-fuzz.patch create mode 100644 fix_engine.py create mode 100644 pr-body-agents.md create mode 100644 pr-body-cjk.md create mode 100644 pr-body-fuzz.md create mode 100644 pr-body-paste.md create mode 100644 pr-body-rlm.md create mode 100644 pr-body-voice.md create mode 100644 pr-body.md create mode 100644 shell-live-progress.patch diff --git a/codewhale b/codewhale new file mode 160000 index 000000000..5dffecef4 --- /dev/null +++ b/codewhale @@ -0,0 +1 @@ +Subproject commit 5dffecef40a876cae8d1bdf27750fb48183ae76a diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index fb89de619..14530bf30 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -151,7 +151,7 @@ const DISPATCH_WATCHDOG_TIMEOUT: Duration = Duration::from_secs(30); // the per-tool spinner pulse — keep this fast enough that the spout reads as // motion (~12 fps) instead of teleport-frames. const UI_STATUS_ANIMATION_MS: u64 = 80; -const SIDEBAR_VISIBLE_MIN_WIDTH: u16 = 100; +const SIDEBAR_VISIBLE_MIN_WIDTH: u16 = 60; const DEFAULT_TERMINAL_PROBE_TIMEOUT_MS: u64 = 500; const PERIODIC_FULL_REPAINT_EVERY_N: u64 = 50; const TURN_META_PREFIX: &str = ""; diff --git a/fix-edit_file-fuzz.patch b/fix-edit_file-fuzz.patch new file mode 100644 index 000000000..7d69aae87 --- /dev/null +++ b/fix-edit_file-fuzz.patch @@ -0,0 +1,9 @@ +The optional `fuzz` parameter was required to attempt the leading-indentation fuzzy fallback when exact search found zero matches. This forced the model to make two calls on every edit that needed fuzzy matching (first without fuzz -> error -> second with fuzz: true), causing a round-trip delay. + +Fix: remove the `fuzz` gate from the count == 0 branch. The tool now automatically retries with indentation-tolerant fuzzy matching when exact search produces no results. The `fuzz` parameter is kept in the schema for backward compatibility but marked deprecated. + +Changes: +- crates/tui/src/tools/file.rs: `if count == 0 && fuzz` -> `if count == 0` (always retry fuzzy fallback) +- crates/tui/src/tools/file.rs: removed dead `else if count == 0 { error }` branch +- crates/tui/src/tools/file.rs: updated description to note automatic fuzzy fallback +- crates/tui/src/tools/file.rs: marked fuzz parameter as deprecated in schema diff --git a/fix_engine.py b/fix_engine.py new file mode 100644 index 000000000..556066307 --- /dev/null +++ b/fix_engine.py @@ -0,0 +1,39 @@ +import re + +file_path = r'C:\project\F_project1\CodeWhale\crates\tui\src\core\engine.rs' +with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + +# 1. Add helper function after runtime_prompt_text +marker = ' "\n )\n}\n\n/// Spawn the engine' +helper_fn = ''' " + ) +} + +/// Check if a user message contains real user input (not just runtime metadata). +/// Returns true if the message has actual user text content beyond internal tags. +fn has_real_user_content(text: &str) -> bool { + // Strip known internal tags and check if meaningful content remains + let stripped = text + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", ""); + + // Check if there's non-whitespace content after stripping tags + let trimmed = stripped.trim(); + !trimmed.is_empty() && trimmed.len() > 10 // Allow for minimal metadata +} + +/// Spawn the engine''' + +if marker in content: + content = content.replace(marker, helper_fn) + print("OK: helper function added") +else: + print("WARN: marker not found for helper function") + +with open(file_path, 'w', encoding='utf-8') as f: + f.write(content) diff --git a/pr-body-agents.md b/pr-body-agents.md new file mode 100644 index 000000000..065bf3ca3 --- /dev/null +++ b/pr-body-agents.md @@ -0,0 +1,33 @@ +## Summary + +Auto-collapse completed sub-agents in the Agents sidebar panel. Non-running agents now show only a single line (label), freeing vertical space for active agents. + +## Problem + +Completed/failed/interrupted/cancelled sub-agents each occupied **2 lines** in the sidebar (label + detail line), wasting space that could be used for running agents or other content. With many agents, the sidebar became unnecessarily crowded. + +## Solution + +In `subagent_panel_lines()`, check the agent status before rendering the detail line. If the agent is not running (i.e. completed, failed, interrupted, cancelled), skip the detail line entirely and only render the single-line label. + +**Before:** +``` +✓ explore foo ← 2 lines per agent + abc123 · 3 steps · 12.3s +✗ build failed ← 2 lines per agent + def456 · 7 steps · 45.6s +● analysis running ← 2 lines per agent + ghi789 · 2 steps · 5.1s · parsing output... +``` + +**After:** +``` +✓ explore foo ← 1 line (collapsed) +✗ build failed ← 1 line (collapsed) +● analysis running ← 2 lines (expanded: label + detail) + ghi789 · 2 steps · 5.1s · parsing output... +``` + +## File changed + +`crates/tui/src/tui/sidebar.rs` — 5 lines added: `is_completed` check + early `continue` before the detail line. diff --git a/pr-body-cjk.md b/pr-body-cjk.md new file mode 100644 index 000000000..4204ad855 --- /dev/null +++ b/pr-body-cjk.md @@ -0,0 +1,27 @@ +## Bug + +When `assistant_text` contains CJK characters (3-byte UTF-8), the byte slice +`&assistant_text[..SUMMARY_LIMIT.saturating_sub(3)]` (byte 277) can land in +the middle of a multi-byte sequence, causing a panic: + +``` +byte index 277 is not a char boundary (it is inside a 3-byte UTF-8 sequence) +``` + +## Fix + +Replace the raw byte-index slice with `truncate_with_ellipsis()` which already +finds the nearest safe char boundary via `char_indices()`. + +## Change + +```diff +-format!("{}...", &assistant_text[..SUMMARY_LIMIT.saturating_sub(3)]) ++crate::utils::truncate_with_ellipsis(&assistant_text, SUMMARY_LIMIT, "...") +``` + +1 line changed. + +## Files + +- crates/tui/src/runtime_threads.rs:1437 diff --git a/pr-body-fuzz.md b/pr-body-fuzz.md new file mode 100644 index 000000000..7d69aae87 --- /dev/null +++ b/pr-body-fuzz.md @@ -0,0 +1,9 @@ +The optional `fuzz` parameter was required to attempt the leading-indentation fuzzy fallback when exact search found zero matches. This forced the model to make two calls on every edit that needed fuzzy matching (first without fuzz -> error -> second with fuzz: true), causing a round-trip delay. + +Fix: remove the `fuzz` gate from the count == 0 branch. The tool now automatically retries with indentation-tolerant fuzzy matching when exact search produces no results. The `fuzz` parameter is kept in the schema for backward compatibility but marked deprecated. + +Changes: +- crates/tui/src/tools/file.rs: `if count == 0 && fuzz` -> `if count == 0` (always retry fuzzy fallback) +- crates/tui/src/tools/file.rs: removed dead `else if count == 0 { error }` branch +- crates/tui/src/tools/file.rs: updated description to note automatic fuzzy fallback +- crates/tui/src/tools/file.rs: marked fuzz parameter as deprecated in schema diff --git a/pr-body-paste.md b/pr-body-paste.md new file mode 100644 index 000000000..51c84c445 --- /dev/null +++ b/pr-body-paste.md @@ -0,0 +1,21 @@ +## Problem + +Pasting large text into the CodeWhale TUI composer (e.g. pasting a 20KB log file or code block) immediately converts it to an `@.deepseek/pastes/paste-xxx.md` mention. The user's text vanishes from the composer and is replaced by a cryptic `@file` reference they did not ask for. This is confusing — the user expected to see their pasted text and be able to review/edit it before sending. + +## Root Cause + +`insert_paste_text()` calls `consolidate_large_input_if_oversized()` at paste time, which checks if `char_count(input) > MAX_SUBMITTED_INPUT_CHARS` (16,000 chars). When exceeded, it immediately writes the text to a paste file and replaces the composer input with `@.deepseek/pastes/paste-xxx.md`. + +The consolidation is useful at submit time (safety net), but at paste time it is surprising and confusing. + +## Fix + +1. **Remove the immediate consolidation from `insert_paste_text()`** — the text now stays in the composer as-is after pasting. +2. **Keep the consolidation as a safety net at submit time** — the same logic already runs when the user presses Enter, so the model still gets the `@file` reference if needed. +3. **Improve the toast message** — explain what happened when the submit-time consolidation fires. + +The user now sees their full pasted text in the composer and can review/edit before sending. The consolidation only triggers when they press Enter if the text is still over the 16K char limit. + +## Files + +- `crates/tui/src/tui/app.rs`: commented out `self.consolidate_large_input_if_oversized()` in `insert_paste_text`, updated toast message diff --git a/pr-body-rlm.md b/pr-body-rlm.md new file mode 100644 index 000000000..7dd7c6c47 --- /dev/null +++ b/pr-body-rlm.md @@ -0,0 +1,9 @@ +RLM sessions that produce large stdout/stderr (e.g. reading a local log file, dumping a large JSON table, or printing diagnostic output) currently inline the full preview into the parent tool result. On long-running RLM sessions this bloat accumulates and pressures the parent context window. + +Fix: when `rlm_eval` stdout or stderr exceeds 1000 characters, the full body is stored as a `var_handle` in the handle store. The tool result returns a short inline note (`"N chars; retrieve via handle_read"`) plus `stdout_handle` / `stderr_handle` fields containing the handle reference. The model calls `handle_read` for bounded projections. + +Changes: +- `rlm.rs`: Added `STDOUT_HANDLE_THRESHOLD_CHARS` constant +- `rlm.rs`: Added `route_output()` helper that stores large text as a var_handle +- `rlm.rs`: Modified `rlm_eval` execute to route stdout/stderr >= 1k chars into handles +- `rlm.rs`: Updated description to document the new handle-routing behavior diff --git a/pr-body-voice.md b/pr-body-voice.md new file mode 100644 index 000000000..414b4c544 --- /dev/null +++ b/pr-body-voice.md @@ -0,0 +1,32 @@ +## Summary + +Validate terminal-safe voice shortcut and STT helper setup for the voice input feature shipping in v0.8.45/v0.8.46. + +## Completed + +- **`voice_input.rs`**: Added `diagnose_voice_setup()` async function that checks: + - Missing/invalid `voice_input_command` config + - Inexecutable binary (spawns `--version` as probe) + - Permission/spawn failures with clear error messages +- **`voice_input.rs`**: Added `terminal_detection_tests` module with: + - `chord_likely_reaches_tui()` heuristic function documenting known-consumed chords + - Tests for common chords (Ctrl-K consumed, Ctrl-L/Ctrl-C safe) + - Candidate shortcut test matrix (F2, F3, Alt-Space, Ctrl-] etc.) +- **`docs/VOICE_INPUT_TERMINALS.md`**: Terminal compatibility matrix documenting Ctrl-K behavior across 13 terminal emulators + STT helper setup checklist + recommended safe chords +- **`docs/KEYBINDINGS.md`**: Added Voice input section documenting the Ctrl-K caveat with cross-reference to the new terminal matrix doc + +## Not completed (needs manual verification) + +- **Actual terminal testing** on macOS Terminal.app, iTerm2, Ghostty, Warp, Windows Terminal, Linux terminals +- **Final default chord selection** — the matrix lists candidates but the final binding needs human verification +- **Manual QA checklist** for terminals that consume modifier keys +- **Compilation verification** — changes made to a feature branch (work/v0.8.45-flash) without Rust CI available + +## Files changed + +``` +crates/tui/src/tui/voice_input.rs | 110 +++++++++++++++++ +docs/KEYBINDINGS.md | 22 ++++ +docs/VOICE_INPUT_TERMINALS.md | 87 +++++++++++++ +3 files changed, 219 insertions(+) +``` diff --git a/pr-body.md b/pr-body.md new file mode 100644 index 000000000..8eb17c997 --- /dev/null +++ b/pr-body.md @@ -0,0 +1,30 @@ +## Summary + +Add real-time incremental output display for shell execution commands. The TUI now displays shell output **while the command is still running**, instead of hiding all output until completion. + +## Changes + +- **`ExecCell`**: Added `live_output: Option` field to store incremental output during execution +- **`history.rs`**: Modified render logic to show `live_output` when `output` is not yet available (priority: final output > live output > hints) +- **`app.rs`**: Added `poll_shell_progress()` method that polls `ShellManager` during idle frames and updates matching `ExecCell` entries +- **`ui.rs`**: Wired `poll_shell_progress()` into the event loop after `tick_quit_armed()` +- **All ExecCell construction sites**: Added `live_output: None` to 8 files + +## How it works + +1. During idle frames, the TUI polls `ShellManager.list_jobs()` for running shell processes +2. Running exec cells are matched to shell jobs by command prefix +3. Live output (tail of stdout/stderr) is written to `ExecCell.live_output` +4. The renderer displays it in the transcript immediately + +## Files changed +``` +crates/tui/src/tui/active_cell.rs | 1 + +crates/tui/src/tui/app.rs | 69 +++++++++++++++++++++++++++++ +crates/tui/src/tui/history.rs | 11 ++++ +crates/tui/src/tui/sidebar.rs | 5 ++ +crates/tui/src/tui/tool_routing.rs | 2 + +crates/tui/src/tui/transcript.rs | 1 + +crates/tui/src/tui/ui.rs | 2 + +crates/tui/src/tui/ui/tests.rs | 3 + +``` diff --git a/shell-live-progress.patch b/shell-live-progress.patch new file mode 100644 index 000000000..e69de29bb