@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.
- package/README.md +20 -6
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +343 -39
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/mcp/index.js +279 -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 +343 -39
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/mcp/index.js +279 -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
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# π§ Lemma v0.
|
|
1
|
+
# π§ Lemma v0.6.0
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@nxuss/lemma)
|
|
4
4
|
[](https://github.com/Nxusbets/lemma/blob/main/LICENSE)
|
|
@@ -25,8 +25,20 @@ Lemma is a high-performance orchestration layer that sits between your developme
|
|
|
25
25
|
### π₯οΈ Real-Time Telemetry & Time-Travel Dashboard
|
|
26
26
|
**Full Observability & Interactive Debugging.** Spin up a gorgeous React-based dashboard at `http://localhost:8081/dashboard/` to monitor your entire AI workflow. View real-time charts of cost savings, token compactions, and cache hits. Walk through complex multi-agent conversations step-by-step using a time-travel slider, select and inspect active agent states, and export detailed telemetry audits instantly.
|
|
27
27
|
|
|
28
|
-
###
|
|
29
|
-
**
|
|
28
|
+
### πΈ Kiro/Cursor Composer Bypass (Pro Option) π
|
|
29
|
+
**Autonomous Multi-File Editing For Free.** Exposes `write_workspace_file`, smart `apply_workspace_patch` (smart search-and-replace patching), and sandbox terminal tool `run_workspace_command` (compile/lint check) to Cursor's standard chat or Claude Desktop via MCP. Any standard free chat instantly acts as a fully-featured, unlimited autonomous multi-file Composer without needing a premium Kiro/Cursor Pro subscription.
|
|
30
|
+
|
|
31
|
+
### π AST Time-Travel Telemetry: El Multiverso de CΓ³digo (Pro Option) π
|
|
32
|
+
**Regressive Error Debugging & Time-Travel.** Spawns a background recursive file watcher that debounces filesystem edits and calculates incremental AST diffs on every save. Merges active compiler crash logs to package a chronologically ordered time-traveling timeline resource (`lemma://multiverse/timeline`) so your LLM agents can inspect past codebase states and regressions instantly when you say "It broke".
|
|
33
|
+
|
|
34
|
+
### πΊοΈ Zero-Shot Project Onboarding (Pro Option) π
|
|
35
|
+
**One-Shot Architecture Map.** Exposes a dynamic onboard resource (`lemma://project/onboarding`) that scans package files, folders, and key directory markers to compile a beautiful, structured markdown guide of your codebase's stack, directory layouts, and design principles so the LLM gets fully onboarded in a single shot.
|
|
36
|
+
|
|
37
|
+
### π§ Cross-Project Bug Telepathy (Pro Option) π
|
|
38
|
+
**Global Developer Hive Mind.** Lemma dynamically scans and queries vector memory indexes of all other local projects on your computer. If you solved a compiler issue or technical blocker in a different workspace last week, Lemma will telepathically inject that high-confidence solution to the current project's chat without you typing a single word.
|
|
39
|
+
|
|
40
|
+
### βοΈ Codebase Context Squeezer & Log Collapsing
|
|
41
|
+
**Massive Speedups & Zero Token Waste.** Get instant model responses and make your context windows go up to 10x further. Lemma dynamically squeezes outgoing codebase payloads, strips comments/whitespaces, and collapses repetitive console logs or redundant compiler stack traces, saving up to 95% on token consumption automatically.
|
|
30
42
|
|
|
31
43
|
### πΈ Local Cache-Augmented Response Synthesis (CARS)
|
|
32
44
|
**Zero-Cost Answer Synthesis.** Experience instantaneous responses for questions similar to what you've asked beforeβwithout hitting the cloud or spending a single penny on API tokens. Lemma automatically refines past knowledge to answer your current questions in under a second, keeping your work completely private and free of charge.
|
|
@@ -141,8 +153,10 @@ Create or edit `lemma.config.json` in your project root to disable or fine-tune
|
|
|
141
153
|
| **Privacy Firewall** | β
Included | β
Advanced Masking |
|
|
142
154
|
| **Complexity Router** | β
Included | β
Custom Routing Policies |
|
|
143
155
|
| **Caching** | Exact Match | **Semantic Memory** |
|
|
144
|
-
| **Hive Mind** | Local Only | **Cloud Sync (Team Memory)
|
|
145
|
-
| **
|
|
156
|
+
| **Hive Mind** | Local Only | **Cloud Sync (Team Memory) + Cross-Project Telepathy π** |
|
|
157
|
+
| **AST Time-Travel Telemetry** | β None | **Codebase Multiverse (Cap 10 AST Snapshots) π** |
|
|
158
|
+
| **Kiro/Cursor Composer Bypass** | β None | **Smart Patches & Sandbox Terminal MCP π** |
|
|
159
|
+
| **Zero-Shot Onboarding Guide** | β None | **Codebase Architecture Map MCP π** |
|
|
146
160
|
| **Telemetry Dashboard** | Basic Stats | **Time-Travel Debugger & 60fps Graph** |
|
|
147
161
|
| **Limits** | 300 requests/mo | **Unlimited Agentic Power** |
|
|
148
162
|
|
|
@@ -160,7 +174,7 @@ You don't need to change your habits. Just point your tool's "Base URL" to Lemma
|
|
|
160
174
|
* **Custom Apps:** Replace `https://api.openai.com/v1` with `http://localhost:8081/v1` in your SDK initialization.
|
|
161
175
|
|
|
162
176
|
### π Pro Integration (Model Context Protocol - MCP)
|
|
163
|
-
For **Lemma Pro** users, you can expose Lemma's entire intelligence layer (Privacy Firewall, Semantic Brain Search, and Auto-Heal) directly as native tools using the **Model Context Protocol (MCP)**.
|
|
177
|
+
For **Lemma Pro** users, you can expose Lemma's entire intelligence layer (Privacy Firewall, Semantic Brain Search, and Auto-Heal, plus the new Composer Bypass Suite, AST Timeline, and Zero-Shot Onboarding Map) directly as native tools using the **Model Context Protocol (MCP)**. This lets the LLM read, search, write, patch, and compile files in your workspace autonomously.
|
|
164
178
|
|
|
165
179
|
#### π’ Cursor:
|
|
166
180
|
1. Go to **Settings > Features > MCP**.
|
|
@@ -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
|
});
|
|
@@ -1233,17 +1398,148 @@ class LemmaServer {
|
|
|
1233
1398
|
if (!source || !validSources.includes(source)) {
|
|
1234
1399
|
return res.status(400).json({ error: 'Invalid source' });
|
|
1235
1400
|
}
|
|
1401
|
+
let tk = 0;
|
|
1236
1402
|
if (typeof tokens === 'number' && tokens > 0) {
|
|
1403
|
+
tk = tokens;
|
|
1237
1404
|
SavingsLedger_1.savingsLedger.recordTokens(source, tokens);
|
|
1238
1405
|
}
|
|
1239
1406
|
else if (typeof charsBefore === 'number' && typeof charsAfter === 'number') {
|
|
1407
|
+
tk = Math.floor(Math.max(0, charsBefore - charsAfter) / 4);
|
|
1240
1408
|
SavingsLedger_1.savingsLedger.record(source, charsBefore, charsAfter);
|
|
1241
1409
|
}
|
|
1410
|
+
// Update active project stats to immediately reflect on Dashboard total requests & charts
|
|
1411
|
+
if (tk > 0) {
|
|
1412
|
+
if (!this.stats[this.projectName]) {
|
|
1413
|
+
this.stats[this.projectName] = { total: 0, hits: 0, misses: 0, totalLatency: 0, totalTokensSaved: 0, providers: {} };
|
|
1414
|
+
}
|
|
1415
|
+
const s = this.stats[this.projectName];
|
|
1416
|
+
s.total++;
|
|
1417
|
+
s.totalTokensSaved += tk;
|
|
1418
|
+
s.totalLatency += 15; // mock latency for local MCP metrics
|
|
1419
|
+
if (source === 'cache') {
|
|
1420
|
+
s.hits++;
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
s.misses++;
|
|
1424
|
+
}
|
|
1425
|
+
if (!s.providers['mcp'])
|
|
1426
|
+
s.providers['mcp'] = { hits: 0, misses: 0 };
|
|
1427
|
+
source === 'cache' ? s.providers['mcp'].hits++ : s.providers['mcp'].misses++;
|
|
1428
|
+
// Write updated stats back to disk immediately
|
|
1429
|
+
writeJson(STATS_FILE, this.stats).catch(() => { });
|
|
1430
|
+
// Push to live telemetry logEvent array so the Dashboard event timeline updates instantly!
|
|
1431
|
+
logEvent({
|
|
1432
|
+
type: source === 'cache' ? 'cache:hit' : source,
|
|
1433
|
+
project: this.projectName,
|
|
1434
|
+
latency: 15,
|
|
1435
|
+
provider: 'mcp',
|
|
1436
|
+
tokens: tk
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1242
1439
|
res.json({ ok: true, snapshot: SavingsLedger_1.savingsLedger.getSnapshot().total });
|
|
1243
1440
|
});
|
|
1244
1441
|
this.app.get('/dashboard/*', (req, res) => res.sendFile(path_1.default.join(this.dashboardPath, 'index.html')));
|
|
1245
1442
|
}
|
|
1246
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
|
+
}
|
|
1247
1543
|
async handleCompletion(req, res, provider) {
|
|
1248
1544
|
const t0 = Date.now();
|
|
1249
1545
|
const pro = await isPro();
|
|
@@ -1280,16 +1576,20 @@ class LemmaServer {
|
|
|
1280
1576
|
}
|
|
1281
1577
|
// Perform semantic cache check (resolves in ~15ms)
|
|
1282
1578
|
if (pro) {
|
|
1283
|
-
const semHit = await semanticGet(provider, prompt);
|
|
1579
|
+
const semHit = await semanticGet(provider, prompt, this.projectName);
|
|
1284
1580
|
if (semHit) {
|
|
1285
1581
|
if (semHit.similarity >= 0.90) {
|
|
1286
1582
|
await recordStat(this.stats, this.projectName, true, Date.now() - t0, provider, 2000);
|
|
1287
1583
|
const unmaskedData = semanticScrubber.unmask(semHit.data, tokenMap);
|
|
1584
|
+
const cacheHeader = semHit.hiveMind ? 'HIT-HIVE-MIND' : 'HIT';
|
|
1288
1585
|
if (isStream)
|
|
1289
|
-
return simulateHitStream(res, unmaskedData, provider, originalModel, tier, semHit.similarity);
|
|
1290
|
-
res.setHeader('X-Lemma-Cache',
|
|
1586
|
+
return simulateHitStream(res, unmaskedData, provider, originalModel, tier, semHit.similarity, cacheHeader);
|
|
1587
|
+
res.setHeader('X-Lemma-Cache', cacheHeader);
|
|
1291
1588
|
res.setHeader('X-Lemma-Similarity', semHit.similarity.toFixed(3));
|
|
1292
1589
|
res.setHeader('X-Lemma-Tier', tier);
|
|
1590
|
+
if (semHit.hiveMind && semHit.originProject) {
|
|
1591
|
+
res.setHeader('X-Lemma-Hive-Origin', semHit.originProject);
|
|
1592
|
+
}
|
|
1293
1593
|
return res.json(unmaskedData);
|
|
1294
1594
|
}
|
|
1295
1595
|
else if (semHit.similarity >= 0.82) { // raised from 0.70 to 0.82 to prevent false positives
|
|
@@ -1437,7 +1737,7 @@ Adjusted Answer:`;
|
|
|
1437
1737
|
const data = await this.callUpstream(req.body, provider);
|
|
1438
1738
|
cacheSet(provider, prompt, data);
|
|
1439
1739
|
if (pro) {
|
|
1440
|
-
await semanticSet(provider, prompt, data);
|
|
1740
|
+
await semanticSet(provider, prompt, data, this.projectName);
|
|
1441
1741
|
await cloudSync.set(prompt, data);
|
|
1442
1742
|
}
|
|
1443
1743
|
if (!pro) {
|
|
@@ -1692,7 +1992,11 @@ commander_1.program.command('start')
|
|
|
1692
1992
|
if (opts.stack) {
|
|
1693
1993
|
const { spawn } = require('child_process');
|
|
1694
1994
|
console.log('π Launching Lemma Full Stack...');
|
|
1695
|
-
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 });
|
|
1696
2000
|
chromaProcess.unref();
|
|
1697
2001
|
console.log(' β
ChromaDB requested');
|
|
1698
2002
|
const dash = spawn('npm', ['run', 'dev', '--prefix', 'dashboard', '--', '--port', '8082'], { stdio: 'ignore', detached: true });
|