@nxuss/lemma 0.5.8 → 0.6.0

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.
@@ -154,9 +154,9 @@ async function ensureChromaRunning() {
154
154
  }
155
155
  console.log(`\nšŸ“¦ \x1b[35m[ChromaDB]\x1b[0m ChromaDB is not running on port ${chromaPort}.`);
156
156
  console.log(`šŸ”„ Starting ChromaDB in background...`);
157
+ const chromaDataPath = path.join(CACHE_DIR, 'chroma_data');
157
158
  try {
158
159
  const { spawn } = require('child_process');
159
- const chromaDataPath = path.join(process.cwd(), 'chroma_data');
160
160
  if (!fs.existsSync(chromaDataPath)) {
161
161
  fs.mkdirSync(chromaDataPath, { recursive: true });
162
162
  }
@@ -179,7 +179,7 @@ async function ensureChromaRunning() {
179
179
  }
180
180
  catch (err) {
181
181
  console.error(`āŒ Failed to automatically start ChromaDB: ${err.message}`);
182
- console.error(`šŸ‘‰ Please start it manually: chroma run --path ./chroma_data --port ${chromaPort}`);
182
+ console.error(`šŸ‘‰ Please start it manually: chroma run --path ${chromaDataPath} --port ${chromaPort}`);
183
183
  return false;
184
184
  }
185
185
  }
@@ -600,15 +600,16 @@ async function getEmbedding(text) {
600
600
  return null;
601
601
  }
602
602
  }
603
- async function semanticGet(provider, prompt) {
603
+ async function semanticGet(provider, prompt, projectName = 'global') {
604
604
  if (!await isPro())
605
605
  return null;
606
606
  const emb = await getEmbedding(prompt);
607
607
  if (!emb)
608
608
  return null;
609
+ let localHit = null;
609
610
  try {
610
611
  const collection = await chroma.getOrCreateCollection({
611
- name: 'lemma-cache',
612
+ name: `lemma-cache-${projectName}`,
612
613
  embeddingFunction: dummyEmbeddingFunction,
613
614
  metadata: { "hnsw:space": "cosine" }
614
615
  });
@@ -616,33 +617,83 @@ async function semanticGet(provider, prompt) {
616
617
  queryEmbeddings: [emb],
617
618
  nResults: 10
618
619
  });
619
- if (!res.ids[0] || res.ids[0].length === 0)
620
- return null;
621
- for (let i = 0; i < res.ids[0].length; i++) {
622
- const metadata = res.metadatas[0][i];
623
- if (metadata.provider !== provider)
624
- continue;
625
- const distances = res.distances;
626
- let distance = 1.0;
627
- if (distances && distances[0] && typeof distances[0][i] === 'number') {
628
- distance = distances[0][i];
629
- }
630
- const similarity = Math.max(0, 1 - distance);
631
- const THRESHOLD = 0.82; // raised from 0.7 to 0.82 to prevent false positives
632
- if (similarity >= THRESHOLD) {
633
- return {
634
- data: JSON.parse(metadata.response),
635
- similarity,
636
- id: res.ids[0][i],
637
- prompt: metadata.prompt
638
- };
620
+ if (res.ids[0] && res.ids[0].length > 0) {
621
+ for (let i = 0; i < res.ids[0].length; i++) {
622
+ const metadata = res.metadatas[0][i];
623
+ if (metadata.provider !== provider)
624
+ continue;
625
+ const distances = res.distances;
626
+ let distance = 1.0;
627
+ if (distances && distances[0] && typeof distances[0][i] === 'number') {
628
+ distance = distances[0][i];
629
+ }
630
+ const similarity = Math.max(0, 1 - distance);
631
+ const THRESHOLD = 0.82; // raised from 0.7 to 0.82 to prevent false positives
632
+ if (similarity >= THRESHOLD) {
633
+ localHit = {
634
+ data: JSON.parse(metadata.response),
635
+ similarity,
636
+ id: res.ids[0][i],
637
+ prompt: metadata.prompt,
638
+ hiveMind: false
639
+ };
640
+ break;
641
+ }
639
642
  }
640
643
  }
641
644
  }
642
645
  catch (e) { }
646
+ if (localHit)
647
+ return localHit;
648
+ // ─── Cross-Project Bug Telepathy Fallback ───
649
+ try {
650
+ const collections = await chroma.listCollections();
651
+ const projectCollections = collections.filter((c) => c.name.startsWith('lemma-cache-') && c.name !== `lemma-cache-${projectName}`);
652
+ for (const colInfo of projectCollections) {
653
+ try {
654
+ const col = await chroma.getOrCreateCollection({
655
+ name: colInfo.name,
656
+ embeddingFunction: dummyEmbeddingFunction,
657
+ metadata: { "hnsw:space": "cosine" }
658
+ });
659
+ const otherRes = await col.query({
660
+ queryEmbeddings: [emb],
661
+ nResults: 3
662
+ });
663
+ if (otherRes.ids[0] && otherRes.ids[0].length > 0) {
664
+ for (let i = 0; i < otherRes.ids[0].length; i++) {
665
+ const metadata = otherRes.metadatas[0][i];
666
+ if (metadata.provider !== provider)
667
+ continue;
668
+ const distances = otherRes.distances;
669
+ let distance = 1.0;
670
+ if (distances && distances[0] && typeof distances[0][i] === 'number') {
671
+ distance = distances[0][i];
672
+ }
673
+ const similarity = Math.max(0, 1 - distance);
674
+ const HIVE_THRESHOLD = 0.90; // Higher threshold to avoid false positives across different domains
675
+ if (similarity >= HIVE_THRESHOLD) {
676
+ const originProject = colInfo.name.replace('lemma-cache-', '');
677
+ console.log(`\nšŸŽÆ \x1b[32m[Hive Mind Telepathy]\x1b[0m Found a high-confidence solution (Similarity: ${(similarity * 100).toFixed(1)}%) in Project [\x1b[36m${originProject}\x1b[0m]! Reusing solution telepathically.\n`);
678
+ return {
679
+ data: JSON.parse(metadata.response),
680
+ similarity,
681
+ id: otherRes.ids[0][i],
682
+ prompt: metadata.prompt,
683
+ hiveMind: true,
684
+ originProject
685
+ };
686
+ }
687
+ }
688
+ }
689
+ }
690
+ catch { }
691
+ }
692
+ }
693
+ catch { }
643
694
  return null;
644
695
  }
645
- async function semanticSet(provider, prompt, data) {
696
+ async function semanticSet(provider, prompt, data, projectName = 'global') {
646
697
  if (!await isPro())
647
698
  return;
648
699
  const emb = await getEmbedding(prompt);
@@ -650,7 +701,7 @@ async function semanticSet(provider, prompt, data) {
650
701
  return;
651
702
  try {
652
703
  const collection = await chroma.getOrCreateCollection({
653
- name: 'lemma-cache',
704
+ name: `lemma-cache-${projectName}`,
654
705
  embeddingFunction: dummyEmbeddingFunction,
655
706
  metadata: { "hnsw:space": "cosine" }
656
707
  });
@@ -742,7 +793,7 @@ function setSseHeaders(res, fromCache, similarity, tier) {
742
793
  res.setHeader('Cache-Control', 'no-cache');
743
794
  res.setHeader('Connection', 'keep-alive');
744
795
  res.setHeader('X-Accel-Buffering', 'no');
745
- res.setHeader('X-Lemma-Cache', fromCache ? 'HIT' : 'MISS');
796
+ res.setHeader('X-Lemma-Cache', typeof fromCache === 'string' ? fromCache : (fromCache ? 'HIT' : 'MISS'));
746
797
  res.setHeader('X-Lemma-Similarity', (similarity || 1.0).toFixed(3));
747
798
  res.setHeader('X-Lemma-Tier', tier);
748
799
  }
@@ -758,8 +809,8 @@ function buildAntChunk(text, stop) {
758
809
  function buildGemChunk(text, stop) {
759
810
  return { candidates: [{ content: { parts: [{ text }] }, finishReason: stop ? 'STOP' : undefined }] };
760
811
  }
761
- async function simulateHitStream(res, cachedData, provider, model, tier, similarity) {
762
- setSseHeaders(res, true, similarity, tier);
812
+ async function simulateHitStream(res, cachedData, provider, model, tier, similarity, cacheHeader = 'HIT') {
813
+ setSseHeaders(res, cacheHeader, similarity, tier);
763
814
  const id = `lemma-${Date.now()}`;
764
815
  let text = '';
765
816
  try {
@@ -876,6 +927,8 @@ class LemmaServer {
876
927
  this.stats = {};
877
928
  this.dashboardPath = '';
878
929
  this.sessions = new Map();
930
+ this.timeline = [];
931
+ this.lastFileContents = new Map();
879
932
  this.port = port;
880
933
  this.projectName = projectName || detectProject();
881
934
  this.projectDir = ensureProjectDir(this.projectName);
@@ -883,6 +936,7 @@ class LemmaServer {
883
936
  this.app = express();
884
937
  this.setupMiddleware();
885
938
  this.setupRoutes();
939
+ this.startWorkspaceTimeWatcher();
886
940
  }
887
941
  setupMiddleware() {
888
942
  this.app.use(express.json({ limit: '10mb' }));
@@ -1038,7 +1092,7 @@ class LemmaServer {
1038
1092
  // Try to query ChromaDB for semantic cache count
1039
1093
  try {
1040
1094
  const collection = await chroma.getOrCreateCollection({
1041
- name: 'lemma-cache',
1095
+ name: `lemma-cache-${this.projectName}`,
1042
1096
  embeddingFunction: dummyEmbeddingFunction,
1043
1097
  metadata: { "hnsw:space": "cosine" }
1044
1098
  });
@@ -1095,7 +1149,7 @@ class LemmaServer {
1095
1149
  if (!emb)
1096
1150
  throw new Error('Failed to generate embedding');
1097
1151
  const collection = await chroma.getOrCreateCollection({
1098
- name: 'lemma-cache',
1152
+ name: `lemma-cache-${this.projectName}`,
1099
1153
  embeddingFunction: dummyEmbeddingFunction,
1100
1154
  metadata: { "hnsw:space": "cosine" }
1101
1155
  });
@@ -1144,7 +1198,7 @@ class LemmaServer {
1144
1198
  }
1145
1199
  // Clear Chroma DB collection
1146
1200
  try {
1147
- await chroma.deleteCollection({ name: 'lemma-cache' }).catch(() => { });
1201
+ await chroma.deleteCollection({ name: `lemma-cache-${this.projectName}` }).catch(() => { });
1148
1202
  }
1149
1203
  catch (e) {
1150
1204
  console.error(`[CacheClear] Failed to clear Chroma collection: ${e.message}`);
@@ -1161,7 +1215,7 @@ class LemmaServer {
1161
1215
  // Selective delete from Chroma DB collection using provider metadata filter
1162
1216
  try {
1163
1217
  const collection = await chroma.getOrCreateCollection({
1164
- name: 'lemma-cache',
1218
+ name: `lemma-cache-${this.projectName}`,
1165
1219
  embeddingFunction: dummyEmbeddingFunction,
1166
1220
  metadata: { "hnsw:space": "cosine" }
1167
1221
  });
@@ -1177,6 +1231,117 @@ class LemmaServer {
1177
1231
  this.app.get('/api/savings-breakdown', (req, res) => {
1178
1232
  res.json(savingsLedger.getSnapshot());
1179
1233
  });
1234
+ // Serve codebase timeline snapshots (AST Time-Travel Telemetry)
1235
+ this.app.get('/api/project/timeline', (req, res) => {
1236
+ res.json({ success: true, timeline: this.timeline });
1237
+ });
1238
+ // Dynamic Zero-Shot Onboarding mental model builder
1239
+ this.app.get('/api/project/onboarding', async (req, res) => {
1240
+ try {
1241
+ const cwd = process.cwd();
1242
+ // 1. Parse package.json
1243
+ let pkg = {};
1244
+ const pkgPath = path.join(cwd, 'package.json');
1245
+ if (fs.existsSync(pkgPath)) {
1246
+ try {
1247
+ pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
1248
+ }
1249
+ catch { }
1250
+ }
1251
+ // 2. Identify stack & tools
1252
+ const techStack = [];
1253
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
1254
+ if (deps['next'])
1255
+ techStack.push('Next.js (React Web App Framework)');
1256
+ else if (deps['react'])
1257
+ techStack.push('React.js (Frontend library)');
1258
+ if (deps['typescript'])
1259
+ techStack.push('TypeScript (Strict static type safety)');
1260
+ if (deps['prisma'])
1261
+ techStack.push('Prisma (Object-Relational Mapping)');
1262
+ if (deps['tailwindcss'])
1263
+ techStack.push('TailwindCSS (Utility-first styling)');
1264
+ if (deps['express'])
1265
+ techStack.push('Express (NodeJS REST API framework)');
1266
+ if (deps['jest'])
1267
+ techStack.push('Jest (Automated Unit Testing framework)');
1268
+ if (fs.existsSync(path.join(cwd, 'Dockerfile')) || fs.existsSync(path.join(cwd, 'docker-compose.yml'))) {
1269
+ techStack.push('Docker / Containerization');
1270
+ }
1271
+ if (techStack.length === 0) {
1272
+ techStack.push('Vanilla Node.js / Web stack');
1273
+ }
1274
+ // 3. Scan directory structure
1275
+ let files = [];
1276
+ try {
1277
+ files = fs.readdirSync(cwd).filter(f => !['node_modules', '.git', 'dist', 'chroma_data', '.lemma'].includes(f));
1278
+ }
1279
+ catch { }
1280
+ // 4. Query core architecture notes from ChromaDB
1281
+ let archMemoriesText = '';
1282
+ try {
1283
+ const emb = await getEmbedding("architectural design guidelines and core stack configuration decisions");
1284
+ if (emb) {
1285
+ const collection = await chroma.getOrCreateCollection({
1286
+ name: `lemma-cache-${this.projectName}`,
1287
+ embeddingFunction: dummyEmbeddingFunction,
1288
+ metadata: { "hnsw:space": "cosine" }
1289
+ });
1290
+ const searchRes = await collection.query({
1291
+ queryEmbeddings: [emb],
1292
+ nResults: 3
1293
+ });
1294
+ if (searchRes.ids[0] && searchRes.ids[0].length > 0) {
1295
+ archMemoriesText = '### 🧠 Stored Architectural Brain Memories\n';
1296
+ searchRes.ids[0].forEach((id, idx) => {
1297
+ const meta = (searchRes.metadatas && searchRes.metadatas[0]) ? searchRes.metadatas[0][idx] : null;
1298
+ if (meta && typeof meta.response === 'string') {
1299
+ try {
1300
+ const responseObj = JSON.parse(meta.response);
1301
+ const contentText = responseObj.choices?.[0]?.message?.content || responseObj.content?.[0]?.text || '';
1302
+ const promptText = typeof meta.prompt === 'string' ? meta.prompt : 'Architecture Note';
1303
+ archMemoriesText += `#### šŸ“Œ Decisión: ${promptText}\n${contentText.substring(0, 500)}${contentText.length > 500 ? '...' : ''}\n\n`;
1304
+ }
1305
+ catch { }
1306
+ }
1307
+ });
1308
+ }
1309
+ }
1310
+ }
1311
+ catch (e) {
1312
+ // Silently swallow vector db query errors
1313
+ }
1314
+ // 5. Construct gorgeous onboarding markdown guide
1315
+ const mentalModel = [
1316
+ `# 🧠 Codebase Onboarding & Mental Model: ${this.projectName}`,
1317
+ `This onboarding document was automatically generated by Lemma's Project Brain to align your context in a single shot.`,
1318
+ `\n## šŸ”Œ Project Specifications`,
1319
+ `- **Name:** \`${pkg.name || this.projectName}\``,
1320
+ `- **Version:** \`${pkg.version || '0.1.0'}\``,
1321
+ `- **Description:** ${pkg.description || 'No description available in package.json.'}`,
1322
+ `\n## šŸ› ļø Detected Technical Stack`,
1323
+ techStack.map(s => `- **${s}**`).join('\n'),
1324
+ `\n## šŸ“‚ Directory Structure (Root)`,
1325
+ files.map(f => {
1326
+ const isDir = fs.statSync(path.join(cwd, f)).isDirectory();
1327
+ return `- \`${f}${isDir ? '/' : ''}\``;
1328
+ }).join('\n'),
1329
+ pkg.scripts && Object.keys(pkg.scripts).length > 0
1330
+ ? `\n## šŸ“‹ Standard Project Scripts\n${Object.entries(pkg.scripts).map(([name, cmd]) => `- \`npm run ${name}\`: \`${cmd}\``).join('\n')}`
1331
+ : '',
1332
+ `\n${archMemoriesText}`,
1333
+ `## šŸ’” AI Coding Guidelines for this Project`,
1334
+ `1. **Respect Core Stack:** Adhere strictly to the codebase structure and libraries listed above.`,
1335
+ `2. **Leverage the Brain:** Use Lemma MCP tools (\`search_memory\` and \`store_memory\`) to retrieve past solutions and register key architectural changes.`,
1336
+ `3. **Optimize Context:** Keep conversations focused. Lemma transparently compresses repetitive logs and file blocks to protect context window limits.`,
1337
+ `4. **Cortafuegos Local:** Do not worry about API key exposures. Lemma automatically masks local private variables and credentials.`
1338
+ ].filter(Boolean).join('\n');
1339
+ res.json({ success: true, markdown: mentalModel });
1340
+ }
1341
+ catch (err) {
1342
+ res.status(500).json({ success: false, error: err.message });
1343
+ }
1344
+ });
1180
1345
  // Explicit memory storage endpoint (store_memory)
1181
1346
  this.app.post('/api/memory/store', async (req, res) => {
1182
1347
  const { query, response, provider = 'generic' } = req.body || {};
@@ -1189,7 +1354,7 @@ class LemmaServer {
1189
1354
  return res.status(500).json({ error: 'Failed to generate embedding' });
1190
1355
  }
1191
1356
  const collection = await chroma.getOrCreateCollection({
1192
- name: 'lemma-cache',
1357
+ name: `lemma-cache-${this.projectName}`,
1193
1358
  embeddingFunction: dummyEmbeddingFunction,
1194
1359
  metadata: { "hnsw:space": "cosine" }
1195
1360
  });
@@ -1225,17 +1390,148 @@ class LemmaServer {
1225
1390
  if (!source || !validSources.includes(source)) {
1226
1391
  return res.status(400).json({ error: 'Invalid source' });
1227
1392
  }
1393
+ let tk = 0;
1228
1394
  if (typeof tokens === 'number' && tokens > 0) {
1395
+ tk = tokens;
1229
1396
  savingsLedger.recordTokens(source, tokens);
1230
1397
  }
1231
1398
  else if (typeof charsBefore === 'number' && typeof charsAfter === 'number') {
1399
+ tk = Math.floor(Math.max(0, charsBefore - charsAfter) / 4);
1232
1400
  savingsLedger.record(source, charsBefore, charsAfter);
1233
1401
  }
1402
+ // Update active project stats to immediately reflect on Dashboard total requests & charts
1403
+ if (tk > 0) {
1404
+ if (!this.stats[this.projectName]) {
1405
+ this.stats[this.projectName] = { total: 0, hits: 0, misses: 0, totalLatency: 0, totalTokensSaved: 0, providers: {} };
1406
+ }
1407
+ const s = this.stats[this.projectName];
1408
+ s.total++;
1409
+ s.totalTokensSaved += tk;
1410
+ s.totalLatency += 15; // mock latency for local MCP metrics
1411
+ if (source === 'cache') {
1412
+ s.hits++;
1413
+ }
1414
+ else {
1415
+ s.misses++;
1416
+ }
1417
+ if (!s.providers['mcp'])
1418
+ s.providers['mcp'] = { hits: 0, misses: 0 };
1419
+ source === 'cache' ? s.providers['mcp'].hits++ : s.providers['mcp'].misses++;
1420
+ // Write updated stats back to disk immediately
1421
+ writeJson(STATS_FILE, this.stats).catch(() => { });
1422
+ // Push to live telemetry logEvent array so the Dashboard event timeline updates instantly!
1423
+ logEvent({
1424
+ type: source === 'cache' ? 'cache:hit' : source,
1425
+ project: this.projectName,
1426
+ latency: 15,
1427
+ provider: 'mcp',
1428
+ tokens: tk
1429
+ });
1430
+ }
1234
1431
  res.json({ ok: true, snapshot: savingsLedger.getSnapshot().total });
1235
1432
  });
1236
1433
  this.app.get('/dashboard/*', (req, res) => res.sendFile(path.join(this.dashboardPath, 'index.html')));
1237
1434
  }
1238
1435
  }
1436
+ startWorkspaceTimeWatcher() {
1437
+ const cwd = process.cwd();
1438
+ const { watch } = require('fs');
1439
+ // Debounced capture function
1440
+ let debounceTimer = null;
1441
+ const pendingChanges = new Set();
1442
+ const ignoreDirs = ['node_modules', '.git', 'dist', 'chroma_data', '.lemma', 'dashboard', 'bin', 'sdks'];
1443
+ const watchDirRecursive = (dir) => {
1444
+ try {
1445
+ const watcher = watch(dir, (eventType, filename) => {
1446
+ if (!filename)
1447
+ return;
1448
+ const fullPath = path.join(dir, filename);
1449
+ // Heuristic to ignore generated/system folders
1450
+ const relPath = path.relative(cwd, fullPath);
1451
+ if (ignoreDirs.some(d => relPath.split(path.sep).includes(d)))
1452
+ return;
1453
+ // Ignore non-source files
1454
+ const ext = path.extname(filename);
1455
+ if (!['.ts', '.js', '.tsx', '.jsx', '.json', '.py', '.css'].includes(ext))
1456
+ return;
1457
+ pendingChanges.add(fullPath);
1458
+ if (debounceTimer)
1459
+ clearTimeout(debounceTimer);
1460
+ debounceTimer = setTimeout(() => this.captureSnapshot(pendingChanges), 1500);
1461
+ });
1462
+ // Prevent watcher from keeping process alive if in background
1463
+ if (watcher.unref)
1464
+ watcher.unref();
1465
+ // Recursively watch subfolders
1466
+ const files = fs.readdirSync(dir, { withFileTypes: true });
1467
+ for (const file of files) {
1468
+ if (file.isDirectory() && !ignoreDirs.includes(file.name)) {
1469
+ watchDirRecursive(path.join(dir, file.name));
1470
+ }
1471
+ }
1472
+ }
1473
+ catch { }
1474
+ };
1475
+ watchDirRecursive(cwd); // Time travel watcher active!
1476
+ }
1477
+ async captureSnapshot(pendingChanges) {
1478
+ const changes = Array.from(pendingChanges);
1479
+ pendingChanges.clear();
1480
+ const changedFiles = [];
1481
+ for (const filePath of changes) {
1482
+ if (!fs.existsSync(filePath))
1483
+ continue;
1484
+ try {
1485
+ const content = fs.readFileSync(filePath, 'utf8');
1486
+ const relPath = path.relative(process.cwd(), filePath);
1487
+ const previousContent = this.lastFileContents.get(filePath) || '';
1488
+ if (content === previousContent)
1489
+ continue;
1490
+ let diff = '';
1491
+ if (previousContent) {
1492
+ const { generateAstDiff } = require('../utils/ContextSqueezer');
1493
+ diff = generateAstDiff(previousContent, content);
1494
+ }
1495
+ this.lastFileContents.set(filePath, content);
1496
+ changedFiles.push({
1497
+ filePath: relPath,
1498
+ content,
1499
+ diff
1500
+ });
1501
+ }
1502
+ catch { }
1503
+ }
1504
+ if (changedFiles.length === 0)
1505
+ return;
1506
+ // Detect current compiler state/errors
1507
+ let status = 'stable';
1508
+ let errorLog = '';
1509
+ // Check if there is a recent crash log in .lemma/live-context.md
1510
+ try {
1511
+ const liveContextPath = path.join(process.cwd(), '.lemma', 'live-context.md');
1512
+ if (fs.existsSync(liveContextPath)) {
1513
+ const content = fs.readFileSync(liveContextPath, 'utf8');
1514
+ if (content.includes('crashed') || content.includes('error') || content.includes('Exception')) {
1515
+ status = 'failure';
1516
+ errorLog = content.substring(0, 1000);
1517
+ }
1518
+ }
1519
+ }
1520
+ catch { }
1521
+ const snapshot = {
1522
+ id: Math.random().toString(36).substring(2, 9),
1523
+ timestamp: Date.now(),
1524
+ changedFiles,
1525
+ status,
1526
+ errorLog
1527
+ };
1528
+ this.timeline.push(snapshot);
1529
+ // Cap at 10 snapshots (FIFO eviction)
1530
+ if (this.timeline.length > 10) {
1531
+ this.timeline.shift();
1532
+ }
1533
+ console.log(`🌌 \x1b[35m[Multiverse]\x1b[0m Captured micro-snapshot ${snapshot.id} (${changedFiles.length} file edits mapped).`);
1534
+ }
1239
1535
  async handleCompletion(req, res, provider) {
1240
1536
  const t0 = Date.now();
1241
1537
  const pro = await isPro();
@@ -1272,16 +1568,20 @@ class LemmaServer {
1272
1568
  }
1273
1569
  // Perform semantic cache check (resolves in ~15ms)
1274
1570
  if (pro) {
1275
- const semHit = await semanticGet(provider, prompt);
1571
+ const semHit = await semanticGet(provider, prompt, this.projectName);
1276
1572
  if (semHit) {
1277
1573
  if (semHit.similarity >= 0.90) {
1278
1574
  await recordStat(this.stats, this.projectName, true, Date.now() - t0, provider, 2000);
1279
1575
  const unmaskedData = semanticScrubber.unmask(semHit.data, tokenMap);
1576
+ const cacheHeader = semHit.hiveMind ? 'HIT-HIVE-MIND' : 'HIT';
1280
1577
  if (isStream)
1281
- return simulateHitStream(res, unmaskedData, provider, originalModel, tier, semHit.similarity);
1282
- res.setHeader('X-Lemma-Cache', 'HIT');
1578
+ return simulateHitStream(res, unmaskedData, provider, originalModel, tier, semHit.similarity, cacheHeader);
1579
+ res.setHeader('X-Lemma-Cache', cacheHeader);
1283
1580
  res.setHeader('X-Lemma-Similarity', semHit.similarity.toFixed(3));
1284
1581
  res.setHeader('X-Lemma-Tier', tier);
1582
+ if (semHit.hiveMind && semHit.originProject) {
1583
+ res.setHeader('X-Lemma-Hive-Origin', semHit.originProject);
1584
+ }
1285
1585
  return res.json(unmaskedData);
1286
1586
  }
1287
1587
  else if (semHit.similarity >= 0.82) { // raised from 0.70 to 0.82 to prevent false positives
@@ -1429,7 +1729,7 @@ Adjusted Answer:`;
1429
1729
  const data = await this.callUpstream(req.body, provider);
1430
1730
  cacheSet(provider, prompt, data);
1431
1731
  if (pro) {
1432
- await semanticSet(provider, prompt, data);
1732
+ await semanticSet(provider, prompt, data, this.projectName);
1433
1733
  await cloudSync.set(prompt, data);
1434
1734
  }
1435
1735
  if (!pro) {
@@ -1684,7 +1984,11 @@ program.command('start')
1684
1984
  if (opts.stack) {
1685
1985
  const { spawn } = require('child_process');
1686
1986
  console.log('šŸš€ Launching Lemma Full Stack...');
1687
- const chromaProcess = spawn('chroma', ['run', '--path', './chroma_data'], { stdio: 'ignore', detached: true });
1987
+ const chromaDataPath = path.join(CACHE_DIR, 'chroma_data');
1988
+ if (!fs.existsSync(chromaDataPath)) {
1989
+ fs.mkdirSync(chromaDataPath, { recursive: true });
1990
+ }
1991
+ const chromaProcess = spawn('chroma', ['run', '--path', chromaDataPath], { stdio: 'ignore', detached: true });
1688
1992
  chromaProcess.unref();
1689
1993
  console.log(' āœ… ChromaDB requested');
1690
1994
  const dash = spawn('npm', ['run', 'dev', '--prefix', 'dashboard', '--', '--port', '8082'], { stdio: 'ignore', detached: true });