# AI Code Diff Preview 类 Cursor 风格的 AI 代码差异预览 VSCode 插件 — Accept/Reject 审查变更。 ## 使用方法 1. 安装 `.vsix` 并重启 VSCode 2. 将下面配置复制到项目 `.claude/settings.json`(或 `~/.claude/settings.json` 全局生效) 3. 用 Claude Code 编辑项目 → Stop 后自动弹出 QuickPick 变更列表 → VSCode 内置 Diff 逐文件审查 ![AI Diff](https://raw.githubusercontent.com/Gmaker689/ai-diff-preview/master/assets/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202026-06-19%20134307.png) 触发AI Diff后快捷键`Ctrl+Shift+D`打开`QuickPick`预览,一键 `接受/拒绝` 变更 ![QuickPick](https://raw.githubusercontent.com/Gmaker689/ai-diff-preview/master/assets/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202026-06-19%20134244.png) 进入单个`diff`界面可在状态栏右下角选择单个文件`接受/拒绝` ![Accepet/Reject](https://raw.githubusercontent.com/Gmaker689/ai-diff-preview/master/assets/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202026-06-19%20134322.png) ## Claude Code Hooks 必须配置 将以下内容写入 `.claude/settings.json`: ![settings.json路径](https://raw.githubusercontent.com/Gmaker689/ai-diff-preview/master/assets/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202026-06-19%20115339.png) ```json { "hooks": { "PreToolUse": [ { "matcher": "Write", "hooks": [ { "type": "command", "command": "node -e \"const fs=require('fs'),p=require('path');let b='';process.stdin.on('data',c=>b+=c);process.stdin.on('end',()=>{try{const j=JSON.parse(b);const fp=j.tool_input?.file_path;if(!fp)return;const d=p.join(process.env.CLAUDE_PLUGIN_ROOT||'.','.claude','hooks');fs.mkdirSync(d,{recursive:true});let oc='';try{oc=fs.readFileSync(fp,'utf-8')}catch(e){}const s={originalContent:oc,timestamp:Date.now()};const f=p.join(d,'pre-write-snapshot.json'),tmp=f+'.tmp';fs.writeFileSync(tmp,JSON.stringify(s));fs.renameSync(tmp,f)}catch(e){}})\"", "timeout": 5 } ] } ], "PostToolUse": [ { "matcher": "Edit", "hooks": [ { "type": "command", "command": "node -e \"const fs=require('fs'),p=require('path'),d=p.join(process.env.CLAUDE_PLUGIN_ROOT||'.','.claude','hooks');let b='';process.stdin.on('data',c=>b+=c);process.stdin.on('end',()=>{try{const j=JSON.parse(b);const e={type:'edit',filePath:j.tool_input?.file_path||'',content:j.tool_input?.content||'',oldString:j.tool_input?.old_string||'',newString:j.tool_input?.new_string||'',timestamp:Date.now(),toolName:'Edit'};fs.mkdirSync(d,{recursive:true});const t=p.join(d,'pending.json'),tmp=t+'.tmp';fs.writeFileSync(tmp,JSON.stringify(e));fs.renameSync(tmp,t)}catch{}})\"", "timeout": 5 } ] } ], "Stop": [ { "hooks": [ { "type": "command", "command": "node -e \"const fs=require('fs'),p=require('path'),d=p.join(process.env.CLAUDE_PLUGIN_ROOT||'.','.claude','hooks');try{fs.mkdirSync(d,{recursive:true});const t=p.join(d,'pending.json'),tmp=t+'.tmp';fs.writeFileSync(tmp,JSON.stringify({type:'stop',filePath:'',content:'',oldString:'',newString:'',timestamp:Date.now(),toolName:'Stop'}));fs.renameSync(tmp,t)}catch{}\"", "timeout": 5 } ] } ], "StopFailure": [ { "hooks": [ { "type": "command", "command": "node -e \"const fs=require('fs'),p=require('path'),d=p.join(process.env.CLAUDE_PLUGIN_ROOT||'.','.claude','hooks');try{fs.mkdirSync(d,{recursive:true});const t=p.join(d,'pending.json'),tmp=t+'.tmp';fs.writeFileSync(tmp,JSON.stringify({type:'stopFailure',filePath:'',content:'',oldString:'',newString:'',timestamp:Date.now(),toolName:'StopFailure'}));fs.renameSync(tmp,t)}catch{}\"", "timeout": 5 } ] } ] } } ``` ### Hooks 说明 | Hook | 匹配器 | 作用 | | --- | --- | --- | | **PreToolUse** | `Write` | 写入前保存文件原始内容快照,用于 diff 对比 | | **PostToolUse** | `Edit` | 记录编辑信息(oldString/newString),用于精确 diff | | **Stop** | — | Claude 正常完成响应 → 触发变更总览 | | **StopFailure** | — | API 错误导致中断 → 触发变更总览 | ## 命令 | 命令 | 快捷键 | 功能 | | --- | --- | --- | | `AI Diff: 显示所有变更文件列表` | `Ctrl+Shift+D` | QuickPick 总览 + Accept All / Reject All | | `AI Diff: 下一个变更` | `Alt+↓` | 跳转下一个 pending 文件 | | `AI Diff: 上一个变更` | `Alt+↑` | 跳转上一个 pending 文件 | | `AI Diff: 接受所有变更` | `Ctrl+Shift+A` | Accept All | | `AI Diff: 拒绝所有变更` | `Ctrl+Shift+R` | Reject All | Diff 编辑器打开后,状态栏右侧自动出现 **Accept / Reject / 接受全部** 按钮。 ## 开发 ```bash npm install npm run build # 编译 npm run test # 测试 npm run package # 生成 ai-diff-preview-*.vsix ``` ## 许可证 MIT © 2026 [Gmaker689](https://github.com/Gmaker689)