- Removed legacy lowercase Alt+arrow byte handling so alt-arrow sequences are matched through kitty-aware CSI/u paths instead of deprecated escape-letter forms.
- Added support for `alt+enter` from both `\x1b\r` and `\x1b\n` during key parsing and matching.
- Expanded tests to cover mixed Kitty/tmux modes and to restrict legacy uppercase Alt+arrow aliases to non-Kitty protocol mode only.
- Preserved Alt and Ctrl+Alt letter ESC-prefix matching when kitty_protocol_active is true to support mixed tmux and Kitty keyboard modes.
- Parsed two-byte ESC sequences before legacy sequence lookup so mixed-mode Meta pairs are treated as Alt letter keys instead of legacy aliases.
- Updated native and TUI key tests to verify Alt+letter and Alt+Shift+letter parsing and matching while enhanced mode is active.
Fixes#1511
- Changed `bridge_chunks` to drain all currently queued output chunks into one string before invoking the JS callback.
- Added a 64 KiB batch cap with a small initial buffer so each N-API dispatch stays bounded while reducing callback churn from chatty processes.
- 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.
- 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
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.