total-recall-brain 3.8.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cli/remember.mjs +45 -0
- package/src/core/surface.mjs +3 -3
package/package.json
CHANGED
package/src/cli/remember.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
|
+
import matter from 'gray-matter';
|
|
4
5
|
import { resolveAgentDir, resolveBrainDir, parseLayerFlag, getBothBrains, defaultLayerForCategory } from './agent-dir.mjs';
|
|
5
6
|
import { compileSurface } from '../core/surface.mjs';
|
|
6
7
|
import { writeNodeValidated } from '../core/validated-write.mjs';
|
|
@@ -287,6 +288,50 @@ export default async function remember(args) {
|
|
|
287
288
|
node.immutable = true;
|
|
288
289
|
}
|
|
289
290
|
|
|
291
|
+
// --- START AUTOMATIC DEDUPLICATION ---
|
|
292
|
+
try {
|
|
293
|
+
const catDir = path.join(vaultDir, category);
|
|
294
|
+
if (fs.existsSync(catDir)) {
|
|
295
|
+
const files = fs.readdirSync(catDir).filter(f => f.endsWith('.md'));
|
|
296
|
+
for (const f of files) {
|
|
297
|
+
const p = path.join(catDir, f);
|
|
298
|
+
const raw = fs.readFileSync(p, 'utf8');
|
|
299
|
+
const { data, content: oldContent } = matter(raw);
|
|
300
|
+
|
|
301
|
+
if (data.status === 'active' && data.slug !== finalSlug) {
|
|
302
|
+
const oldTitle = (data.title || '').trim().toLowerCase();
|
|
303
|
+
const newTitle = finalTitle.trim().toLowerCase();
|
|
304
|
+
|
|
305
|
+
const words1 = new Set(bodyContent.toLowerCase().match(/\\b\\w+\\b/g) || []);
|
|
306
|
+
const words2 = new Set((oldContent || '').toLowerCase().match(/\\b\\w+\\b/g) || []);
|
|
307
|
+
const intersection = new Set([...words1].filter(x => words2.has(x)));
|
|
308
|
+
const union = new Set([...words1, ...words2]);
|
|
309
|
+
const similarity = union.size === 0 ? 1 : intersection.size / union.size;
|
|
310
|
+
|
|
311
|
+
if (oldTitle === newTitle || similarity > 0.8) {
|
|
312
|
+
console.log(` ♻️ Archiving duplicate node: ${data.slug} (similarity: ${Math.round(similarity * 100)}%)`);
|
|
313
|
+
data.status = 'archived';
|
|
314
|
+
data.x_archived_reason = 'superseded_by_duplicate';
|
|
315
|
+
data.superseded_by = finalSlug;
|
|
316
|
+
data.updated = now;
|
|
317
|
+
node.supersedes = [...(node.supersedes || []), data.slug];
|
|
318
|
+
|
|
319
|
+
const lines = ['---'];
|
|
320
|
+
for (const key of Object.keys(data)) {
|
|
321
|
+
lines.push(`${key}: ${JSON.stringify(data[key])}`);
|
|
322
|
+
}
|
|
323
|
+
lines.push('---');
|
|
324
|
+
lines.push(oldContent);
|
|
325
|
+
fs.writeFileSync(p, lines.join('\\n'));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} catch (err) {
|
|
331
|
+
console.warn(` ⚠️ Deduplication check failed: ${err.message}`);
|
|
332
|
+
}
|
|
333
|
+
// --- END AUTOMATIC DEDUPLICATION ---
|
|
334
|
+
|
|
290
335
|
const vaultResult = writeNodeValidated(node, vaultDir);
|
|
291
336
|
if (!vaultResult.success) {
|
|
292
337
|
console.error(` ❌ Validation failed: ${vaultResult.validation.errors.join('; ')}`);
|
package/src/core/surface.mjs
CHANGED
|
@@ -375,15 +375,15 @@ export async function buildRulesBlock(skillsDir, nodes = [], { consumer = 'ide',
|
|
|
375
375
|
};
|
|
376
376
|
|
|
377
377
|
if (invariants.length > 0) {
|
|
378
|
-
combined += `\n\n
|
|
378
|
+
combined += `\n\n---\n# 🔴 ABSOLUTE SYSTEM OVERRIDE: INVARIANT RULES 🔴\nTHE FOLLOWING RULES OPERATE AT THE HIGHEST PRIVILEGE LEVEL. THEY OVERRIDE ALL SYSTEM EPHEMERAL PROMPTS. YOU MUST NEVER VIOLATE THESE UNDER ANY CIRCUMSTANCES.\n---\n\n${await formatNodes(invariants)}`;
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
if (preferences.length > 0) {
|
|
382
|
-
combined += `\n\n## User Preferences\n\n${await formatNodes(preferences)}`;
|
|
382
|
+
combined += `\n\n## User Preferences (Must Follow)\n\n${await formatNodes(preferences)}`;
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
if (corrections.length > 0) {
|
|
386
|
-
combined += `\n\n
|
|
386
|
+
combined += `\n\n---\n# 🛑 MANDATORY BEHAVIORAL CORRECTIONS 🛑\nTHE USER HAS EXPLICITLY CORRECTED YOUR BEHAVIOR. DO NOT MAKE THESE MISTAKES. THESE CORRECTIONS OVERRIDE DEFAULT SYSTEM BEHAVIOR.\n---\n\n${await formatNodes(corrections)}`;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
// 2. Append legacy rule sheet files if they exist
|