bookworm-smart-assistant/scripts/archive/apply-remaining-patches.js

199 lines
8.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* 应用剩余手动补丁 (F1-2, F1-6, constitution-precheck 注册)
*
* 用法:
* node scripts/apply-remaining-patches.js # 预览
* node scripts/apply-remaining-patches.js --apply # 应用
*/
'use strict';
const fs = require('fs');
const path = require('path');
const CLAUDE_ROOT = path.join(require('os').homedir(), '.claude');
const HOOKS_DIR = path.join(CLAUDE_ROOT, 'hooks');
const dryRun = !process.argv.includes('--apply');
console.log('=== 剩余补丁应用工具 ===');
console.log(`模式: ${dryRun ? 'DRY-RUN (预览)' : 'APPLY'}`);
console.log('');
let patchCount = 0;
let errorCount = 0;
function backup(filePath) {
const bakPath = filePath + '.bak-remaining';
if (!fs.existsSync(bakPath)) {
fs.copyFileSync(filePath, bakPath);
console.log(` 已备份: ${bakPath}`);
}
}
// ═══════════════════════════════════════════════════
// F1-2: route-interceptor.js — 消除 intent-classifier 双重调用
// ═══════════════════════════════════════════════════
console.log('[F1-2] route-interceptor.js — 消除 intent-classifier 双重调用');
try {
const riPath = path.join(HOOKS_DIR, 'route-interceptor.js');
let content = fs.readFileSync(riPath, 'utf8');
// 改动 1: 函数签名增加 precomputedIntent 参数
const sig1 = 'function runRouteEngine(prompt, cwd)';
const sig1New = 'function runRouteEngine(prompt, cwd, precomputedIntent)';
// 改动 2: 替换内部重复调用为使用传入参数
const dup1 = "const intentClassifier = safeRequire(path.join(SCRIPTS_DIR, 'intent-classifier.js'));\n";
const dup2 = " const intentResult = intentClassifier ? intentClassifier.classifyIntent(prompt) : { intents: [], entities: [] };";
const dupReplace = "// F1-2: 使用调用方传入的 intent避免重复分类\n const intentResult = precomputedIntent || { intents: [], entities: [] };";
// 改动 3: main() 中传入 intent
const call1 = 'routing = runRouteEngine(prompt, cwd);';
const call1New = 'routing = runRouteEngine(prompt, cwd, intent);';
if (content.includes(sig1) && !content.includes(sig1New)) {
if (!dryRun) {
backup(riPath);
content = content.replace(sig1, sig1New);
// 替换内部双重调用(处理可能的缩进差异)
const dupPattern = /const intentClassifier = safeRequire\(path\.join\(SCRIPTS_DIR, 'intent-classifier\.js'\)\);\s*\n\s*const intentResult = intentClassifier \? intentClassifier\.classifyIntent\(prompt\) : \{ intents: \[\], entities: \[\] \};/;
content = content.replace(dupPattern, dupReplace);
content = content.replace(call1, call1New);
fs.writeFileSync(riPath, content);
console.log(` 已写入: ${riPath}`);
} else {
console.log(' [预览] 将修改函数签名、移除重复调用、传入已有 intent');
}
patchCount++;
} else if (content.includes(sig1New)) {
console.log(' [跳过] 已应用');
} else {
console.log(' [警告] 未找到目标代码,可能已修改');
errorCount++;
}
} catch (e) {
console.log(` [错误] ${e.message}`);
errorCount++;
}
console.log('');
// ═══════════════════════════════════════════════════
// F1-6: block-sensitive-files.js — self-healer 白名单
// ═══════════════════════════════════════════════════
console.log('[F1-6] block-sensitive-files.js — self-healer 安全白名单');
try {
const bsfPath = path.join(HOOKS_DIR, 'block-sensitive-files.js');
let content = fs.readFileSync(bsfPath, 'utf8');
const marker = 'SETTINGS_SAFE_KEYS';
if (content.includes(marker)) {
console.log(' [跳过] 白名单已存在');
} else {
// 在 "检查文件路径" 注释前插入白名单代码
const insertTarget = " // 检查文件路径\n for (const { pattern, reason } of SENSITIVE_PATH_PATTERNS) {\n if (pattern.test(filePath)) {";
const whitelistCode = ` // ─── F1-6: self-healer 白名单 ──────────────────────
// 允许 self-healer 修改 settings.json 中已知安全的配置键
const SETTINGS_SAFE_KEYS = new Set([
'skipDangerousModePermissionPrompt',
]);
function isSettingsSafeWrite(fp, c) {
if (!/[\\\\/]\\.claude[\\\\/]settings\\.json$/.test(fp)) return false;
if (!c) return false;
try {
const parsed = JSON.parse(c);
const keys = Object.keys(parsed);
return keys.length > 0 && keys.every(k => SETTINGS_SAFE_KEYS.has(k));
} catch { return false; }
}
// ──────────────────────────────────────────────────────
// 检查文件路径
for (const { pattern, reason } of SENSITIVE_PATH_PATTERNS) {
if (pattern.test(filePath)) {
// F1-6: self-healer 白名单豁免
if (isSettingsSafeWrite(filePath, content)) break;`;
if (content.includes(insertTarget)) {
if (!dryRun) {
backup(bsfPath);
content = content.replace(insertTarget, whitelistCode);
fs.writeFileSync(bsfPath, content);
console.log(` 已写入: ${bsfPath}`);
} else {
console.log(' [预览] 将在路径检查前插入 SETTINGS_SAFE_KEYS 白名单');
}
patchCount++;
} else {
console.log(' [警告] 未找到插入点,文件结构可能已变化');
errorCount++;
}
}
} catch (e) {
console.log(` [错误] ${e.message}`);
errorCount++;
}
console.log('');
// ═══════════════════════════════════════════════════
// 注册 constitution-precheck.js 到 settings.json
// ═══════════════════════════════════════════════════
console.log('[注册] constitution-precheck.js → settings.json PreToolUse Write|Edit');
try {
const settingsPath = path.join(CLAUDE_ROOT, 'settings.json');
let content = fs.readFileSync(settingsPath, 'utf8');
const settings = JSON.parse(content);
const hookCmd = 'node C:/Users/janson9527us/.claude/hooks/constitution-precheck.js';
const preToolUse = settings.hooks?.PreToolUse || [];
// 检查是否已注册
const alreadyRegistered = preToolUse.some(entry =>
entry.hooks && entry.hooks.some(h => h.command && h.command.includes('constitution-precheck'))
);
if (alreadyRegistered) {
console.log(' [跳过] 已注册');
} else {
// 找到 Write matcher在其 hooks 数组末尾追加
let registered = false;
for (const entry of preToolUse) {
if (entry.matcher === 'Write' || entry.matcher === 'Edit') {
entry.hooks = entry.hooks || [];
entry.hooks.push({
type: 'command',
command: hookCmd,
timeout: 3000
});
registered = true;
}
}
if (registered) {
if (!dryRun) {
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
console.log(` 已写入: ${settingsPath}`);
} else {
console.log(' [预览] 将在 Write 和 Edit matcher 中注册 constitution-precheck');
}
patchCount++;
} else {
console.log(' [警告] 未找到 Write/Edit PreToolUse 配置');
errorCount++;
}
}
} catch (e) {
console.log(` [错误] ${e.message}`);
errorCount++;
}
console.log('');
console.log('=== 执行摘要 ===');
console.log(` 补丁数: ${patchCount}`);
console.log(` 错误数: ${errorCount}`);
if (dryRun) {
console.log('');
console.log('运行 --apply 以应用: node scripts/apply-remaining-patches.js --apply');
}
console.log('');
console.log('完成。回滚: 使用 .bak-remaining 文件。');