Files
reverse-skills-max/vendor-new/xbsReverseSkill/web-js-env-patcher/scripts/check_change_memory.js

284 lines
7.8 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
'use strict';
const fs = require('fs');
const path = require('path');
const REQUIRED_FIELDS = [
'修改前逻辑',
'问题证据',
'本次修改',
'修改理由',
'已失败尝试',
'禁止回退',
'验证命令',
'验证结果',
'当前验证范围',
'遗留风险',
'当前状态',
];
function parseArgs(argv) {
const args = {
caseDir: 'case',
changed: [],
init: false,
requireEntry: false,
json: false,
markdown: false,
help: false,
};
for (let i = 2; i < argv.length; i++) {
const a = argv[i];
if (a === '--case-dir' || a === '--dir' || a === '-d') args.caseDir = argv[++i] || '';
else if (a === '--changed' || a === '--file') args.changed.push(argv[++i] || '');
else if (a === '--init') args.init = true;
else if (a === '--require-entry') args.requireEntry = true;
else if (a === '--json') args.json = true;
else if (a === '--markdown') args.markdown = true;
else if (a === '--help' || a === '-h') args.help = true;
else throw new Error(`未知参数:${a}`);
}
return args;
}
function usage() {
return `用法:
node scripts/check_change_memory.js --case-dir case --init --markdown
node scripts/check_change_memory.js --case-dir case --markdown
node scripts/check_change_memory.js --case-dir case --changed result/src/env/install-env.js --require-entry --markdown
node scripts/check_change_memory.js --case-dir case --changed result/src/signer/index.js --json
说明:检查复杂 case 是否维护 case/notes/代码变更记忆.md,并确认关键源码修改后记录了修改原因、失败尝试、禁止回退、验证范围和遗留风险。`;
}
function normalizeSlash(v) {
return String(v || '').replace(/\\/g, '/');
}
function exists(p) {
try { fs.accessSync(p); return true; } catch { return false; }
}
function memoryPath(caseDir) {
return path.join(path.resolve(caseDir), 'notes', '代码变更记忆.md');
}
function createTemplate() {
return `# 代码变更记忆
本文件由通用代码变更记忆机制维护,用于记录复杂 case 中关键源码修改的事实、原因、失败尝试、禁止回退、验证范围和遗留风险。不要把“当前报错消失”直接写成永久结论。
## 使用规则
- 修改关键源码前先读取本文件,并搜索文件名、函数名、错误关键词和参数名。
- 修改关键源码后立即追加一条“变更”记录。
- 不删除失败记录;如历史记录被新证据替代,新增记录说明替代原因。
- 状态只能使用:临时修复 / 当前验证通过 / 已失败 / 被替代 / 稳定基线。
## 变更 001 - 待填写
- 时间:YYYY-MM-DD HH:mm
- 涉及文件:
- 涉及函数 / 模块:
- 当前状态:临时修复
### 修改前逻辑
待填写。
### 问题证据
待填写。
### 本次修改
待填写。
### 修改理由
待填写。
### 已失败尝试
待填写。
### 禁止回退
待填写。
### 验证命令
\`\`\`bash
# 待填写
\`\`\`
### 验证结果
待填写。
### 当前验证范围
待填写。
### 遗留风险
待填写。
`;
}
function initMemory(file) {
fs.mkdirSync(path.dirname(file), { recursive: true });
if (!exists(file)) {
fs.writeFileSync(file, createTemplate(), 'utf8');
return 'created';
}
return 'kept';
}
function hasEntry(text) {
return /^##\s+变更\s+\d+/m.test(text);
}
function fieldMissing(text) {
return REQUIRED_FIELDS.filter(field => !text.includes(field));
}
function changedMatched(text, changedFile) {
const normalized = normalizeSlash(changedFile).replace(/^\.\//, '');
const basename = path.basename(normalized);
const textSlash = normalizeSlash(text);
if (normalized && textSlash.includes(normalized)) return true;
if (basename && textSlash.includes(basename)) return true;
return false;
}
function check(args) {
const caseDir = path.resolve(args.caseDir || 'case');
const file = memoryPath(caseDir);
const problems = [];
const warnings = [];
const actions = [];
if (args.init) actions.push({ action: initMemory(file), path: file });
if (!exists(file)) {
problems.push(`未找到通用代码变更记忆文件:${file}`);
return {
caseDir,
memoryFile: file,
exists: false,
clean: false,
actions,
requiredFields: REQUIRED_FIELDS,
missingFields: REQUIRED_FIELDS.slice(),
changed: args.changed,
matchedChanged: [],
problems,
warnings,
};
}
const text = fs.readFileSync(file, 'utf8');
const missingFields = fieldMissing(text);
if (missingFields.length) problems.push(`代码变更记忆缺少必要字段:${missingFields.join('、')}`);
const entryExists = hasEntry(text);
if (args.requireEntry && !entryExists) problems.push('已要求至少一条变更记录,但未发现 “## 变更 001” 这类记录。');
if (!entryExists) warnings.push('尚未发现正式变更记录;如果已经修改关键源码,请立即追加记录。');
const matchedChanged = [];
for (const changed of args.changed.filter(Boolean)) {
const matched = changedMatched(text, changed);
matchedChanged.push({ file: changed, matched });
if (args.requireEntry && !matched) {
problems.push(`已修改关键文件但代码变更记忆中未提到该文件:${changed}`);
} else if (!matched) {
warnings.push(`代码变更记忆中未提到变更文件:${changed}`);
}
}
if (/正确方案/.test(text)) {
warnings.push('发现“正确方案”表述;建议改为“当前验证通过 / 稳定基线”,并写明验证范围和遗留风险。');
}
return {
caseDir,
memoryFile: file,
exists: true,
clean: problems.length === 0,
actions,
requiredFields: REQUIRED_FIELDS,
missingFields,
hasEntry: entryExists,
changed: args.changed,
matchedChanged,
problems,
warnings,
};
}
function renderMarkdown(result) {
const lines = [
'# 通用代码变更记忆检查',
'',
`- case 目录:${result.caseDir}`,
`- 记忆文件:${result.memoryFile}`,
`- 文件存在:${result.exists ? '是' : '否'}`,
`- 是否通过:${result.clean ? '是' : '否'}`,
'',
];
if (result.actions.length) {
lines.push('## 初始化动作');
for (const item of result.actions) {
lines.push(`- ${item.action === 'created' ? '已创建' : '已存在'}:${item.path}`);
}
lines.push('');
}
lines.push('## 必要字段');
for (const field of result.requiredFields) {
lines.push(`- ${field}:${result.missingFields.includes(field) ? '缺失' : '存在'}`);
}
lines.push('');
if (result.matchedChanged && result.matchedChanged.length) {
lines.push('## 变更文件命中');
for (const item of result.matchedChanged) {
lines.push(`- ${item.file}:${item.matched ? '已记录' : '未记录'}`);
}
lines.push('');
}
if (result.problems.length) {
lines.push('## 问题');
for (const p of result.problems) lines.push(`- ${p}`);
lines.push('');
}
if (result.warnings.length) {
lines.push('## 提醒');
for (const w of result.warnings) lines.push(`- ${w}`);
lines.push('');
}
return lines.join('\n') + '\n';
}
try {
const args = parseArgs(process.argv);
if (args.help) {
console.log(usage());
process.exit(0);
}
const result = check(args);
if (args.json) console.log(JSON.stringify(result, null, 2));
if (args.markdown || !args.json) process.stdout.write(renderMarkdown(result));
process.exit(result.clean ? 0 : 1);
} catch (err) {
console.error(err.message || String(err));
console.error(usage());
process.exit(1);
}