# diff The diff pane as a plugin: `/diff` opens the session's uncommitted changes beside the transcript, one row per changed file and every file's hunks beneath, and closes it again; each toggle leaves `Diff panel shown` or `Diff panel hidden` in the transcript. The header, the file list and its toggles stay put while the wheel moves the hunks under them three rows a tick, or the list a file a tick while the wheel is over a list longer than its eight rows (the plugin answers the pane's `ui.scroll` itself); a row's click puts that file's hunks at the top; the list also scrolls under the built-in's list keys (`ctrl+up`/`ctrl+down`, `opt+up`/`opt+down`), and `ctrl+x b` moves the comparison base on, as the built-in's chord does: both through Buttons that declare the engine's own actions. The pane refreshes as Claude edits, runs shell commands and finishes turns, and while it is open it polls the repository's HEAD so a commit or checkout made elsewhere shows too. The first successful edit of a session opens the pane by itself where the terminal is wide enough (144 columns when the person never chose, 110 when they kept it open before; a person who closed it is left alone). Under the fullscreen layout a terminal under 110 columns gets the built-in's line asking for a wider one and nothing opens. Without that layout (`CLAUDE_CODE_NO_FLICKER=0`, which `/diff` learns from the command's `presentation`) the pane opens inline at any width, focused and as tall as its content (the open's `rows`), in the built-in dialog's shape: the title, the count, five file rows at a time round the selected one (`❯`, where the focus ring starts; the plugin follows the ring's walk through `ui.focus` and re-centres the rows as the built-in does), the key hints; Enter shows that file's hunks alone, Escape backs out to the list and then closes, leaving `Diff dialog dismissed`; toasts are held while it is up. A file's ask button arms that file: its hunks ride the next prompt as context, once. The pane compares the working tree against HEAD, split at the session's start (the default), against HEAD plainly, or against the merge-base with the default branch; the base line under the header names a base other than the session's, and the choice is kept per repository in the plugin's store. A picker shows one earlier turn's edits instead of the working tree, read from the session's messages. Files that changed before the session started (by their timestamp, among the paths already dirty at the start), and noise (lockfiles, generated and test files), are listed apart and folded until asked for; a rename lists as git prints it. Outside a git repository `/diff` says so and does nothing else. `hooks/register.ts` is the module; everything under `hooks/` is its parts. ## What it hooks | event | what the hook does | | --- | --- | | `session.start` | Binds the engine once, registers `/diff` (a session where another `/diff` is listed leaves the plugin idle), and pins the repository. | | `ui.render` of `PromptHint` | Reads the terminal's width, which decides whether the first edit opens the pane. | | `ui.render` of `Pane` | Draws the pane: docked, the header, base line, source picker, file list and toggles over the window of hunks; inline, the dialog. | | `command.run` of `diff` | Opens or closes the pane (focused and closing on Escape without the fullscreen layout), says which, and remembers the choice. | | `ui.close` of the pane | Backs out of the dialog's detail view instead of closing; else remembers the person's close as `/diff`'s. | | `ui.scroll` of the pane | Docked, moves the hunks under the pinned header and list (three rows a wheel tick, a page a page key), or the list when the wheel is over it, and keeps the engine's window still. | | `ui.focus` in the pane | In the dialog's list, selects the file the ring lands on, re-centres the five rows on it, and lands the ring where that row now sits. | | `command.run` of `clear`, `resume` | Closes the pane and forgets the session's state. | | `tool.call` of `Edit`, `Write`, `NotebookEdit` | After the edit, refreshes an open pane; the session's first successful edit opens it. | | `tool.call` of `Bash`, `PowerShell` | After the command, refreshes an open pane. | | `turn.complete` | Refreshes an open pane. | | `prompt.submit` | Adds the armed file's hunks to the prompt's context and disarms. | ## What it calls on `$` `clock.after`, `clock.every`, `clock.now`, `command.register`, `fs.list`, `fs.read`, `fs.stat`, `process.run` (git, read-only), `session.messages`, `store.get`, `store.set`, `telemetry.log`, `telemetry.mark`, `ui.close`, `ui.invalidate`, `ui.log`, `ui.open`, `ui.resolve`, `ui.status`. `$.telemetry` is the telemetry plugin's noun; where it is absent the rows are dropped and nothing else changes. ## Try it ```sh claude --plugin-dir /path/to/diff ``` then `/diff` inside a git repository with a modified file.