ClaudeMods
☰
KO
● 0 명 접속 중 · 조회 0 회
후원프로젝트 제출
공식 예제 · 작성자 anthropics

diff

diff 패널을 플러그인으로 제공합니다. /diff는 세션의 커밋되지 않은 변경을 transcript 옆에 파일별 hunk와 함께 열고 Claude가 편집하거나 명령을 실행할 때 새로 고칩니다. 첫 편집은 터미널 너비가 충분할 때 열리며 파일의 ask 버튼은 다음 프롬프트에 해당 hunk를 실어 보냅니다.

번역 완료

이 mod 소개

diff

diff 패널을 플러그인으로 제공합니다. /diff는 세션의 커밋되지 않은 변경을 transcript 옆에 열고, 변경된 파일마다 한 줄과 그 아래 모든 파일의 hunk를 보여 준 뒤 다시 닫습니다. 전환할 때마다 transcript에 Diff panel shown 또는 Diff panel hidden을 남깁니다. 헤더와 파일 목록, 전환 버튼은 그대로 둡니다. 휠이 hunk 위에 있을 때는 한 번에 3줄씩, 8줄보다 긴 목록 위에 있을 때는 한 번에 파일 하나씩 움직입니다(플러그인이 패널의 ui.scroll에 직접 응답). 행을 클릭하면 해당 파일의 hunk가 맨 위로 옵니다. 목록은 내장 목록 키(ctrl+up/ctrl+down, opt+up/opt+down)로도 스크롤되고 ctrl+x b는 내장 단축키처럼 비교 기준을 앞으로 옮깁니다. 둘 다 엔진 자체 동작을 선언하는 Buttons를 통합니다. 패널은 Claude가 편집하고 셸 명령을 실행할 때 새로 고치며, 열려 있는 동안 저장소의 HEAD도 폴링하므로 다른 곳에서 만든 커밋이나 checkout도 표시합니다.

메인 루프에서 세션의 첫 성공 편집이 발생하면 내장 패널이 첫 checkpoint에서 열리는 것처럼 패널을 자동으로 엽니다. 레이아웃이 각 그림의 viewport가 말하는 전체 화면 배치에서 transcript 옆에 도킹하고, 터미널 너비가 충분하며(사용자가 선택하지 않았으면 144열, 전에 열어 둔 경우 110열, 닫았던 경우 그 선택을 존중), 파일 checkpointing이 켜져 있을 때입니다. 서브에이전트의 편집은 열지 않고 화면에 지시가 없으면 스스로도 열지 않습니다. 도킹된 패널은 열기 전에 fetch하여 내장 패널이 데이터를 준비하는 것처럼 Loading diff… 상태로 남지 않습니다. 엔진이 그려지지 않은 채 대기시키던 열기 요청은 철회하므로 이후 resize가 자리를 만들지 않으며 다음 편집에서 다시 묻습니다. transcript에 이미 그런 편집이 있는 재개·계속 세션은 너비를 알게 되는 즉시 같은 조건으로 패널을 엽니다.

전체 화면 배치에서 터미널이 110열보다 좁으면 내장 패널이 더 넓은 폭을 요구하는 한 줄만 표시하고 아무것도 열지 않습니다. 해당 배치가 없으면(CLAUDE_CODE_NO_FLICKER=0, /diff가 명령의 presentation에서 읽음) 패널은 어떤 너비에서도 인라인으로 열리고 포커스를 얻으며 내용 높이(열기의 rows)만큼 표시됩니다. 모양은 내장 대화상자와 같고 제목, 개수, 선택된 항목 주변의 파일 행 5개(❯는 포커스 링의 시작 위치이며 플러그인은 ui.focus를 통한 링의 이동을 따라 내장처럼 행을 다시 가운데 맞춤), 키 힌트를 보여 줍니다. Enter는 그 파일의 hunk만 표시하고 Escape는 목록으로 돌아갔다가 닫으며 Diff dialog dismissed를 남깁니다. 떠 있는 동안 toast는 보류합니다. 파일의 ask 버튼은 파일을 무장하고 해당 hunk를 다음 프롬프트의 컨텍스트에 한 번만 넣습니다.

패널은 작업 트리와 HEAD를 비교합니다. 기본값은 세션 시작 시점을 경계로 삼습니다(엔진이 $.session.usage()에서 주는 시작점이므로 재개 세션은 최초 시작점을 유지하고 /clear는 새로 시작). HEAD와 직접 비교하거나 기본 브랜치와의 merge-base와 비교할 수도 있습니다. 헤더 아래 기준선은 세션 기준과 다른 기준을 표시하고 선택은 저장소별로 플러그인의 store에 보관합니다. 선택기는 작업 트리 대신 세션 메시지에서 읽은 이전 턴의 편집을 보여 줄 수 있습니다. 세션 시작 전 변경된 파일(패널이 저장소를 처음 읽었을 때 이미 dirty였던 경로 가운데 타임스탬프로 판별)과 잡음 파일(lockfile, 생성 파일, 테스트 파일)은 따로 표시하고 요청할 때까지 접어 둡니다. 이름 변경은 git이 출력하는 대로 표시합니다. git 저장소 밖에서는 /diff가 그 사실만 말하고 다른 일은 하지 않습니다.

내장 패널이 git을 실행하는 시점도 같습니다. 세션 시작 시에는 아무것도 하지 않습니다. /diff 또는 패널을 열 공간이 있는 첫 편집이 저장소를 처음 필요로 할 때 세션 시작 디렉터리에서 git rev-parse를 한 번 실행합니다(저장소가 아니라는 결과도 /clear 또는 /resume까지 보존). 작업 트리는 열린 패널에서 편집이 적용되거나 셸 명령이 실행된 뒤 fetch로만 읽습니다. 내장 패널에 대응하는 동작이 없는 유일한 읽기는 패널의 첫 fetch에서 수행하는 git status이며, 내장이 이동한 파일에 표시하는 변경 시각을 대신합니다.

hooks/register.ts가 모듈이고 hooks/ 아래는 모두 그 구성 요소입니다.

연결하는 항목

| event | 훅의 동작 | | --- | --- | | session.start | 엔진을 한 번 바인딩하고 /diff를 등록합니다(다른 /diff가 목록에 있는 세션에서는 플러그인이 대기). 저장소에는 묻지 않고 /diff 또는 첫 편집이 올 때 고정하며, 디스패치 바깥에서 transcript를 읽고 편집한 턴이 있는 재개 세션은 첫 편집처럼 패널을 엽니다. | | ui.render of PromptHint | 터미널 너비와 레이아웃이 패널을 도킹하는지를 읽어 첫 편집에서 열지 결정합니다. | | ui.render of Pane | 패널을 그립니다. 도킹 시 헤더, 기준선, 소스 선택기, 파일 목록과 전환을 hunk 창 위에 그리고 인라인에서는 대화상자를 그립니다. | | command.run of diff | 저장소가 고정되지 않았다면 고정하고 패널을 열거나 닫습니다(포커스를 주고 전체 화면 배치가 아니면 Escape로 닫음). 어느 상태인지 말하고 선택을 기억합니다. | | ui.close of the pane | 대화상자의 상세 보기에서 한 단계 뒤로 가며 닫지 않습니다. 그 외에는 사용자가 /diff로 닫은 선택을 기억합니다. | | ui.scroll of the pane | 도킹 시 고정된 헤더와 목록 아래에서 hunk를 움직입니다(휠은 한 번에 3줄, 페이지 키는 한 페이지). 휠이 목록 위면 목록을 움직이고 엔진 창은 그대로 둡니다. | | ui.focus in the pane | 대화상자 목록에서 링이 도달한 파일을 선택하고 그 파일을 중심으로 5개 행을 다시 배치한 다음 링을 현재 해당 행 위치에 둡니다. | | command.run of clear, resume | 패널을 닫고 세션 상태와 고정된 저장소를 함께 잊습니다. | | tool.call of Edit, Write, NotebookEdit | 적용된 편집(거부되거나 실패하지 않은 편집) 뒤 열린 패널을 새로 고칩니다. 메인 루프의 첫 편집이면 터미널 공간과 checkpointing이 있을 때 패널을 열고 그때 저장소를 고정합니다. | | tool.call of Bash, PowerShell | 거부되지 않은 명령 뒤 열린 패널을 새로 고칩니다. 실패와 중단도 포함합니다. | | prompt.submit | 무장한 파일의 hunk를 프롬프트 컨텍스트에 추가하고 무장을 해제합니다. |

$에 호출하는 항목

clock.after, clock.every, clock.now, command.register, env.get (CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING), fs.list, fs.read, fs.stat, process.run(읽기 전용 git), session.id, session.messages, settings.read, store.get, store.set, telemetry.log, telemetry.mark, ui.close, ui.invalidate, ui.log, ui.open, ui.resolve, ui.status.

$.telemetry는 telemetry 플러그인의 명사입니다. 없으면 해당 행은 버리고 다른 내용은 바꾸지 않습니다.

사용해 보기

claude --plugin-dir /path/to/diff

그다음 수정된 파일이 있는 git 저장소 안에서 /diff를 실행합니다.

설치

먼저 작성자의 README에서 marketplace와 플러그인 이름을 확인하세요. 저장소 구조에 따라 명령어가 달라질 수 있습니다.

claude plugin marketplace add anthropics/claude-code
claude plugin install diff
원문 / README

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 and runs shell commands, and while it is open it polls the repository's HEAD so a commit or checkout made elsewhere shows too. The main loop's first successful edit of a session opens the pane by itself, as the built-in panel opens on its first checkpoint: where the layout docks it beside the transcript (the fullscreen layout, which each drawing's viewport says), 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) and file checkpointing is on; a subagent's edit opens nothing, and where the surface does not say, nothing opens by itself. A docked pane fetches before it opens, as the built-in panel primes its data, so it never lands on Loading diff…; an open the engine leaves waiting undrawn is withdrawn, so no later resize seats it, and the next edit asks again. A session resumed or continued whose transcript already holds such an edit opens the pane on the same terms as soon as the width is known, as the built-in opens on the history it restores.

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; the start the engine gives in $.session.usage(), so a resumed session keeps its first and /clear begins anew), 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 when the pane first read the repository), 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.

Git runs when the built-in panel's would: nothing at the session's start; one git rev-parse, in the directory the session started in, when /diff or the first edit a pane has room to open on first needs the repository (an answer of no repository is kept too, until /clear or /resume forgets it); and the working tree is read only by a fetch for a pane that is open, after an edit that landed or a shell command that ran. The one read the built-in has no counterpart for is a git status at a pane's first fetch, which stands in for the change time the built-in dates a moved file by.

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 and registers /diff (a session where another /diff is listed leaves the plugin idle); asks nothing of the repository, which /diff or the first edit pins when it comes; off its dispatch, reads the transcript, and for a resumed session whose turns edited opens the pane as the first edit would. | | ui.render of PromptHint | Reads the terminal's width and whether its layout docks a pane, which decide 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 | Pins the repository when none is, 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, the pinned repository with it. | | tool.call of Edit, Write, NotebookEdit | After an edit that landed (not refused, not failed), refreshes an open pane; the main loop's first such edit opens it, pinning the repository then if the terminal has the room and checkpointing is on. | | tool.call of Bash, PowerShell | After a command that was not refused, failed and interrupted ones too, 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, env.get (CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING), fs.list, fs.read, fs.stat, process.run (git, read-only), session.id, session.messages, settings.read, 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

claude --plugin-dir /path/to/diff

then /diff inside a git repository with a modified file.

비슷한 프로젝트