Files
ai-diff-preview/README.md
T
Gmarker689 fa44163ca3 refactor: 用 StopFailure 事件替代定时轮询,移除空闲检测定时器
- 移除 heartbeat 空闲检测机制(PreToolUse 空 matcher)
- 新增 StopFailure hooks 处理 API 错误中断场景
- PostToolUse 仅保留 Edit(Write 通过 PreToolUse 快照实现)
- 移除 extension.ts 中 idleTimer/idleCooldown/heartbeatWatcher
- 更新 README hooks 配置文档
- 添加 MIT LICENSE
2026-06-24 17:58:15 +08:00

108 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)