bookworm-smart-assistant/hooks/session-start-restore.js

106 lines
3.8 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* UserPromptSubmit Hook - 会话启动恢复器
* 1. 检测 handoff.json 存在时注入恢复上下文并归档
* 2. 每次会话首次 prompt 时重置 heartbeat 计数器解决 /clear 后计数不归零问题
*/
const fs = require('fs');
const path = require('path');
const CLAUDE_ROOT = require('./lib/root.js');
const readStdin = require('./lib/read-stdin.js');
const SESSION_STATE_DIR = path.join(CLAUDE_ROOT, 'session-state');
const HANDOFF_PATH = path.join(SESSION_STATE_DIR, 'handoff.json');
const RESTORE_MARKER = path.join(SESSION_STATE_DIR, '.session-restored');
const HEARTBEAT_FILE = path.join(CLAUDE_ROOT, 'debug', 'session-heartbeat.json');
(async () => {
try {
let hookData = {};
try { hookData = await readStdin(); } catch (_) {}
const sessionId = hookData.session_id || '';
let messages = [];
// === 1. 会话 ID 变化检测 → 重置 heartbeat ===
let lastSessionId = '';
try {
if (fs.existsSync(RESTORE_MARKER)) {
lastSessionId = fs.readFileSync(RESTORE_MARKER, 'utf8').trim();
}
} catch (_) {}
if (sessionId && sessionId !== lastSessionId) {
// 新会话(/clear 或新窗口),重置 heartbeat 计数器
if (fs.existsSync(HEARTBEAT_FILE)) {
fs.writeFileSync(HEARTBEAT_FILE, JSON.stringify({
count: 0, lastActivity: Date.now(), notified: []
}), 'utf8');
}
// 记录当前 session_id
if (!fs.existsSync(SESSION_STATE_DIR)) {
fs.mkdirSync(SESSION_STATE_DIR, { recursive: true });
}
fs.writeFileSync(RESTORE_MARKER, sessionId, 'utf8');
// === 1b. Auto Git Pull新会话拉取最新代码===
try {
const sync = require('../scripts/auto-git-sync.js');
const pullMsgs = sync.pullLatest();
if (pullMsgs && pullMsgs.length > 0) {
messages.push(pullMsgs.join('\n'));
}
} catch (_) {} // fail-open
}
// === 2. Handoff 恢复 ===
if (fs.existsSync(HANDOFF_PATH)) {
let handoff = {};
try {
handoff = JSON.parse(fs.readFileSync(HANDOFF_PATH, 'utf8'));
} catch (_) {
fs.unlinkSync(HANDOFF_PATH);
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
return process.exit(0);
}
// 检查是否过期24 小时)
const age = Date.now() - new Date(handoff.timestamp || 0).getTime();
if (age > 24 * 60 * 60 * 1000) {
const archiveName = `handoff-expired-${Date.now()}.json`;
fs.renameSync(HANDOFF_PATH, path.join(SESSION_STATE_DIR, archiveName));
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
return process.exit(0);
}
// 归档并注入恢复上下文
const archiveName = `handoff-${Date.now()}.json`;
fs.renameSync(HANDOFF_PATH, path.join(SESSION_STATE_DIR, archiveName));
messages.push([
'[SESSION_RESTORE] 检测到上次会话的 handoff 记录:',
`- 时间: ${handoff.timestamp}`,
`- 工作目录: ${handoff.working_directory || 'unknown'}`,
`- 工具调用数: ${handoff.tool_call_count || 'unknown'}`,
`- 摘要: ${handoff.conversation_summary || '无'}`,
'',
'请检查 memory 和 task 列表以恢复上下文。'
].join('\n'));
}
if (messages.length > 0) {
console.log(JSON.stringify({
continue: true,
suppressOutput: false,
systemMessage: messages.join('\n')
}));
} else {
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
}
} catch {
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
}
process.exit(0);
})();