refactor: 用 StopFailure 事件替代定时轮询,移除空闲检测定时器

- 移除 heartbeat 空闲检测机制(PreToolUse 空 matcher)
- 新增 StopFailure hooks 处理 API 错误中断场景
- PostToolUse 仅保留 Edit(Write 通过 PreToolUse 快照实现)
- 移除 extension.ts 中 idleTimer/idleCooldown/heartbeatWatcher
- 更新 README hooks 配置文档
- 添加 MIT LICENSE
This commit is contained in:
2026-06-24 17:58:15 +08:00
parent 50b3868cc5
commit fa44163ca3
4 changed files with 132 additions and 22 deletions
+44 -10
View File
@@ -30,6 +30,10 @@ export function activate(context: vscode.ExtensionContext) {
hookHandler = new HookHandler(changeSetManager, diffViewer);
statusBar = new StatusBarManager(changeSetManager, diffViewer);
// ★ 设置 hook 目录路径,供 snapshotManager 读取 PreToolUse 快照
const hookDir = getHookDir();
if (hookDir) changeSetManager.setHookDir(hookDir);
// DiffViewer 操作后刷新状态栏
diffViewer.onDiffAction = () => refreshUI();
@@ -95,6 +99,24 @@ function registerCommands(context: vscode.ExtensionContext) {
vscode.commands.registerCommand(COMMANDS.PREV_DIFF, () => navigateDiff(-1))
);
// ★ 逐次编辑导航
context.subscriptions.push(
vscode.commands.registerCommand('aiDiffPreview.nextEdit', () => diffViewer.showNextEdit())
);
context.subscriptions.push(
vscode.commands.registerCommand('aiDiffPreview.prevEdit', () => diffViewer.showPrevEdit())
);
context.subscriptions.push(
vscode.commands.registerCommand('aiDiffPreview.showEditAtIndex', () => {
const session = diffViewer.getActiveSession();
if (session) {
const set = changeSetManager.getCurrentSet();
const fc = set?.changes.find(c => c.id === session.fileId);
if (fc) diffViewer.showEditAtIndex(fc, 0);
}
})
);
// Diff 界面状态栏按钮
context.subscriptions.push(
vscode.commands.registerCommand('aiDiffPreview.acceptCurrentDiff', async () => {
@@ -150,6 +172,14 @@ function refreshUI(): void {
vscode.commands.executeCommand('setContext', 'aiDiffPreview.isActive', hasPending);
}
// ---- Hook 目录 ----
function getHookDir(): string | null {
const folder = vscode.workspace.workspaceFolders?.[0];
if (!folder) return null;
return path.join(folder.uri.fsPath, '.claude', 'hooks');
}
// ---- Hook 监听 ----
function registerHookFileWatcher(context: vscode.ExtensionContext) {
@@ -160,11 +190,13 @@ function registerHookFileWatcher(context: vscode.ExtensionContext) {
if (!fs.existsSync(hookDir)) fs.mkdirSync(hookDir, { recursive: true });
const pendingFile = path.join(hookDir, 'pending.json');
const watcher = vscode.workspace.createFileSystemWatcher(
// ★ 监听 pending.json(Edit/Stop/StopFailure 事件)
const pendingWatcher = vscode.workspace.createFileSystemWatcher(
new vscode.RelativePattern(hookDir, 'pending.json')
);
const handle = () => {
const handlePending = () => {
try {
if (!fs.existsSync(pendingFile)) return;
const content = fs.readFileSync(pendingFile, 'utf-8');
@@ -172,18 +204,20 @@ function registerHookFileWatcher(context: vscode.ExtensionContext) {
const event: ClaudeHookEvent = JSON.parse(content);
if (Date.now() - event.timestamp > 10000) return;
if (event.toolName === 'Stop') {
if (event.toolName === 'Stop' || event.toolName === 'StopFailure') {
// ★ 对话结束(正常完成或 API 失败)→ 触发 diff 总览
hookHandler.handleStop();
} else if (event.toolName === 'Edit' || event.toolName === 'Write') {
} else if (event.toolName === 'Edit') {
hookHandler.handleEdit(event);
}
refreshUI();
} catch (e) { console.error('[AI Diff] 处理触发文件失败:', e); }
} catch (e) { console.error('[AI Diff] 处理 pending 文件失败:', e); }
};
watcher.onDidChange(handle);
watcher.onDidCreate(handle);
context.subscriptions.push(watcher);
pendingWatcher.onDidChange(handlePending);
pendingWatcher.onDidCreate(handlePending);
context.subscriptions.push(pendingWatcher);
console.log(`[AI Diff] Hook 监听已启动: ${hookDir}`);
}
@@ -199,8 +233,8 @@ function checkPendingHookFile() {
fs.unlinkSync(pendingFile);
const event: ClaudeHookEvent = JSON.parse(content);
if (Date.now() - event.timestamp < 15000) {
if (event.toolName === 'Stop') hookHandler.handleStop();
else if (event.toolName === 'Edit' || event.toolName === 'Write') hookHandler.handleEdit(event);
if (event.toolName === 'Stop' || event.toolName === 'StopFailure') hookHandler.handleStop();
else if (event.toolName === 'Edit') hookHandler.handleEdit(event);
refreshUI();
}
} catch (e) { console.error('[AI Diff] 处理 pending hook 失败:', e); }