- Added `unfoldUntilLines`/`unfoldLimitLines` options to progressively reveal nested elidable spans breadth-first instead of collapsing everything behind the outermost elision.
- Added `minTotalLines` setting to skip summarization for short files, returning verbatim content instead.
- Added `:` selector support to `search` paths for constraining matches to specific line ranges.
- Extracted `parseLineRanges`/`parseLineRangeChunk`/`isLineInRanges` from `read.ts` into shared `path-utils.ts`.
- Removed inline_body from grammar; op sigils (↑, ↓, :) now accept no trailing content.
- Executor emits INLINE_PAYLOAD_ACCEPTED_WARNING when legacy inline form is encountered but still applies the edit leniently.
- Updated prompt docs and all tests to use bare op + `+`-prefixed continuation rows.
- Queued modified file contents during ast_edit and flushed writes only after all file edits succeeded.
- Extracted write-logic into ast_edit_blocking with in-memory pending writes to avoid partial disk updates.
- Added a regression test ensuring no file is written when overlapping apply_edits fails partway through a run.
- Added a Bun-based `scripts/ci-release-notes.ts` utility that extracts `## [version]` entries from `packages/*/CHANGELOG.md` and writes a combined `release-notes.md` for the release tag.
- Updated the GitHub release workflow to install Bun, run the notes generator, and pass the generated `release-notes.md` to `softprops/action-gh-release` via `body_path`.
- Updated the auth-gateway OpenAI responses caching test to use shared E2E helper utilities and the common gateway URL constant.
- Initialized and reset in-memory settings in the nested live rendering test fixture to isolate test state between runs.
brush_core::interp::setup_open_file_with_contents wrote the entire heredoc/here-string body into an anonymous pipe synchronously before handing the reader to the downstream command. Bodies that exceed the OS pipe buffer (~4 KiB on Windows, 16-64 KiB on macOS) deadlocked the writer forever, and the bash tool tripped its 305 s hard timeout without ever launching the consumer. The Linux fast path still uses F_SETPIPE_SZ to grow the pipe inline; every other platform (and Linux bodies that overflow pipe-max-size) now decouples the write onto a fire-and-forget thread that terminates on drain or BrokenPipe.
Adds a 256 KiB regression test that exercises the worst-case shape (: builtin, which never drains stdin), guarded by tokio::time::timeout(10s) so a regression fails CI fast instead of hanging.
- Updated `TempDirGuard` creation in grep tests to include PID and an atomic sequence, preventing temp path collisions.
- Removed the `branch` filter from GitHub action run queries so results are matched by `head_sha` only.
- Adjusted run-watch calls to the simplified `fetchRunsForCommit` interface without the branch argument.
The Hangul Compatibility Jamo width correction (U+3131..=U+318E → 1 cell)
was applied unconditionally, but macOS terminals (Ghostty, Terminal.app,
iTerm2) are the only ones that render jamo as 1 cell. WezTerm and most
Linux terminals follow UAX#11 and render them as 2 cells.
The unconditional correction caused a width mismatch between the TUI's
calculated column positions and the terminal's actual rendering, resulting
in corrupted layout and broken Korean input on non-macOS platforms.
- Rust: gate the correction with cfg!(target_os = "macos")
- TS: gate the correction with process.platform === "darwin"
- On non-macOS, use UnicodeWidthStr for multi-char graphemes (perf)
Constraint: macOS terminals render jamo as 1 cell despite UAX#11.
Rejected: removing the correction entirely — would break macOS.
Tested: native build succeeds, typecheck passes.
Not-tested: visual verification on macOS terminals.
Scope-risk: narrow
- Updated `terminate_new_descendants` to require the `Sync` marker on its hash-set hasher.
- This tightened async helper bounds without changing its termination-wave logic.
Stopped marking persistent bash sessions as permanently broken when the JavaScript abort or timeout race wins.
Stopped the Rust descendant kill-wave helper once no cancellation targets remain so later commands are not swept into old cancels.
Fixes#1347
Added Rust-side descendant termination on shell cancellation paths so aborts and timeouts escalate to SIGKILL even if brush cleanup stalls.
Covered SIGTERM-ignoring shell workloads in native tests.
Fixes#1347
Pressing Enter sends `\r` (0x0D), and so does Ctrl+M; the terminal
cannot disambiguate the two in legacy mode (no Kitty keyboard protocol,
no modifyOtherKeys). matches_key was claiming both "enter" and
"ctrl+m" for the bare byte, so binding a command to Ctrl+M also fired
on Enter. Same collision applied for Ctrl+H vs Backspace, Ctrl+I vs Tab,
Ctrl+J vs LF/Enter, Ctrl+[ vs Escape, and the symmetric ctrl+alt+letter
legacy form (ESC + ctrl-char) vs Alt+Enter/Alt+Tab/Alt+Backspace.
The legacy ctrl+<letter>/<symbol> and ctrl+alt+<letter> fast-paths now
skip bytes terminals also send for named keys (0x08 0x09 0x0a 0x0d 0x1b
0x7f). The named key wins; the colliding ctrl+<letter> still matches
when the terminal disambiguates via the Kitty keyboard protocol or
modifyOtherKeys. parseKey already preferred the named key — matchesKey
now agrees.
Fixes#1354
- UAX#11 classifies U+3131..=U+318E as Wide (2 cells), but Ghostty, Terminal.app, and iTerm2 render them as 1 cell.
- Added `char_width_corrected` in Rust to override jamo width, mirroring the existing TS-side correction in `visibleWidth`.
- Replaced `UnicodeWidthStr::width` with per-char corrected sum so the override applies inside combining clusters.
- Added native-layer regression tests to guard cross-layer parity between Rust and TS width calculations.