#!/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');