@nxuss/lemma 0.5.9 → 0.6.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/README.md +20 -6
- package/dashboard/dist/assets/{index-0N21ZtpO.js → index-BJzMIGrT.js} +2 -2
- package/dashboard/dist/assets/{index-0N21ZtpO.js.map → index-BJzMIGrT.js.map} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +311 -39
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/mcp/index.js +276 -0
- package/dist/cjs/mcp/index.js.map +1 -1
- package/dist/cjs/utils/ContextSqueezer.d.ts +4 -0
- package/dist/cjs/utils/ContextSqueezer.d.ts.map +1 -1
- package/dist/cjs/utils/ContextSqueezer.js +35 -1
- package/dist/cjs/utils/ContextSqueezer.js.map +1 -1
- package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +311 -39
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/mcp/index.js +276 -0
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/utils/ContextSqueezer.d.ts +4 -0
- package/dist/esm/utils/ContextSqueezer.d.ts.map +1 -1
- package/dist/esm/utils/ContextSqueezer.js +34 -1
- package/dist/esm/utils/ContextSqueezer.js.map +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>lemma Dashboard - Agent Orchestration Hub</title>
|
|
8
|
-
<script type="module" crossorigin src="/dashboard/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/dashboard/assets/index-BJzMIGrT.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/dashboard/assets/index-DUOrThix.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lemma-proxy.d.ts","sourceRoot":"","sources":["../../../src/cli/lemma-proxy.ts"],"names":[],"mappings":";AA8NA,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAkMnJ;AAED,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA4DvF;
|
|
1
|
+
{"version":3,"file":"lemma-proxy.d.ts","sourceRoot":"","sources":["../../../src/cli/lemma-proxy.ts"],"names":[],"mappings":";AA8NA,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAkMnJ;AAED,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA4DvF;AAm2DD,wBAAsB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA6KhL"}
|
|
@@ -162,9 +162,9 @@ async function ensureChromaRunning() {
|
|
|
162
162
|
}
|
|
163
163
|
console.log(`\n📦 \x1b[35m[ChromaDB]\x1b[0m ChromaDB is not running on port ${chromaPort}.`);
|
|
164
164
|
console.log(`🔄 Starting ChromaDB in background...`);
|
|
165
|
+
const chromaDataPath = path_1.default.join(CACHE_DIR, 'chroma_data');
|
|
165
166
|
try {
|
|
166
167
|
const { spawn } = require('child_process');
|
|
167
|
-
const chromaDataPath = path_1.default.join(process.cwd(), 'chroma_data');
|
|
168
168
|
if (!fs_1.default.existsSync(chromaDataPath)) {
|
|
169
169
|
fs_1.default.mkdirSync(chromaDataPath, { recursive: true });
|
|
170
170
|
}
|
|
@@ -187,7 +187,7 @@ async function ensureChromaRunning() {
|
|
|
187
187
|
}
|
|
188
188
|
catch (err) {
|
|
189
189
|
console.error(`❌ Failed to automatically start ChromaDB: ${err.message}`);
|
|
190
|
-
console.error(`👉 Please start it manually: chroma run --path
|
|
190
|
+
console.error(`👉 Please start it manually: chroma run --path ${chromaDataPath} --port ${chromaPort}`);
|
|
191
191
|
return false;
|
|
192
192
|
}
|
|
193
193
|
}
|
|
@@ -608,15 +608,16 @@ async function getEmbedding(text) {
|
|
|
608
608
|
return null;
|
|
609
609
|
}
|
|
610
610
|
}
|
|
611
|
-
async function semanticGet(provider, prompt) {
|
|
611
|
+
async function semanticGet(provider, prompt, projectName = 'global') {
|
|
612
612
|
if (!await isPro())
|
|
613
613
|
return null;
|
|
614
614
|
const emb = await getEmbedding(prompt);
|
|
615
615
|
if (!emb)
|
|
616
616
|
return null;
|
|
617
|
+
let localHit = null;
|
|
617
618
|
try {
|
|
618
619
|
const collection = await chroma.getOrCreateCollection({
|
|
619
|
-
name:
|
|
620
|
+
name: `lemma-cache-${projectName}`,
|
|
620
621
|
embeddingFunction: dummyEmbeddingFunction,
|
|
621
622
|
metadata: { "hnsw:space": "cosine" }
|
|
622
623
|
});
|
|
@@ -624,33 +625,83 @@ async function semanticGet(provider, prompt) {
|
|
|
624
625
|
queryEmbeddings: [emb],
|
|
625
626
|
nResults: 10
|
|
626
627
|
});
|
|
627
|
-
if (
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
628
|
+
if (res.ids[0] && res.ids[0].length > 0) {
|
|
629
|
+
for (let i = 0; i < res.ids[0].length; i++) {
|
|
630
|
+
const metadata = res.metadatas[0][i];
|
|
631
|
+
if (metadata.provider !== provider)
|
|
632
|
+
continue;
|
|
633
|
+
const distances = res.distances;
|
|
634
|
+
let distance = 1.0;
|
|
635
|
+
if (distances && distances[0] && typeof distances[0][i] === 'number') {
|
|
636
|
+
distance = distances[0][i];
|
|
637
|
+
}
|
|
638
|
+
const similarity = Math.max(0, 1 - distance);
|
|
639
|
+
const THRESHOLD = 0.82; // raised from 0.7 to 0.82 to prevent false positives
|
|
640
|
+
if (similarity >= THRESHOLD) {
|
|
641
|
+
localHit = {
|
|
642
|
+
data: JSON.parse(metadata.response),
|
|
643
|
+
similarity,
|
|
644
|
+
id: res.ids[0][i],
|
|
645
|
+
prompt: metadata.prompt,
|
|
646
|
+
hiveMind: false
|
|
647
|
+
};
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
647
650
|
}
|
|
648
651
|
}
|
|
649
652
|
}
|
|
650
653
|
catch (e) { }
|
|
654
|
+
if (localHit)
|
|
655
|
+
return localHit;
|
|
656
|
+
// ─── Cross-Project Bug Telepathy Fallback ───
|
|
657
|
+
try {
|
|
658
|
+
const collections = await chroma.listCollections();
|
|
659
|
+
const projectCollections = collections.filter((c) => c.name.startsWith('lemma-cache-') && c.name !== `lemma-cache-${projectName}`);
|
|
660
|
+
for (const colInfo of projectCollections) {
|
|
661
|
+
try {
|
|
662
|
+
const col = await chroma.getOrCreateCollection({
|
|
663
|
+
name: colInfo.name,
|
|
664
|
+
embeddingFunction: dummyEmbeddingFunction,
|
|
665
|
+
metadata: { "hnsw:space": "cosine" }
|
|
666
|
+
});
|
|
667
|
+
const otherRes = await col.query({
|
|
668
|
+
queryEmbeddings: [emb],
|
|
669
|
+
nResults: 3
|
|
670
|
+
});
|
|
671
|
+
if (otherRes.ids[0] && otherRes.ids[0].length > 0) {
|
|
672
|
+
for (let i = 0; i < otherRes.ids[0].length; i++) {
|
|
673
|
+
const metadata = otherRes.metadatas[0][i];
|
|
674
|
+
if (metadata.provider !== provider)
|
|
675
|
+
continue;
|
|
676
|
+
const distances = otherRes.distances;
|
|
677
|
+
let distance = 1.0;
|
|
678
|
+
if (distances && distances[0] && typeof distances[0][i] === 'number') {
|
|
679
|
+
distance = distances[0][i];
|
|
680
|
+
}
|
|
681
|
+
const similarity = Math.max(0, 1 - distance);
|
|
682
|
+
const HIVE_THRESHOLD = 0.90; // Higher threshold to avoid false positives across different domains
|
|
683
|
+
if (similarity >= HIVE_THRESHOLD) {
|
|
684
|
+
const originProject = colInfo.name.replace('lemma-cache-', '');
|
|
685
|
+
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`);
|
|
686
|
+
return {
|
|
687
|
+
data: JSON.parse(metadata.response),
|
|
688
|
+
similarity,
|
|
689
|
+
id: otherRes.ids[0][i],
|
|
690
|
+
prompt: metadata.prompt,
|
|
691
|
+
hiveMind: true,
|
|
692
|
+
originProject
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
catch { }
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
catch { }
|
|
651
702
|
return null;
|
|
652
703
|
}
|
|
653
|
-
async function semanticSet(provider, prompt, data) {
|
|
704
|
+
async function semanticSet(provider, prompt, data, projectName = 'global') {
|
|
654
705
|
if (!await isPro())
|
|
655
706
|
return;
|
|
656
707
|
const emb = await getEmbedding(prompt);
|
|
@@ -658,7 +709,7 @@ async function semanticSet(provider, prompt, data) {
|
|
|
658
709
|
return;
|
|
659
710
|
try {
|
|
660
711
|
const collection = await chroma.getOrCreateCollection({
|
|
661
|
-
name:
|
|
712
|
+
name: `lemma-cache-${projectName}`,
|
|
662
713
|
embeddingFunction: dummyEmbeddingFunction,
|
|
663
714
|
metadata: { "hnsw:space": "cosine" }
|
|
664
715
|
});
|
|
@@ -750,7 +801,7 @@ function setSseHeaders(res, fromCache, similarity, tier) {
|
|
|
750
801
|
res.setHeader('Cache-Control', 'no-cache');
|
|
751
802
|
res.setHeader('Connection', 'keep-alive');
|
|
752
803
|
res.setHeader('X-Accel-Buffering', 'no');
|
|
753
|
-
res.setHeader('X-Lemma-Cache', fromCache ? 'HIT' : 'MISS');
|
|
804
|
+
res.setHeader('X-Lemma-Cache', typeof fromCache === 'string' ? fromCache : (fromCache ? 'HIT' : 'MISS'));
|
|
754
805
|
res.setHeader('X-Lemma-Similarity', (similarity || 1.0).toFixed(3));
|
|
755
806
|
res.setHeader('X-Lemma-Tier', tier);
|
|
756
807
|
}
|
|
@@ -766,8 +817,8 @@ function buildAntChunk(text, stop) {
|
|
|
766
817
|
function buildGemChunk(text, stop) {
|
|
767
818
|
return { candidates: [{ content: { parts: [{ text }] }, finishReason: stop ? 'STOP' : undefined }] };
|
|
768
819
|
}
|
|
769
|
-
async function simulateHitStream(res, cachedData, provider, model, tier, similarity) {
|
|
770
|
-
setSseHeaders(res,
|
|
820
|
+
async function simulateHitStream(res, cachedData, provider, model, tier, similarity, cacheHeader = 'HIT') {
|
|
821
|
+
setSseHeaders(res, cacheHeader, similarity, tier);
|
|
771
822
|
const id = `lemma-${Date.now()}`;
|
|
772
823
|
let text = '';
|
|
773
824
|
try {
|
|
@@ -884,6 +935,8 @@ class LemmaServer {
|
|
|
884
935
|
this.stats = {};
|
|
885
936
|
this.dashboardPath = '';
|
|
886
937
|
this.sessions = new Map();
|
|
938
|
+
this.timeline = [];
|
|
939
|
+
this.lastFileContents = new Map();
|
|
887
940
|
this.port = port;
|
|
888
941
|
this.projectName = projectName || detectProject();
|
|
889
942
|
this.projectDir = ensureProjectDir(this.projectName);
|
|
@@ -891,6 +944,7 @@ class LemmaServer {
|
|
|
891
944
|
this.app = (0, express_1.default)();
|
|
892
945
|
this.setupMiddleware();
|
|
893
946
|
this.setupRoutes();
|
|
947
|
+
this.startWorkspaceTimeWatcher();
|
|
894
948
|
}
|
|
895
949
|
setupMiddleware() {
|
|
896
950
|
this.app.use(express_1.default.json({ limit: '10mb' }));
|
|
@@ -1046,7 +1100,7 @@ class LemmaServer {
|
|
|
1046
1100
|
// Try to query ChromaDB for semantic cache count
|
|
1047
1101
|
try {
|
|
1048
1102
|
const collection = await chroma.getOrCreateCollection({
|
|
1049
|
-
name:
|
|
1103
|
+
name: `lemma-cache-${this.projectName}`,
|
|
1050
1104
|
embeddingFunction: dummyEmbeddingFunction,
|
|
1051
1105
|
metadata: { "hnsw:space": "cosine" }
|
|
1052
1106
|
});
|
|
@@ -1103,7 +1157,7 @@ class LemmaServer {
|
|
|
1103
1157
|
if (!emb)
|
|
1104
1158
|
throw new Error('Failed to generate embedding');
|
|
1105
1159
|
const collection = await chroma.getOrCreateCollection({
|
|
1106
|
-
name:
|
|
1160
|
+
name: `lemma-cache-${this.projectName}`,
|
|
1107
1161
|
embeddingFunction: dummyEmbeddingFunction,
|
|
1108
1162
|
metadata: { "hnsw:space": "cosine" }
|
|
1109
1163
|
});
|
|
@@ -1152,7 +1206,7 @@ class LemmaServer {
|
|
|
1152
1206
|
}
|
|
1153
1207
|
// Clear Chroma DB collection
|
|
1154
1208
|
try {
|
|
1155
|
-
await chroma.deleteCollection({ name:
|
|
1209
|
+
await chroma.deleteCollection({ name: `lemma-cache-${this.projectName}` }).catch(() => { });
|
|
1156
1210
|
}
|
|
1157
1211
|
catch (e) {
|
|
1158
1212
|
console.error(`[CacheClear] Failed to clear Chroma collection: ${e.message}`);
|
|
@@ -1169,7 +1223,7 @@ class LemmaServer {
|
|
|
1169
1223
|
// Selective delete from Chroma DB collection using provider metadata filter
|
|
1170
1224
|
try {
|
|
1171
1225
|
const collection = await chroma.getOrCreateCollection({
|
|
1172
|
-
name:
|
|
1226
|
+
name: `lemma-cache-${this.projectName}`,
|
|
1173
1227
|
embeddingFunction: dummyEmbeddingFunction,
|
|
1174
1228
|
metadata: { "hnsw:space": "cosine" }
|
|
1175
1229
|
});
|
|
@@ -1185,6 +1239,117 @@ class LemmaServer {
|
|
|
1185
1239
|
this.app.get('/api/savings-breakdown', (req, res) => {
|
|
1186
1240
|
res.json(SavingsLedger_1.savingsLedger.getSnapshot());
|
|
1187
1241
|
});
|
|
1242
|
+
// Serve codebase timeline snapshots (AST Time-Travel Telemetry)
|
|
1243
|
+
this.app.get('/api/project/timeline', (req, res) => {
|
|
1244
|
+
res.json({ success: true, timeline: this.timeline });
|
|
1245
|
+
});
|
|
1246
|
+
// Dynamic Zero-Shot Onboarding mental model builder
|
|
1247
|
+
this.app.get('/api/project/onboarding', async (req, res) => {
|
|
1248
|
+
try {
|
|
1249
|
+
const cwd = process.cwd();
|
|
1250
|
+
// 1. Parse package.json
|
|
1251
|
+
let pkg = {};
|
|
1252
|
+
const pkgPath = path_1.default.join(cwd, 'package.json');
|
|
1253
|
+
if (fs_1.default.existsSync(pkgPath)) {
|
|
1254
|
+
try {
|
|
1255
|
+
pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
|
|
1256
|
+
}
|
|
1257
|
+
catch { }
|
|
1258
|
+
}
|
|
1259
|
+
// 2. Identify stack & tools
|
|
1260
|
+
const techStack = [];
|
|
1261
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
1262
|
+
if (deps['next'])
|
|
1263
|
+
techStack.push('Next.js (React Web App Framework)');
|
|
1264
|
+
else if (deps['react'])
|
|
1265
|
+
techStack.push('React.js (Frontend library)');
|
|
1266
|
+
if (deps['typescript'])
|
|
1267
|
+
techStack.push('TypeScript (Strict static type safety)');
|
|
1268
|
+
if (deps['prisma'])
|
|
1269
|
+
techStack.push('Prisma (Object-Relational Mapping)');
|
|
1270
|
+
if (deps['tailwindcss'])
|
|
1271
|
+
techStack.push('TailwindCSS (Utility-first styling)');
|
|
1272
|
+
if (deps['express'])
|
|
1273
|
+
techStack.push('Express (NodeJS REST API framework)');
|
|
1274
|
+
if (deps['jest'])
|
|
1275
|
+
techStack.push('Jest (Automated Unit Testing framework)');
|
|
1276
|
+
if (fs_1.default.existsSync(path_1.default.join(cwd, 'Dockerfile')) || fs_1.default.existsSync(path_1.default.join(cwd, 'docker-compose.yml'))) {
|
|
1277
|
+
techStack.push('Docker / Containerization');
|
|
1278
|
+
}
|
|
1279
|
+
if (techStack.length === 0) {
|
|
1280
|
+
techStack.push('Vanilla Node.js / Web stack');
|
|
1281
|
+
}
|
|
1282
|
+
// 3. Scan directory structure
|
|
1283
|
+
let files = [];
|
|
1284
|
+
try {
|
|
1285
|
+
files = fs_1.default.readdirSync(cwd).filter(f => !['node_modules', '.git', 'dist', 'chroma_data', '.lemma'].includes(f));
|
|
1286
|
+
}
|
|
1287
|
+
catch { }
|
|
1288
|
+
// 4. Query core architecture notes from ChromaDB
|
|
1289
|
+
let archMemoriesText = '';
|
|
1290
|
+
try {
|
|
1291
|
+
const emb = await getEmbedding("architectural design guidelines and core stack configuration decisions");
|
|
1292
|
+
if (emb) {
|
|
1293
|
+
const collection = await chroma.getOrCreateCollection({
|
|
1294
|
+
name: `lemma-cache-${this.projectName}`,
|
|
1295
|
+
embeddingFunction: dummyEmbeddingFunction,
|
|
1296
|
+
metadata: { "hnsw:space": "cosine" }
|
|
1297
|
+
});
|
|
1298
|
+
const searchRes = await collection.query({
|
|
1299
|
+
queryEmbeddings: [emb],
|
|
1300
|
+
nResults: 3
|
|
1301
|
+
});
|
|
1302
|
+
if (searchRes.ids[0] && searchRes.ids[0].length > 0) {
|
|
1303
|
+
archMemoriesText = '### 🧠 Stored Architectural Brain Memories\n';
|
|
1304
|
+
searchRes.ids[0].forEach((id, idx) => {
|
|
1305
|
+
const meta = (searchRes.metadatas && searchRes.metadatas[0]) ? searchRes.metadatas[0][idx] : null;
|
|
1306
|
+
if (meta && typeof meta.response === 'string') {
|
|
1307
|
+
try {
|
|
1308
|
+
const responseObj = JSON.parse(meta.response);
|
|
1309
|
+
const contentText = responseObj.choices?.[0]?.message?.content || responseObj.content?.[0]?.text || '';
|
|
1310
|
+
const promptText = typeof meta.prompt === 'string' ? meta.prompt : 'Architecture Note';
|
|
1311
|
+
archMemoriesText += `#### 📌 Decisión: ${promptText}\n${contentText.substring(0, 500)}${contentText.length > 500 ? '...' : ''}\n\n`;
|
|
1312
|
+
}
|
|
1313
|
+
catch { }
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
catch (e) {
|
|
1320
|
+
// Silently swallow vector db query errors
|
|
1321
|
+
}
|
|
1322
|
+
// 5. Construct gorgeous onboarding markdown guide
|
|
1323
|
+
const mentalModel = [
|
|
1324
|
+
`# 🧠 Codebase Onboarding & Mental Model: ${this.projectName}`,
|
|
1325
|
+
`This onboarding document was automatically generated by Lemma's Project Brain to align your context in a single shot.`,
|
|
1326
|
+
`\n## 🔌 Project Specifications`,
|
|
1327
|
+
`- **Name:** \`${pkg.name || this.projectName}\``,
|
|
1328
|
+
`- **Version:** \`${pkg.version || '0.1.0'}\``,
|
|
1329
|
+
`- **Description:** ${pkg.description || 'No description available in package.json.'}`,
|
|
1330
|
+
`\n## 🛠️ Detected Technical Stack`,
|
|
1331
|
+
techStack.map(s => `- **${s}**`).join('\n'),
|
|
1332
|
+
`\n## 📂 Directory Structure (Root)`,
|
|
1333
|
+
files.map(f => {
|
|
1334
|
+
const isDir = fs_1.default.statSync(path_1.default.join(cwd, f)).isDirectory();
|
|
1335
|
+
return `- \`${f}${isDir ? '/' : ''}\``;
|
|
1336
|
+
}).join('\n'),
|
|
1337
|
+
pkg.scripts && Object.keys(pkg.scripts).length > 0
|
|
1338
|
+
? `\n## 📋 Standard Project Scripts\n${Object.entries(pkg.scripts).map(([name, cmd]) => `- \`npm run ${name}\`: \`${cmd}\``).join('\n')}`
|
|
1339
|
+
: '',
|
|
1340
|
+
`\n${archMemoriesText}`,
|
|
1341
|
+
`## 💡 AI Coding Guidelines for this Project`,
|
|
1342
|
+
`1. **Respect Core Stack:** Adhere strictly to the codebase structure and libraries listed above.`,
|
|
1343
|
+
`2. **Leverage the Brain:** Use Lemma MCP tools (\`search_memory\` and \`store_memory\`) to retrieve past solutions and register key architectural changes.`,
|
|
1344
|
+
`3. **Optimize Context:** Keep conversations focused. Lemma transparently compresses repetitive logs and file blocks to protect context window limits.`,
|
|
1345
|
+
`4. **Cortafuegos Local:** Do not worry about API key exposures. Lemma automatically masks local private variables and credentials.`
|
|
1346
|
+
].filter(Boolean).join('\n');
|
|
1347
|
+
res.json({ success: true, markdown: mentalModel });
|
|
1348
|
+
}
|
|
1349
|
+
catch (err) {
|
|
1350
|
+
res.status(500).json({ success: false, error: err.message });
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1188
1353
|
// Explicit memory storage endpoint (store_memory)
|
|
1189
1354
|
this.app.post('/api/memory/store', async (req, res) => {
|
|
1190
1355
|
const { query, response, provider = 'generic' } = req.body || {};
|
|
@@ -1197,7 +1362,7 @@ class LemmaServer {
|
|
|
1197
1362
|
return res.status(500).json({ error: 'Failed to generate embedding' });
|
|
1198
1363
|
}
|
|
1199
1364
|
const collection = await chroma.getOrCreateCollection({
|
|
1200
|
-
name:
|
|
1365
|
+
name: `lemma-cache-${this.projectName}`,
|
|
1201
1366
|
embeddingFunction: dummyEmbeddingFunction,
|
|
1202
1367
|
metadata: { "hnsw:space": "cosine" }
|
|
1203
1368
|
});
|
|
@@ -1276,6 +1441,105 @@ class LemmaServer {
|
|
|
1276
1441
|
this.app.get('/dashboard/*', (req, res) => res.sendFile(path_1.default.join(this.dashboardPath, 'index.html')));
|
|
1277
1442
|
}
|
|
1278
1443
|
}
|
|
1444
|
+
startWorkspaceTimeWatcher() {
|
|
1445
|
+
const cwd = process.cwd();
|
|
1446
|
+
const { watch } = require('fs');
|
|
1447
|
+
// Debounced capture function
|
|
1448
|
+
let debounceTimer = null;
|
|
1449
|
+
const pendingChanges = new Set();
|
|
1450
|
+
const ignoreDirs = ['node_modules', '.git', 'dist', 'chroma_data', '.lemma', 'dashboard', 'bin', 'sdks'];
|
|
1451
|
+
const watchDirRecursive = (dir) => {
|
|
1452
|
+
try {
|
|
1453
|
+
const watcher = watch(dir, (eventType, filename) => {
|
|
1454
|
+
if (!filename)
|
|
1455
|
+
return;
|
|
1456
|
+
const fullPath = path_1.default.join(dir, filename);
|
|
1457
|
+
// Heuristic to ignore generated/system folders
|
|
1458
|
+
const relPath = path_1.default.relative(cwd, fullPath);
|
|
1459
|
+
if (ignoreDirs.some(d => relPath.split(path_1.default.sep).includes(d)))
|
|
1460
|
+
return;
|
|
1461
|
+
// Ignore non-source files
|
|
1462
|
+
const ext = path_1.default.extname(filename);
|
|
1463
|
+
if (!['.ts', '.js', '.tsx', '.jsx', '.json', '.py', '.css'].includes(ext))
|
|
1464
|
+
return;
|
|
1465
|
+
pendingChanges.add(fullPath);
|
|
1466
|
+
if (debounceTimer)
|
|
1467
|
+
clearTimeout(debounceTimer);
|
|
1468
|
+
debounceTimer = setTimeout(() => this.captureSnapshot(pendingChanges), 1500);
|
|
1469
|
+
});
|
|
1470
|
+
// Prevent watcher from keeping process alive if in background
|
|
1471
|
+
if (watcher.unref)
|
|
1472
|
+
watcher.unref();
|
|
1473
|
+
// Recursively watch subfolders
|
|
1474
|
+
const files = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
1475
|
+
for (const file of files) {
|
|
1476
|
+
if (file.isDirectory() && !ignoreDirs.includes(file.name)) {
|
|
1477
|
+
watchDirRecursive(path_1.default.join(dir, file.name));
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
catch { }
|
|
1482
|
+
};
|
|
1483
|
+
watchDirRecursive(cwd); // Time travel watcher active!
|
|
1484
|
+
}
|
|
1485
|
+
async captureSnapshot(pendingChanges) {
|
|
1486
|
+
const changes = Array.from(pendingChanges);
|
|
1487
|
+
pendingChanges.clear();
|
|
1488
|
+
const changedFiles = [];
|
|
1489
|
+
for (const filePath of changes) {
|
|
1490
|
+
if (!fs_1.default.existsSync(filePath))
|
|
1491
|
+
continue;
|
|
1492
|
+
try {
|
|
1493
|
+
const content = fs_1.default.readFileSync(filePath, 'utf8');
|
|
1494
|
+
const relPath = path_1.default.relative(process.cwd(), filePath);
|
|
1495
|
+
const previousContent = this.lastFileContents.get(filePath) || '';
|
|
1496
|
+
if (content === previousContent)
|
|
1497
|
+
continue;
|
|
1498
|
+
let diff = '';
|
|
1499
|
+
if (previousContent) {
|
|
1500
|
+
const { generateAstDiff } = require('../utils/ContextSqueezer');
|
|
1501
|
+
diff = generateAstDiff(previousContent, content);
|
|
1502
|
+
}
|
|
1503
|
+
this.lastFileContents.set(filePath, content);
|
|
1504
|
+
changedFiles.push({
|
|
1505
|
+
filePath: relPath,
|
|
1506
|
+
content,
|
|
1507
|
+
diff
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
catch { }
|
|
1511
|
+
}
|
|
1512
|
+
if (changedFiles.length === 0)
|
|
1513
|
+
return;
|
|
1514
|
+
// Detect current compiler state/errors
|
|
1515
|
+
let status = 'stable';
|
|
1516
|
+
let errorLog = '';
|
|
1517
|
+
// Check if there is a recent crash log in .lemma/live-context.md
|
|
1518
|
+
try {
|
|
1519
|
+
const liveContextPath = path_1.default.join(process.cwd(), '.lemma', 'live-context.md');
|
|
1520
|
+
if (fs_1.default.existsSync(liveContextPath)) {
|
|
1521
|
+
const content = fs_1.default.readFileSync(liveContextPath, 'utf8');
|
|
1522
|
+
if (content.includes('crashed') || content.includes('error') || content.includes('Exception')) {
|
|
1523
|
+
status = 'failure';
|
|
1524
|
+
errorLog = content.substring(0, 1000);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
catch { }
|
|
1529
|
+
const snapshot = {
|
|
1530
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
1531
|
+
timestamp: Date.now(),
|
|
1532
|
+
changedFiles,
|
|
1533
|
+
status,
|
|
1534
|
+
errorLog
|
|
1535
|
+
};
|
|
1536
|
+
this.timeline.push(snapshot);
|
|
1537
|
+
// Cap at 10 snapshots (FIFO eviction)
|
|
1538
|
+
if (this.timeline.length > 10) {
|
|
1539
|
+
this.timeline.shift();
|
|
1540
|
+
}
|
|
1541
|
+
console.log(`🌌 \x1b[35m[Multiverse]\x1b[0m Captured micro-snapshot ${snapshot.id} (${changedFiles.length} file edits mapped).`);
|
|
1542
|
+
}
|
|
1279
1543
|
async handleCompletion(req, res, provider) {
|
|
1280
1544
|
const t0 = Date.now();
|
|
1281
1545
|
const pro = await isPro();
|
|
@@ -1312,16 +1576,20 @@ class LemmaServer {
|
|
|
1312
1576
|
}
|
|
1313
1577
|
// Perform semantic cache check (resolves in ~15ms)
|
|
1314
1578
|
if (pro) {
|
|
1315
|
-
const semHit = await semanticGet(provider, prompt);
|
|
1579
|
+
const semHit = await semanticGet(provider, prompt, this.projectName);
|
|
1316
1580
|
if (semHit) {
|
|
1317
1581
|
if (semHit.similarity >= 0.90) {
|
|
1318
1582
|
await recordStat(this.stats, this.projectName, true, Date.now() - t0, provider, 2000);
|
|
1319
1583
|
const unmaskedData = semanticScrubber.unmask(semHit.data, tokenMap);
|
|
1584
|
+
const cacheHeader = semHit.hiveMind ? 'HIT-HIVE-MIND' : 'HIT';
|
|
1320
1585
|
if (isStream)
|
|
1321
|
-
return simulateHitStream(res, unmaskedData, provider, originalModel, tier, semHit.similarity);
|
|
1322
|
-
res.setHeader('X-Lemma-Cache',
|
|
1586
|
+
return simulateHitStream(res, unmaskedData, provider, originalModel, tier, semHit.similarity, cacheHeader);
|
|
1587
|
+
res.setHeader('X-Lemma-Cache', cacheHeader);
|
|
1323
1588
|
res.setHeader('X-Lemma-Similarity', semHit.similarity.toFixed(3));
|
|
1324
1589
|
res.setHeader('X-Lemma-Tier', tier);
|
|
1590
|
+
if (semHit.hiveMind && semHit.originProject) {
|
|
1591
|
+
res.setHeader('X-Lemma-Hive-Origin', semHit.originProject);
|
|
1592
|
+
}
|
|
1325
1593
|
return res.json(unmaskedData);
|
|
1326
1594
|
}
|
|
1327
1595
|
else if (semHit.similarity >= 0.82) { // raised from 0.70 to 0.82 to prevent false positives
|
|
@@ -1469,7 +1737,7 @@ Adjusted Answer:`;
|
|
|
1469
1737
|
const data = await this.callUpstream(req.body, provider);
|
|
1470
1738
|
cacheSet(provider, prompt, data);
|
|
1471
1739
|
if (pro) {
|
|
1472
|
-
await semanticSet(provider, prompt, data);
|
|
1740
|
+
await semanticSet(provider, prompt, data, this.projectName);
|
|
1473
1741
|
await cloudSync.set(prompt, data);
|
|
1474
1742
|
}
|
|
1475
1743
|
if (!pro) {
|
|
@@ -1724,7 +1992,11 @@ commander_1.program.command('start')
|
|
|
1724
1992
|
if (opts.stack) {
|
|
1725
1993
|
const { spawn } = require('child_process');
|
|
1726
1994
|
console.log('🚀 Launching Lemma Full Stack...');
|
|
1727
|
-
const
|
|
1995
|
+
const chromaDataPath = path_1.default.join(CACHE_DIR, 'chroma_data');
|
|
1996
|
+
if (!fs_1.default.existsSync(chromaDataPath)) {
|
|
1997
|
+
fs_1.default.mkdirSync(chromaDataPath, { recursive: true });
|
|
1998
|
+
}
|
|
1999
|
+
const chromaProcess = spawn('chroma', ['run', '--path', chromaDataPath], { stdio: 'ignore', detached: true });
|
|
1728
2000
|
chromaProcess.unref();
|
|
1729
2001
|
console.log(' ✅ ChromaDB requested');
|
|
1730
2002
|
const dash = spawn('npm', ['run', 'dev', '--prefix', 'dashboard', '--', '--port', '8082'], { stdio: 'ignore', detached: true });
|