Two wrap artifacts in the Edit result card, both in wrapEditRendererLine:
- A row that broke inside an intra-line diff highlight ended with inverse
video still active (only the foreground was reset), so the frame's
right-edge padding painted as a default-foreground block. Every wrapped
diff row now closes inverse alongside the foreground reset; the next row
re-opens its own state, so highlights spanning the break render the same.
- The gutter matcher required a marker at column 0 immediately followed by
digits, a shape only produced when marker and number exactly fill the
gutter. Left-padded gutters (" -42│", any line number narrower than the
widest in the diff) and dedup-blanked gutters (" +│" on the added row
of a single-line replacement) fell back to generic wrapping, so their
continuation rows escaped into the line-number column. │-separated gutters
now accept padded and blank line numbers; ASCII "|" gutters still require
the canonical marker+number shape emitted by the plain fallback, so body
lines that merely start with "|", " |", or "123|" keep wrapping
generically.
Regression tests cover continuation-gutter containment, net-inverse-off at
every row end (with a precondition proving a highlight actually crossed a
break), phantom-gutter rejection for pipe- and digit-leading body lines, and
the plain-fallback canonical-row path.
Validated path-like renderer inputs before calling path helpers so provider-supplied arrays or objects cannot crash TUI rendering before schema validation reports the bad tool call.
Added renderer regression coverage for read, write, and edit call/result components with array and object path arguments.
Fixes#4525
- Replaced commit-based stability checks with a unified `isTranscriptBlockFinalized` tracking mechanism.
- Removed deprecated provisional rendering configuration and flags across tool and renderer interfaces.
- Standardized native scrollback boundary logic to pin at the first unfinalized block using settled row verification.
- Updated and refactored test suites to validate block finalization and settled row boundaries instead of deprecated commit stability methods.
- Added `normalizeSingleStringField` to dynamically map misplaced string inputs to required schema fields for single-argument tools.
- Integrated argument normalization into `validateToolArguments` to handle model-specific variations in JSON payloads during validation passes.
- Updated `coding-agent` streaming and rendering components to recognize `_input` as a legacy alias for `input` across various UI paths and logic flows.
- Refactored `hashlineEditParamsSchema` to strictly enforce the `input` field while maintaining support for legacy aliases via runtime coercion rather than schema definition.
- Corrected unit tests to reflect that `_input` is rejected by the strict schema but handled gracefully by the validation layer.
When a multi-entry single-path edit prunes the first entry's snapshots
(large pre-image) and keeps a later entry's snapshots (file shrunk
between entries), the aggregator at `executeSinglePathEntries` recorded
the later entry's small `oldText`/`newText` as the whole-file
transition. ACP clients would then render a misleading partial diff
instead of degrading to text-only for the over-budget edit.
Add an explicit `snapshotsPruned` marker on `EditToolDetails` /
`EditToolPerFileResult`, set by `pruneSnapshot` whenever it strips a
payload. `executeSinglePathEntries` tracks the flag across child
results and suppresses aggregate `oldText`/`newText` (re-stamping the
marker on the aggregate) the moment any child was pruned;
`executeApplyPatchPerFile` propagates the flag onto each per-file entry.
Regression test exercises the exact scenario the reviewer raised on
#3787: replace mode where entry 1 collapses a >1 MB file to a single
line (pruned) and entry 2 trivially renames the now-tiny result; the
aggregate result now carries `snapshotsPruned: true` with both
snapshot fields omitted.
- Enhanced the edit renderer to support visual tracking of delete and move/rename operations.
- Updated diff computation to correctly handle file-level changes and suppress erroneous "No changes" warnings.
- Improved terminal output with a clearer activity indicator and accurate state representation during multi-file operations.
- Extended the rendering pipeline to display source-to-destination paths for file renames and added validation tests for edit workflows.
- Updated streaming diff renderer to use visual line wrapping instead of line count for preview budget.
- Introduced width-aware slicing to ensure displayed content stays within the provided UI bounds.
- Refactored cache salt parameters to incorporate width for consistent rendering across window resizes.
- Introduced a dynamic row budget for the diff preview to prevent overflow during streaming.
- Integrated `previewWindowRows()` into the cache key to ensure proper re-rendering upon viewport resizing.
- Simplified tail window logic to consistently apply the preview budget.
- Remove the static "pending" hourglass icon from edit and write tool headers to reduce visual noise.
- Update multi-file status lines to use the active spinner icon directly instead of replacing a static icon, ensuring consistent liveness cues.
- Switch all UI components and tests from sharp box corners (`boxSharp`) to rounded ones (`boxRound`).
- Update `Theme` to re-export sharp junction symbols (tees and cross) under `boxRound` to ensure consistent divider rendering in rounded boxes.
- Remove outdated architectural notes regarding forced tool-choice queues in documentation.
- Removed configurable tab width support and the `display.tabWidth` setting across all packages.
- Deleted obsolete utility functions `getIndentation`, `getIndentationNoescape`, and `setDefaultTabWidth`.
- Standardized tab expansion logic to use a fixed `DEFAULT_TAB_WIDTH` globally.
- Cleaned up related configuration schemas, test suites, and internal API signatures to remove path-dependency.
- Added `ToolRenderer.provisionalPendingPreview` in `renderers.ts` and consulted it from `ToolExecutionComponent.isTranscriptBlockCommitStable`, replacing the 15.11.6 blanket gate that marked every collapsed pending preview commit-unstable.
- Marked only the tail-window previews the result render re-anchors as provisional: the edit streamed-diff tail (`edit/renderer.ts`), bash/ssh command caps (`createShellRenderer`, `sshToolRenderer`), and eval cells with interleaved outputs (`evalToolRenderer`).
- Restored mid-stream scrollback commits for every other pending preview, so tool calls taller than the viewport (e.g. a task call's context/assignment markdown) no longer read as cut off until the result lands.
- Added a regression test in `tool-live-region-scrollback.test.ts` scroll-appending a tall collapsed streaming task call into native scrollback mid-stream.
- Added transcript and assistant block version tracking for finalized segments.
- Changed committed block reuse logic to require prior finalization and same version.
- Fixed rerendering of committed finalized blocks when version values changed.
The streaming-spinner relocation in 61a57c1d2 re-added the trailing
"(preview)" label to expanded approval previews whenever a spinner frame
was active, breaking the #1992 contract. Expanded previews now drop the
label but keep the animated glyph so the volatile tail stays live.
- Added status.done and tool.* symbols to theme mappings and presets.
- Replaced generic success glyphs with contextual +/-, tool icons, and warnings.
- Mapped tool/task/job completions to status.done or status.enabled with icon overrides.
- Triggered runtime provider refresh after extension registration and warned on failure.
- Trimmed first-changed-line suffixes from edit path display and passed the line as a hyperlink target.
- Added middle-eliding path truncation for edit and rename file labels.
- Simplified diff-stat output to show only added/removed counts in a compact bracketed form.
- Added a configurable `contentPaddingLeft` option to `renderOutputBlock` with normalized defaults.
- Updated edit renderer output blocks to disable inherited left padding and align inner-width wrapping.
- Added coverage for completed edit gutter rendering to confirm no leading space and correct frame width.
- Fixed edit/read/search/ast-edit/ast-grep outputs to resolve OSC8 links from session cwd.
- Fixed grouped-file output classification to honor headerBase and fileScope for parent path resolution.
- Fixed read and write renderers to use resolved source/resolved paths as hyperlink targets.
- Unified edit, ask, ast-edit, todo, write, and inspect outputs using framedBlock.
- Wrapped failure and warning cases in framed blocks with clearer status states.
- Standardized frame body rendering by trimming blank lines and clipping content width.
- Simplified tool execution layout by removing dynamic padding and background helpers.
- Updated edit result rendering to inline diff change statistics in the file header instead of using a separate metadata row.
- Removed the redundant standalone metadata line and removed the extra blank line before diff bodies for a tighter single-hunk display.
- Added a test asserting the header now contains +/-/hunk stats and that no extra stats row appears before the diff.
Passed options.expanded through the edit call preview renderer so approval previews can lift the streaming diff tail window and hide the preview label.
Added regression coverage for collapsed versus expanded edit preview rendering.
Fixes#1992
- Replaced `¶path#hash` prefix with `[path#hash]` delimiters across parser, tokenizer, and grammar.
- Updated prompts, docs, and recovery paths to the new bracketed form.
- Preserved hidden tmux overlays in live viewport while keeping native scrollback intact during forced renders.
- Adjusted forced-render handling so pure appends preserve full scrollback and return viewport repaint on equal-size diffs.
- Capped streaming edit diff previews to a fixed trailing window and reported hidden hunk counts.
- Updated tmux and regression tests to validate hidden overlays, nativeText frame sources, and scrollback retention.
- Removed stream preview height tracking and simplified tool call rendering to always invalidate the content box.
- Replaced snapshot internals with full-file records and removed contiguous/sparse snapshot APIs.
- Added file-hash normalization, computed `computeFileHash`, and updated grammar/messages to 4-hex tags.
- Simplified recovery by checking whole-file hashes first, then applying merge-replay fallback after mismatches.
- Updated coding-agent tools to use `record`/`recordFileSnapshot` and skip hash headers for unsnapshotted large files.
- Expanded patcher and snapshot tests to verify 4-hex anchors, hash deduplication, and cache-capped behavior.
- Replaced 4-hex content-derived file hashes with 3-hex opaque tags minted by InMemorySnapshotStore, making tags session-bound pointers rather than content fingerprints.
- Removed lru-cache dependency; replaced LRU-bounded per-path rings with a flat 4096-slot global ring using a scrambled permutation to prevent LLM tag extrapolation.
- Made SnapshotStore required in Patcher (was optional); tag resolution now drives stale-anchor detection instead of recomputing hashes at apply time.
- Changed literal payload sigil from `|` to `+` and accepted `^A` shorthand for `^A-A`; added lenient recovery for bare bodies, lone `-` rows, and overlapping bare/concrete block pairs.
- Added `parsePatchStreaming` and `Executor.endStreaming` to handle in-flight ops gracefully during incremental parsing, dropping pending ops with no payload to avoid phantom edits.
- Introduced `PatchSection.applyPartialTo` as a streaming-aware counterpart to `applyTo`, using the new streaming parser for diff preview generation.
- Removed the custom `buildHashlineNaturalOrderPreviews` function and replaced it with calls to `applyPartialTo`, centralizing streaming logic in the parser layer.
- Added a dedicated @oh-my-pi/hashline package with parser, patcher, filesystem, snapshots, and release metadata.
- Migrated coding-agent hashline and stream entrypoints to @oh-my-pi/hashline and removed old hashline module exports.
- Changed multi-section hashline execution to validate section hashes and flush diagnostics only at the final commit.
- Added session fileSnapshotStore support and rewired edit/read/search/write tools to use it instead of fileReadCache.
- Replaced per-line hash anchors with file-level hash validation in hashline format, changing anchor syntax from LINE+HASH to bare LINE numbers.
- Simplified hashline line separator from pipe (|) to colon (:) and replaced replace operator (->) with colon, added delete operator (!) for explicit line deletion.
- Implemented file-read snapshot caching with multi-snapshot ring buffer per path and file-hash-based recovery to detect and recover from stale edits.
- Refactored hashline grammar, parser, and execution to support file-level hash binding, anchor-scoped validation, and structural bracket warnings for delete operations.
- Updated documentation and test fixtures to reflect new hashline syntax with file hashes, colon separators, and delete operator throughout.
- Removed vim edit mode and automatically map existing vim configurations to hashline mode.
- Deleted VimTool class, VimEngine implementation, and all vim-specific editing logic (2409 lines).
- Removed vim mode from EditMode union type, edit tool strategies, and configuration schemas.
- Deleted vim parser, command handler, buffer manager, and renderer modules.
- Updated documentation and tests to remove vim mode references and add deprecation mapping.
- Updated hashline markers to `¶` headers and `^/v/->` operators across constants, prompts, docs, and tests.
- Reworked hashline grammar and parser to support `ANCHOR<SIGIL>[INLINE_PAYLOAD]` with optional inline bodies and `A-B` ranges.
- Changed range and marker syntax from `..`/`"` to `-` and suffix `^/v/->` forms like `7v` and `A->`.
- Aligned `sameLineRange()` output and BOF/EOF handling so `|TEXT` remains cosmetic and payload now follows the op line.
- Centralized op-line detection by replacing local regex helpers with `isHashlineOpLineText` for payload terminator and bad-op checks.
- Changed hashline format to canonical `§` section headers and `"`/`"`/`≔` operations across grammar, parser, and docs.
- Reworked range and op parsing so single anchors are valid, legacy `-`/`-=` ops error, and empty `≔` payloads now delete ranges.
- Removed legacy `HL_EDIT_SEP`, `$HSEP$`, and `hsep` plumbing, adopting raw payload lines.
- Updated execution, input, renderer, and streaming flows to use `HL_FILE_PREFIX` and `HL_OP_CHARS` helpers.
- Updated session-stats parsing to `§`/`≔`/`"`/`"` format, patch envelopes, and bumped parser versions.
- Removed the hashline-separator benchmark script and its PI_HL_SEP job orchestration.
- Updated parseHashlineInputPreviewHeader to strip all leading "@" markers before resolving the preview path, matching existing parser behavior.
- Added a regression test in edit renderer tests confirming both canonical "@@" and longer "@" runs render as clean file paths without extra "@" characters in titles.
- EditTool now carries oldText/newText in per-file results for patch, replace, and multi-file aggregation paths
- Renderer updated to display diff content for all edit modes
- Enables downstream consumers (ACP event mapper, TUI) to render accurate diffs
- Added edit streaming fallback data to the edit render context and used it when no finalized patch text was available.
- Implemented hashline fallback rendering that strips envelope syntax, sanitizes text, and truncates output to a fixed number of lines with a streaming indicator.
- Updated tool execution to populate the fallback preview from the active edit strategy and added a renderer test for hashline envelope input while the diff is not yet computable.
- Rendered diff content before the truncation notice instead of after.
- Updated hidden lines label to show count with "+" prefix.
- Added fallback label when no lines are hidden.
- Added inline hashline parse and apply support for `<` prepend and `+` append operations with prefix+suffix edits.
- Added fail-fast behavior to reject inline modify ops combined with delete or replace on same line.
- Renamed HASHLINE_* and mode symbols to HL_* in prompt tooling, read/search checks, and prompt templates.
- Standardized separators to `PI_HL_SEP`/`HL_EDIT_SEP` and fixed `HL_BODY_SEP='|'`, updating parser formatting behavior.
- Updated benchmark subtype constants and python cleanup test setup to use HL_* values and AgentRegistry mock failure injection.
- Added compact Lark grammar processing and applied it to OpenAI custom-format tools before conversion.
- Reworked atom mode into `---PATH` compact commands with new grammar, parser, and rm/mv file operations.
- Updated `hline`/`href`/`hrefr` helper behavior and hashline mismatch guidance using shared anchor state.
- Standardized path formatting with `formatPathRelativeToCwd` across LSP, prompts, and edit/search/write tools.
- Added benchmark run-path handling, including `.gitignore` runs mapping, absolute reports, and safer snapshot output.
- Added tests for compact grammar payloads, atom parsing/execution, renderer streaming, and path-list outputs.
- Required top-level `path` in edit requests, removed per-entry `path` fields, and updated docs/tests to match.
- Updated patch/hashline/atom streaming preview generation to return a single request-level path diff preview.
- Changed edit execution to route patch/replace/atom/hashline through single-path handlers sharing `path`.
- Added `scripts/analyze-edit-formats` Go CLI with reports to audit edit-tool usage from session JSONL logs.
Drop three pieces of well-intentioned-but-fragile string mangling that
were quietly mutating model edit content:
- `maybeAutocorrectEscapedTabIndentation` in both hashline and atom edit
modes (plus the `PI_HASHLINE_AUTOCORRECT_ESCAPED_TABS` env toggle and
`isEscapedTabAutocorrectEnabled` helper). Literal `\\t` in edit content
is now preserved verbatim.
- `maybeWarnSuspiciousUnicodeEscapePlaceholder` and the
`runHashlinePreflightSanitizers` wrapper. The warning was misleading:
it flagged `\\uDDDD` as suspicious without distinguishing intentional
literal text from a likely-malformed escape, and it was the only
remaining caller.
- The hand-rolled JSON-unescape fallback in
`decodePartialJsonStringFragment` (renderer.ts). The fallback used
`String.fromCharCode` (mishandling lone surrogates) and only covered a
subset of JSON escapes, so the same fragment decoded one way mid-stream
and a different way after `JSON.parse` succeeded. Now we trim a
trailing partial `\\u`/odd-backslash escape and surface the raw
fragment if `JSON.parse` still fails.
- Added sanitizeText to edit renderer previews and diff rendering before line processing.
- Passed filePath into replaceTabs calls across edit previews, errors, and diff visualization paths.