All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- TUI: press
\to toggle how changed lines are laid out, between grouped (all removed, then all added) and interleaved (each removed line next to its replacement). The Diff pane footer shows the current mode.
- serve: long lines now wrap on screen, so reviewing a diff on a phone needs no horizontal scrolling. Two buttons in the reviewer let you switch back to unwrapped lines and pick one of three font sizes; both choices are remembered.
- agent tools: Remove the line-anchored read and edit commands and the corresponding pi edit mode.
- TUI: Esc no longer quits; press q to quit from anywhere.
- diff: changes inside
export const f = () => {}andexport function f()now show the comments written above the function, like every other function shape. Editing only those comments keeps the function as the hunk's context too.
- serve: new
deltoids servecommand runs a small web app for reviewing your edits from a phone. It shows every edit across all your projects; open a session and swipe right/left to move between edits, each auto-centered on the change, and new edits pop up live.
- edit: extra fields in an edit request are now ignored instead of failing the edit, so a stray field the model adds no longer rejects the whole change.
- TUI: the Files view no longer crashes with "file changed before we could read it" when files change while it re-diffs; it keeps the current view and refreshes on the next stable moment. On startup it shows a brief "Loading…" and settles into the live diff.
- TUI: Add support for custom commands.
- TUI: The Files sidebar status colors (stage columns, file/directory name tint, and
+N/-Ncounts) are now themeable via[theme]overrides inconfig.toml. - TUI: Files sidebar directory rows now show name-based icons (
bin,.github,node_modules, …), matching lazygit; unnamed directories keep the folder glyph. - First-class support for symlinks.
- TUI: Reworked the Files sidebar styling.
- TUI/pager: A rename now shows as a single
old → newentry instead of a separate delete and add. - TUI: A file type change (regular ↔ symlink) now shows as a single row instead of a separate delete and add.
- TUI: Untracked files now show
??in the Files sidebar stage field, matchinggit status.
- TUI: A failed Files build now shows the error in the diff pane instead of blanking to "No local changes.", so a real failure is no longer indistinguishable from a clean tree.
- TUI: A binary file change no longer blanks the Files sidebar. Binary files are listed like any other change (with a
(binary)badge and no line counts) and show a "binary file" placeholder in the diff pane. - TUI: A submodule change (commit bump, add, delete, or type change) no longer blanks the Files sidebar with a "missing index blob" error. Submodules are listed like any other change (with a
(submodule)badge and no line counts) and show aSubmodule <old> → <new>placeholder in the diff pane. - A repointed symlink no longer aborts the pager or TUI with a "missing index blob" error.
- Files managed by a git content filter (such as git-crypt) now render like any other file, with full highlighting and breadcrumbs, instead of aborting the pager or TUI with a "missing index blob" error.
- diff: Diffs now show the same added and removed lines as
git diff; an edit plus new functions or tests beside it renders as one hunk that names their shared parent scope in the breadcrumb. - diff: Syntax highlighting now carries across every line shown in a hunk, so multi-line constructs like block comments and strings stay correctly colored throughout.
- diff: New functions or tests added right next to an edit are no longer dropped from the diff. Each brand-new function, method, or labeled test block (Jest/Mocha
it(...), Got.Run(...), RSpecit "…" do) now renders in its own hunk, even when several are added at once. - diff: An edit on an outer block alongside edits in a nested inner block no longer duplicates the shared lines across two overlapping hunks; they now render as a single hunk.
- diff: A hunk that merges changes at different nesting depths now names the lowest common ancestor of the changed lines in its breadcrumb instead of the deepest inner scope, so a scope that does not contain every change is no longer shown.
- diff: Breadcrumbs no longer include block-only wrapper calls such as RSpec
expect { … }/subject { … }or JSbeforeEach(() => {}). Named test blocks and rakenamespace/taskstill show. - diff: A new function or test whose body matches an edited neighbour's no longer has its tail lines rendered twice; each new block renders exactly once.
- edit: The text-mode
edittool now takes a single replacement instead of an array. - hashedit: Ops no longer take a per-op reason; give one reason for the whole edit.
- traces TUI: Reworked the entry list, header, and diff view for a cleaner layout.
- pager: Syntax highlighting now matches the TUI Files tab.
- TUI: The focused pane's scrollbar now matches its border accent color instead of staying blue.
- diff: Syntax highlighting now works for any bundled syntect syntax (e.g. Dockerfile), independent of tree-sitter scope support.
- The
reviewandtracesUIs are unified into a singledeltoids tui(also opened by runningdeltoidsin a terminal), showing the working-tree diff and the trace browser. - TUI: Long diff lines wrap onto continuation rows instead of being cut off at the pane edge.
- TUI: Mouse-wheel scrolling moves in proportion to the gesture instead of jumping too far.
- TUI: The sidebar resizes with
</>or by dragging the divider, opens a bit wider by default, scales with the terminal, and never disappears on narrow terminals. - TUI: The file sidebar lists files and folders interleaved alphabetically (matching lazygit) instead of grouping folders first.
- TUI: Faster scrolling and startup on large diffs and working trees.
- TUI: A focused panel's tab-strip border is now a single continuous colour instead of showing stray blue segments between the tab labels.
- pager: Hunks with no enclosing function now get a line-number box, so consecutive hunks are visibly separated (e.g. in lazygit).
- review TUI: The sidebar is now resizable. Use
</>to narrow/widen it, or drag the divider between the panes with the mouse.
- traces TUI: Entries pane now shows the filename first with a dimmed reason, instead of reason only.
- diff: Reworked how hunks grow their surrounding context, fixing a range of edge cases where edits inside wrapped statements, calls, and literals showed too little (or too much) context.
- tui: fix freeze with blank screen at 100% on macOS.
- diff: Wrapping a function body in a new call expression (e.g.
withDORetry(() => { ... })) no longer duplicates the added lines in a ghost second hunk.
- pi plugin: Steer model to use
read(nothashread) for skill files in hash mode. - diff: Replacing a function body with code containing nested callbacks (arrow functions, call expressions) no longer drops the added lines from the hunk.
- tui: Mouse support in
deltoids tracesanddeltoids review
- diff: Deleting lines inside a class-level decorator (e.g.
@Module({...})) no longer produces an empty diff. - diff: Method renames no longer split across two hunks when a new wrapper method is added nearby.
- diff: Scope expansion for a function body edit now includes leading doc comments and attributes (
///,#[inline], etc.) in the hunk context.
- cli:
deltoids -v/deltoids --versionprint the version. - Add support for hashedit (line+hash anchored edits). Enable in the pi extension by setting
DELTOIDS_EDIT_MODE=hash. - pi: Add custom
renderCall/renderResultfor thehashreadtool so it shows a collapsible box (first 10 lines, expandable) matching Pi's built-in tool chrome.
- diff: Large functions no longer cliff from full context to 3-line context at 200 lines. Changes within 100 lines of each other merge into a single hunk, with up to 100 lines of context before and after. A 500-line function with changes spread every 80 lines now shows the entire function in one hunk instead of fragmenting into tiny disconnected pieces.
- tui: Pane titles in
deltoids reviewanddeltoids tracesnow use lazygit-style dash padding (e.g.─[1]─Entries─) so the title blends into the rounded border instead of leaving gaps. - pi: Clear inherited renderers (
renderCall,renderResult,renderShell) fromedit,write, andreadoverrides so Pi'sToolExecutionComponentfalls back to the built-in renderers by name. Previously the spread copied stale closures; now Pi resolves the renderer fresh from the built-in tool definition, showing the standard colored boxes with diff previews and syntax-highlighted content. - pi: Migrate extension imports from deprecated
@mariozechner/pi-coding-agent/@mariozechner/pi-tuito@earendil-works/pi-coding-agent/@earendil-works/pi-tui.
- claude-code: Add Claude Code plugin
- diff: Add Terraform scope support. Edits inside a
block(resource "aws_s3_bucket" "logs",variable "region",module "vpc", …) anchor the hunk on the block and produce a breadcrumb naming the block by its type plus its string labels. Multi-linetuple([ … ]) andobject({ k = v … }) literals act as data-tier scopes so the binding line and surrounding entries stay visible when an edit lands inside one of them — including the common case where the edit sits in a tuple/object inside a block too large to expand on its own. - diff: A comment or attribute above a function/class now anchors the hunk on that function/class instead of its parent.
- edit/write/traces: Rename the
summaryfield toreasonon edit/write requests, individualedits[], and stored trace entries (CLI flag is now--reason). Old traces still load via asummaryalias. - traces: Trace ids may now be any safe string up to 128 chars (previously ULID-only), so external integrations can key traces on their own session ids (e.g. Claude Code's
session_id).
- Add
deltoids reviewTUI for reviewing diffs.
- Collapse the toolkit into a single
deltoidsbinary with subcommandspager,review,edit,write,traces, shipped as one homebrew formula (brew install juanibiapina/taps/deltoids) and one shell installer. Plaindeltoids(no subcommand) still runs the pager when stdin is piped, so existinggit config core.pager 'deltoids | less -R'setups keep working. - diff: Expand hunk context for multi-line literals so the binding line stays visible, matching the existing JSON/TS-config/YAML behaviour.
- diff: Anchor hunks on callbacks where the first argument is a string (
describe("…", () => {}),it "…" do … end,t.Run("…", func(…) {}),app.get("/…", () => {}),it("…", function() … end), …) and show the call in the breadcrumb so changes inside test cases, subtests, and route handlers locate themselves by suite/route even when the surrounding callback body is too large to fit in the hunk. Unlabeled callbacks (xs.map(x => …),Promise.then(…)) still anchor on their enclosing named function as before. - review: Remove the
Spacepage-down shortcut (usePgDn).
- Add
[theme] mode = "light" | "dark" | "auto"to select the built-in light/dark palettes.
- Use the same light/dark mode for syntax-theme fallback and diff/UI colors.
- Fail with a clear
git fetchhint when a diff references blobs missing from your local repo (e.g.gh pr difffor an unfetched PR). Previously rendered an empty or misleading diff.
- Add public
SnapshotandDiffOptypes for line-level diffs without tree-sitter scope expansion.Snapshot::compute(original, updated)runs the line-level engine once and exposesops(),unified_text(), andalign_old_to_new(line).Diff::snapshot()exposes the underlying snapshot. - Detect languages from shebang lines, filenames, and modelines (e.g. a Bash file named
script).
- Hide
syntax::ParsedFileinternals behind a small interface. Public methods:ParsedFile::parse(path, source),enclosing_scopes(line),is_structure(&scope), andis_data(&scope). - Extract the line-level diff engine from
scope.rsinto a privateenginemodule.Diffnow owns aSnapshotand delegates to it for raw ops and unified text. The scope planner and hunk builder consumeengine::DiffOpinstead of an inline copy. - Swap the line-level diff backend from
similartogix-imara-diffwith the Histogram algorithm and imara's line postprocessing. Hunks and unified text are produced by the same shared backend.
- Fix a bug where a
Deleteop spanning a partial scope plus one or more fully-deleted sibling scopes silently dropped every line beyond the first scope from the engine's hunks. The planner now walks the full delete range and emits one range per scope it intersects, so every removed line is accounted for in some hunk. - Holding
j/kin the trace browser TUI no longer falls behind the keyboard. The input loop now drains every buffered event into one batch and applies the whole batch before the next redraw, so a key-repeat burst collapses into a single redraw at the end of the burst instead of one redraw per repeat.
- Remove the
syntax::parse_filefree function (replaced byParsedFile::parse). - Make
syntax::ParsedFileinternals private:tree,structure_kinds,data_kinds,promoted_kinds, andfunction_body_kinds.
- Rework the scope model into two tiers per language. Named code structures (functions, classes, tables, headings) anchor hunks with innermost strategy; data containers (JSON/TS
object/array, YAMLblock_mapping/block_sequence) fall back with outermost-fit strategy. Small JSON/YAML diffs now render as a single hunk covering the whole root container. Breadcrumb boxes show only named structures, so JSON/YAML configs render with no box. - Recognise JS/TS class fields and lexical declarations whose value is an arrow function or function expression as scopes. Changes inside
foo = async () => { ... }(class field) orconst foo = () => { ... }(top-level) anchor onfoofor both the breadcrumb and the hunk range. Anonymous arrow callbacks and non-function fields are not promoted.
- Fix a bug where diff did not show when adding a line at end of file.
- Fix a bug where newly added files showed nested ancestor scope boxes.
- Fix a bug where a new sibling pair inserted next to a modified pair (JSON/YAML/JS/TS) was dropped from the rendered diff.
- Fix a bug where added lines could be lost when a Replace op spanned two top-level JSON pairs with different keys.
- Renames or arrow-to-method conversions after an earlier line-shifting edit no longer produce a duplicate hunk anchored on the new scope. Rename detection now uses diff-op alignment rather than absolute line position.
- Changes inside a method no longer climb to the enclosing class when the change spans more than one method or the inner method exceeds
MAX_SCOPE_LINES. The hunk anchors on the inner method (or falls back to default 3-line context with the method as the breadcrumb) instead of leaking unrelated sibling methods into the hunk. - Changes on method-level decorator lines (
@Cron(...),@EventPattern(...), etc.) anchor on the method they decorate instead of the enclosing class. Class-level decorators (@Injectable()) anchor on the class. - Functions declared inside another function body (e.g.
fn innerinsidefn outer, orconst inner = () => {}inside a class method) are treated as local helpers and no longer steal the hunk anchor from the enclosing named container. Top-level functions, class methods, class arrow-fields, and top-levelconst f = () => {}continue to be anchors.
Initial beta release.
- Add
deltoids, a diff filter with tree-sitter scope context. - Add
edit, a file edit tool for coding agents. - Add
write, a file write tool for coding agents. - Add
edit-tui, a trace browser TUI. - Add the pi plugin for agent integration.