65 lines
2.5 KiB
JavaScript
65 lines
2.5 KiB
JavaScript
|
|
#!/usr/bin/env node
|
||
|
|
/**
|
||
|
|
* patch-x11-x13-crlf-fix.js
|
||
|
|
* 修复 X11 路径校验 + X13 handoff 原子写, CRLF 规范化
|
||
|
|
*/
|
||
|
|
'use strict';
|
||
|
|
const fs = require('fs');
|
||
|
|
const path = require('path');
|
||
|
|
|
||
|
|
const HOOKS_DIR = path.join(__dirname, '..', '..', 'hooks');
|
||
|
|
let total = 0;
|
||
|
|
|
||
|
|
function patchFile(filePath, label, patches) {
|
||
|
|
if (!fs.existsSync(filePath)) { console.log('SKIP ' + label + ': not found'); return; }
|
||
|
|
let src = fs.readFileSync(filePath, 'utf8');
|
||
|
|
const hadCRLF = src.includes('\r\n');
|
||
|
|
let norm = src.replace(/\r\n/g, '\n');
|
||
|
|
|
||
|
|
for (const p of patches) {
|
||
|
|
if (norm.includes(p.sentinel)) { console.log('SKIP ' + label + '/' + p.id + ': already patched'); continue; }
|
||
|
|
if (!norm.includes(p.old)) { console.error('FAIL ' + label + '/' + p.id + ': old pattern not found'); process.exit(1); }
|
||
|
|
norm = norm.replace(p.old, p.new);
|
||
|
|
console.log('OK ' + p.id + ' applied');
|
||
|
|
total++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (hadCRLF) norm = norm.replace(/\n/g, '\r\n');
|
||
|
|
const tmp = filePath + '.tmp.' + process.pid;
|
||
|
|
fs.writeFileSync(tmp, norm, 'utf8');
|
||
|
|
fs.renameSync(tmp, filePath);
|
||
|
|
}
|
||
|
|
|
||
|
|
// === X11: context-pressure-monitor.js 路径校验 ===
|
||
|
|
patchFile(path.join(HOOKS_DIR, 'context-pressure-monitor.js'), 'context-pressure-monitor', [{
|
||
|
|
id: 'X11',
|
||
|
|
sentinel: '[PATCH-X11-PATH-VALIDATION]',
|
||
|
|
old: `function findTranscript(hookData) {
|
||
|
|
if (hookData.transcript_path && fs.existsSync(hookData.transcript_path)) {
|
||
|
|
return hookData.transcript_path;
|
||
|
|
}
|
||
|
|
const sid = hookData.session_id;
|
||
|
|
if (!sid || !fs.existsSync(PROJECTS_DIR)) return null;`,
|
||
|
|
new: `function findTranscript(hookData) { // [PATCH-X11-PATH-VALIDATION]
|
||
|
|
if (hookData.transcript_path) {
|
||
|
|
const resolved = path.resolve(hookData.transcript_path);
|
||
|
|
if (resolved.startsWith(CLAUDE_ROOT) && fs.existsSync(resolved)) {
|
||
|
|
return resolved;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const sid = hookData.session_id;
|
||
|
|
if (!sid || /[\\/\\\\]/.test(sid) || !fs.existsSync(PROJECTS_DIR)) return null;`
|
||
|
|
}]);
|
||
|
|
|
||
|
|
// === X13: pre-compact-handoff.js handoff.json 原子写 ===
|
||
|
|
patchFile(path.join(HOOKS_DIR, 'pre-compact-handoff.js'), 'pre-compact-handoff', [{
|
||
|
|
id: 'X13',
|
||
|
|
sentinel: '[PATCH-X13-HANDOFF-ATOMIC]',
|
||
|
|
old: " fs.writeFileSync(HANDOFF_PATH, JSON.stringify(handoff, null, 2), 'utf8');",
|
||
|
|
new: ` const _tmpHandoff = HANDOFF_PATH + '.tmp.' + process.pid; // [PATCH-X13-HANDOFF-ATOMIC]
|
||
|
|
fs.writeFileSync(_tmpHandoff, JSON.stringify(handoff, null, 2), 'utf8');
|
||
|
|
fs.renameSync(_tmpHandoff, HANDOFF_PATH);`
|
||
|
|
}]);
|
||
|
|
|
||
|
|
console.log('DONE: ' + total + ' patches applied');
|