migrate-memory-raw-section.js
4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env node
/**
* 一次性迁移:把 `memory.md` 里**自动追加**的那些行,从 `## 关键事实`
* 搬到 `## 原始记录(未提炼)`。
*
* ── 为什么 ────────────────────────────────────────────────────────────
* 旧的 `appendToMemoryMd` 取任务与结果的第一行、砍到 80/100 字符,写进「关键事实」。
* 那不是提炼,是搬运 + 截断。实查(2026-08-20)现有内容里**没有一条真事实**:
*
* - [2026-08-18] [SYSTEM HEARTBEAT] → 🔧 [TOOL_CALL] Bash (ID: toolu_01M1t4...)
* - [2026-04-09] [TASK from master] → ...ates/community-manager/train/evolve/SKILL_v1.md` |
*
* 前者是工具调用 id,后者是**半句**(`...` 开头,主语被截掉了)。
* 而 `memory.md` 会被注入**每一轮**提示词 —— 也就是说这些东西一直在冒充事实。
*
* 写入侧已改(29-DREAM §四 步骤 1),但**存量不会自己搬家**。这个脚本补那一步。
*
* ── 判据(刻意窄)────────────────────────────────────────────────────
* 只搬形如 `- [YYYY-MM-DD] … → …` 的行 —— 那是旧自动追加的**唯一**形状。
* 人手写的事实不长这样,不动它。判不准就不搬:留在原地最多是「多了一条噪声」,
* 搬错则是把人写的东西降级成日志。
*
* ── 用法 ──────────────────────────────────────────────────────────────
* node scripts/migrate-memory-raw-section.js # dry-run,只打印
* node scripts/migrate-memory-raw-section.js --apply # 真改(先自动备份到 .bak)
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const AGENTS = path.join(ROOT, 'system', 'agents');
const RAW_SECTION = '## 原始记录(未提炼)';
/** 旧自动追加的唯一形状 */
const AUTO_LINE = /^-\s*\[\d{4}-\d{2}-\d{2}\]\s.*→/;
const apply = process.argv.includes('--apply');
/** 找出所有 memory.md(本体根 + 公司态两层都可能有) */
function findMemoryFiles() {
const out = [];
const walk = (dir, depth) => {
if (depth > 3) return;
let entries;
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
for (const e of entries) {
const p = path.join(dir, e.name);
if (e.isDirectory()) walk(p, depth + 1);
else if (e.name === 'memory.md' && path.basename(dir) === 'memory') out.push(p);
}
};
walk(AGENTS, 0);
return out;
}
function migrate(text) {
const factsIdx = text.indexOf('## 关键事实');
if (factsIdx < 0) return null;
const head = text.slice(0, factsIdx);
const rest = text.slice(factsIdx + '## 关键事实'.length);
const body = rest.split(/\n## /)[0];
const tail = rest.slice(body.length);
const lines = body.split('\n');
const moved = lines.filter((l) => AUTO_LINE.test(l.trim()));
if (moved.length === 0) return null;
const kept = lines.filter((l) => !AUTO_LINE.test(l.trim()));
// 「关键事实」清空后要留占位,否则下游读到空段会以为文件坏了
const keptText = kept.join('\n').trim() || '\n_(尚无提炼产物)_\n';
let out = `${head}## 关键事实\n${keptText.startsWith('\n') ? keptText : `\n${keptText}\n`}${tail}`;
// 合并进已有的原始记录段;没有就补一个(放最后,提炼产物的位置在前)
if (out.includes(RAW_SECTION)) {
out = out.replace(
new RegExp(`${RAW_SECTION}\\n([\\s\\S]*?)(?=\\n## |$)`),
(m, existing) => `${RAW_SECTION}\n${moved.join('\n')}\n${existing.trim() ? `${existing.trim()}\n` : ''}`
);
} else {
out = `${out.replace(/\s*$/, '')}\n\n${RAW_SECTION}\n${moved.join('\n')}\n`;
}
return { out, movedCount: moved.length };
}
let files = 0; let lines = 0;
for (const f of findMemoryFiles()) {
const text = fs.readFileSync(f, 'utf-8');
const r = migrate(text);
if (!r) continue;
files += 1; lines += r.movedCount;
console.log(`${apply ? '改' : '待改'} ${path.relative(ROOT, f)} 搬 ${r.movedCount} 行`);
if (apply) {
fs.writeFileSync(`${f}.bak`, text, 'utf-8'); // 这些是 gitignore 的运行态,没有 git 历史兜底
fs.writeFileSync(f, r.out, 'utf-8');
}
}
console.log(`\n${apply ? '已改' : 'dry-run'}:${files} 个文件 / ${lines} 行`);
if (!apply && files) console.log('要真改:node scripts/migrate-memory-raw-section.js --apply');