#!/usr/bin/env node /** * Claude Code PostToolUse Hook 脚本 * * 当 Claude Code 执行 Write 或 Edit 工具后,此脚本被调用。 * 它从 stdin 读取 JSON,将事件写入触发文件,供 VSCode 扩展监听。 * * stdin JSON 格式: * { * "session_id": "abc123", * "tool_name": "Write" | "Edit", * "tool_input": { "file_path": "...", "content": "..." }, * "tool_response": { "success": true } * } */ const fs = require('fs'); const path = require('path'); // 触发文件目录 - VSCode 扩展会监听此目录 const TRIGGER_DIR = path.join(__dirname, '..', '.claude', 'hooks'); const TRIGGER_FILE = path.join(TRIGGER_DIR, 'pending.json'); async function main() { // 读取 stdin const chunks = []; for await (const chunk of process.stdin) { chunks.push(chunk); } const input = Buffer.concat(chunks).toString('utf-8'); let data; try { data = JSON.parse(input); } catch (e) { // 非 JSON 输入,忽略 process.exit(0); } const { tool_name, tool_input, tool_response } = data; // 只处理成功的 Write/Edit 操作 if (!tool_response || tool_response.error) { process.exit(0); } const filePath = tool_input?.file_path || tool_response?.filePath; if (!filePath) { process.exit(0); } // 构造事件数据 const event = { type: tool_name === 'Write' ? 'after_write_file' : 'after_code_edit', filePath: filePath, content: tool_input?.content || '', patch: tool_input?.new_content || tool_input?.content || '', oldString: tool_input?.old_string || '', newString: tool_input?.new_string || '', timestamp: Date.now(), toolName: tool_name, }; // 确保目录存在 if (!fs.existsSync(TRIGGER_DIR)) { fs.mkdirSync(TRIGGER_DIR, { recursive: true }); } // 写入触发文件(原子写入:先写临时文件再重命名) const tmpFile = TRIGGER_FILE + '.tmp'; fs.writeFileSync(tmpFile, JSON.stringify(event, null, 2), 'utf-8'); fs.renameSync(tmpFile, TRIGGER_FILE); } main().catch(() => { // 静默失败,不阻塞 Claude Code process.exit(0); });