bookworm-smart-assistant/hooks/lib/tse-retention-extractor.js

106 lines
3.4 KiB
JavaScript
Raw Permalink Normal View History

/**
* tse-retention-extractor.js · TSE v2.0 · 2026-04-27
* transcript 提取文件路径/函数名/TODO/决策点
* 用于 PreCompact 生成保留指令, 必须 <2s 完成
*/
'use strict';
var fs = require('fs');
function extract(transcriptPath) {
if (!transcriptPath || !fs.existsSync(transcriptPath)) return null;
var deadline = Date.now() + 2000;
try {
var stat = fs.statSync(transcriptPath);
if (stat.size > 30 * 1024 * 1024) return null;
var raw = fs.readFileSync(transcriptPath, 'utf8');
var lines = raw.split('\n').filter(Boolean);
var recent = lines.slice(-200);
var filePaths = {};
var functions = {};
var todos = [];
var decisions = [];
for (var i = 0; i < recent.length; i++) {
if (Date.now() > deadline) break;
var obj;
try { obj = JSON.parse(recent[i]); } catch { continue; }
var content = (obj && obj.message && obj.message.content) || (obj && obj.content);
if (!Array.isArray(content)) continue;
for (var j = 0; j < content.length; j++) {
var part = content[j];
if (!part) continue;
if (part.type === 'tool_use') {
var inp = part.input || {};
var fp = inp.file_path;
if (fp && typeof fp === 'string') {
filePaths[fp] = (filePaths[fp] || 0) + 1;
}
if (part.name === 'Grep' && inp.pattern) {
var match = inp.pattern.match(/[a-zA-Z_][a-zA-Z0-9_]{3,40}/);
if (match) functions[match[0]] = (functions[match[0]] || 0) + 1;
}
if (part.name === 'Edit' && inp.file_path) {
filePaths[inp.file_path] = (filePaths[inp.file_path] || 0) + 2;
}
}
var text = part.text || (typeof part === 'string' ? part : '');
if (typeof text === 'string' && text.length > 10) {
var todoRe = /(?:TODO|FIXME|HACK)[:\s](.{5,80})/gi;
var tm;
while ((tm = todoRe.exec(text)) !== null && todos.length < 5) {
todos.push(tm[1].trim());
}
var decRe = /(?:\u51b3\u5b9a|\u9009\u62e9|\u65b9\u6848|\u786e\u8ba4|decided|agreed|choose)[:\s](.{5,80})/gi;
var dm;
while ((dm = decRe.exec(text)) !== null && decisions.length < 5) {
decisions.push(dm[1].trim());
}
}
}
}
var out = ['[TSE\xb7RETENTION] Compact \u4fdd\u7559\u4ee5\u4e0b\u5173\u952e\u4e0a\u4e0b\u6587:'];
var sorted = Object.entries(filePaths).sort(function(a, b) { return b[1] - a[1]; }).slice(0, 10);
if (sorted.length > 0) {
out.push('## \u6d3b\u8dc3\u6587\u4ef6');
for (var si = 0; si < sorted.length; si++) {
var bn = sorted[si][0].split(/[\\/]/).pop();
out.push('- ' + bn + ' (' + sorted[si][1] + 'x) ' + sorted[si][0]);
}
}
var fnArr = Object.keys(functions).slice(0, 15);
if (fnArr.length > 0) {
out.push('## \u5173\u952e\u6807\u8bc6\u7b26');
out.push(fnArr.join(', '));
}
if (todos.length > 0) {
out.push('## \u5f85\u529e');
for (var ti = 0; ti < todos.length; ti++) out.push('- ' + todos[ti]);
}
if (decisions.length > 0) {
out.push('## \u5173\u952e\u51b3\u7b56');
for (var di = 0; di < decisions.length; di++) out.push('- ' + decisions[di]);
}
return out.length > 1 ? out.join('\n') : null;
} catch {
return null;
}
}
module.exports = { extract: extract };