@nxuss/lemma 0.9.3 → 1.0.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 +35 -4
- package/bin/cli.js +97 -0
- package/bin/init.js +307 -0
- package/cache-proxy.js +191 -0
- package/dist/cjs/autopilot/AutopilotWatcher.d.ts +3 -2
- package/dist/cjs/autopilot/AutopilotWatcher.d.ts.map +1 -1
- package/dist/cjs/autopilot/AutopilotWatcher.js +48 -54
- package/dist/cjs/autopilot/AutopilotWatcher.js.map +1 -1
- package/dist/cjs/autopilot/Notifier.d.ts +5 -0
- package/dist/cjs/autopilot/Notifier.d.ts.map +1 -0
- package/dist/cjs/autopilot/Notifier.js +59 -0
- package/dist/cjs/autopilot/Notifier.js.map +1 -0
- package/dist/cjs/autopilot/PatchStore.d.ts +26 -0
- package/dist/cjs/autopilot/PatchStore.d.ts.map +1 -0
- package/dist/cjs/autopilot/PatchStore.js +88 -0
- package/dist/cjs/autopilot/PatchStore.js.map +1 -0
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +123 -4
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/mcp/index.js +1 -1
- package/dist/cjs/mcp/tools.d.ts.map +1 -1
- package/dist/cjs/mcp/tools.js +303 -77
- package/dist/cjs/mcp/tools.js.map +1 -1
- package/dist/cjs/subconscious/TheBrainV2.d.ts +102 -0
- package/dist/cjs/subconscious/TheBrainV2.d.ts.map +1 -0
- package/dist/cjs/subconscious/TheBrainV2.js +444 -0
- package/dist/cjs/subconscious/TheBrainV2.js.map +1 -0
- package/dist/cjs/utils/ContextCollapser.d.ts +21 -0
- package/dist/cjs/utils/ContextCollapser.d.ts.map +1 -0
- package/dist/cjs/utils/ContextCollapser.js +132 -0
- package/dist/cjs/utils/ContextCollapser.js.map +1 -0
- package/dist/cjs/utils/ExecutiveReport.d.ts +29 -0
- package/dist/cjs/utils/ExecutiveReport.d.ts.map +1 -0
- package/dist/cjs/utils/ExecutiveReport.js +111 -0
- package/dist/cjs/utils/ExecutiveReport.js.map +1 -0
- package/dist/cjs/utils/SurgicalASTInsert.d.ts +21 -0
- package/dist/cjs/utils/SurgicalASTInsert.d.ts.map +1 -0
- package/dist/cjs/utils/SurgicalASTInsert.js +173 -0
- package/dist/cjs/utils/SurgicalASTInsert.js.map +1 -0
- package/dist/cjs/utils/SymbolSurgicalContext.d.ts +25 -0
- package/dist/cjs/utils/SymbolSurgicalContext.d.ts.map +1 -0
- package/dist/cjs/utils/SymbolSurgicalContext.js +265 -0
- package/dist/cjs/utils/SymbolSurgicalContext.js.map +1 -0
- package/dist/cjs/utils/WormholeCompressor.d.ts +22 -0
- package/dist/cjs/utils/WormholeCompressor.d.ts.map +1 -0
- package/dist/cjs/utils/WormholeCompressor.js +152 -0
- package/dist/cjs/utils/WormholeCompressor.js.map +1 -0
- package/dist/esm/autopilot/AutopilotWatcher.d.ts +3 -2
- package/dist/esm/autopilot/AutopilotWatcher.d.ts.map +1 -1
- package/dist/esm/autopilot/AutopilotWatcher.js +48 -54
- package/dist/esm/autopilot/AutopilotWatcher.js.map +1 -1
- package/dist/esm/autopilot/Notifier.d.ts +5 -0
- package/dist/esm/autopilot/Notifier.d.ts.map +1 -0
- package/dist/esm/autopilot/Notifier.js +51 -0
- package/dist/esm/autopilot/Notifier.js.map +1 -0
- package/dist/esm/autopilot/PatchStore.d.ts +26 -0
- package/dist/esm/autopilot/PatchStore.d.ts.map +1 -0
- package/dist/esm/autopilot/PatchStore.js +77 -0
- package/dist/esm/autopilot/PatchStore.js.map +1 -0
- package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +123 -4
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/mcp/index.js +1 -1
- package/dist/esm/mcp/tools.d.ts.map +1 -1
- package/dist/esm/mcp/tools.js +303 -77
- package/dist/esm/mcp/tools.js.map +1 -1
- package/dist/esm/subconscious/TheBrainV2.d.ts +102 -0
- package/dist/esm/subconscious/TheBrainV2.d.ts.map +1 -0
- package/dist/esm/subconscious/TheBrainV2.js +431 -0
- package/dist/esm/subconscious/TheBrainV2.js.map +1 -0
- package/dist/esm/utils/ContextCollapser.d.ts +21 -0
- package/dist/esm/utils/ContextCollapser.d.ts.map +1 -0
- package/dist/esm/utils/ContextCollapser.js +128 -0
- package/dist/esm/utils/ContextCollapser.js.map +1 -0
- package/dist/esm/utils/ExecutiveReport.d.ts +29 -0
- package/dist/esm/utils/ExecutiveReport.d.ts.map +1 -0
- package/dist/esm/utils/ExecutiveReport.js +105 -0
- package/dist/esm/utils/ExecutiveReport.js.map +1 -0
- package/dist/esm/utils/SurgicalASTInsert.d.ts +21 -0
- package/dist/esm/utils/SurgicalASTInsert.d.ts.map +1 -0
- package/dist/esm/utils/SurgicalASTInsert.js +134 -0
- package/dist/esm/utils/SurgicalASTInsert.js.map +1 -0
- package/dist/esm/utils/SymbolSurgicalContext.d.ts +25 -0
- package/dist/esm/utils/SymbolSurgicalContext.d.ts.map +1 -0
- package/dist/esm/utils/SymbolSurgicalContext.js +226 -0
- package/dist/esm/utils/SymbolSurgicalContext.js.map +1 -0
- package/dist/esm/utils/WormholeCompressor.d.ts +22 -0
- package/dist/esm/utils/WormholeCompressor.d.ts.map +1 -0
- package/dist/esm/utils/WormholeCompressor.js +148 -0
- package/dist/esm/utils/WormholeCompressor.js.map +1 -0
- package/lemma-proxy.cjs +31 -14
- package/mcp-server.js +220 -0
- package/package.json +7 -2
package/dist/cjs/mcp/tools.js
CHANGED
|
@@ -52,6 +52,12 @@ const utils_1 = require("./utils");
|
|
|
52
52
|
const PRReviewEngine_1 = require("../pr-review/PRReviewEngine");
|
|
53
53
|
const ReviewStore_1 = require("../pr-review/ReviewStore");
|
|
54
54
|
const reviewRunner_1 = require("../pr-review/reviewRunner");
|
|
55
|
+
const TheBrainV2_1 = require("../subconscious/TheBrainV2");
|
|
56
|
+
const ContextCollapser_1 = require("../utils/ContextCollapser");
|
|
57
|
+
const SymbolSurgicalContext_1 = require("../utils/SymbolSurgicalContext");
|
|
58
|
+
const WormholeCompressor_1 = require("../utils/WormholeCompressor");
|
|
59
|
+
const ExecutiveReport_1 = require("../utils/ExecutiveReport");
|
|
60
|
+
const SurgicalASTInsert_1 = require("../utils/SurgicalASTInsert");
|
|
55
61
|
// ── Pro License Gate ──────────────────────────────────────────────────────────
|
|
56
62
|
const LICENSE_FILE = path_1.default.join(os_1.default.homedir(), '.lemma-cache', 'license.json');
|
|
57
63
|
let _proStatusCache = null;
|
|
@@ -69,11 +75,16 @@ function isProUser() {
|
|
|
69
75
|
_proStatusCache = false;
|
|
70
76
|
return false;
|
|
71
77
|
}
|
|
78
|
+
const FREE_TOOLS = new Set([
|
|
79
|
+
// Cache & Memory — the hook that shows instant value
|
|
80
|
+
"smarter_cache", "search_memory", "store_memory",
|
|
81
|
+
// Token optimization — shows what they're saving
|
|
82
|
+
"token_budget", "squeeze_prompt", "turbosqueeze",
|
|
83
|
+
// Utility — just enough to function
|
|
84
|
+
"read_workspace_file", "scrub_privacy", "summarize_long_text",
|
|
85
|
+
]);
|
|
72
86
|
const PRO_GATE_MESSAGE = [
|
|
73
|
-
"🔒
|
|
74
|
-
"",
|
|
75
|
-
"All 32 MCP tools — including The Brain, zero-token static analysis,",
|
|
76
|
-
"PreCrime predictor, Coupling Radar, and Entropy Score — are Pro features.",
|
|
87
|
+
"🔒 This tool requires Lemma Pro.",
|
|
77
88
|
"",
|
|
78
89
|
"✨ Activate your license:",
|
|
79
90
|
" lemma activate <your-key>",
|
|
@@ -359,6 +370,67 @@ const toolDefinitions = [
|
|
|
359
370
|
required: ["text"],
|
|
360
371
|
},
|
|
361
372
|
},
|
|
373
|
+
{
|
|
374
|
+
name: "get_symbol_surgical_context",
|
|
375
|
+
description: "Extrae quirúrgicamente la implementación de un símbolo específico (clase, función, interfaz) y las firmas (pero no la implementación completa) de todas sus dependencias locales del workspace. Ahorra hasta 90% de tokens.",
|
|
376
|
+
inputSchema: {
|
|
377
|
+
type: "object",
|
|
378
|
+
properties: {
|
|
379
|
+
filePath: { type: "string", description: "Ruta relativa del archivo que contiene el símbolo" },
|
|
380
|
+
symbolName: { type: "string", description: "Nombre de la función, clase, tipo o interfaz a extraer" },
|
|
381
|
+
},
|
|
382
|
+
required: ["filePath", "symbolName"],
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: "wormhole_squeeze",
|
|
387
|
+
description: "Comprime código a formato WORMHOLE súper denso usando tokens de palabra clave y abreviación de variables reversible. Ideal para alimentar contextos de lectura al LLM.",
|
|
388
|
+
inputSchema: {
|
|
389
|
+
type: "object",
|
|
390
|
+
properties: {
|
|
391
|
+
text: { type: "string", description: "El código fuente a comprimir" },
|
|
392
|
+
},
|
|
393
|
+
required: ["text"],
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: "generate_executive_roi_report",
|
|
398
|
+
description: "Genera un reporte ejecutivo en formato Markdown de Retorno de Inversión (ROI), dinero ahorrado en API, horas de desarrollo recuperadas y proyecciones de escala para la gerencia.",
|
|
399
|
+
inputSchema: {
|
|
400
|
+
type: "object",
|
|
401
|
+
properties: {
|
|
402
|
+
teamSize: { type: "number", description: "Número de ingenieros en el equipo (default: 10)", default: 10 },
|
|
403
|
+
costPerDevHour: { type: "number", description: "Costo estimado en USD por hora de desarrollador (default: 50)", default: 50 },
|
|
404
|
+
targetPath: { type: "string", description: "Nombre de archivo del reporte generado (default: LEMMA_ROI_REPORT.md)", default: "LEMMA_ROI_REPORT.md" },
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: "surgical_ast_insert",
|
|
410
|
+
description: "Inserta quirúrgicamente código (métodos, propiedades, funciones) en una clase, interfaz o ámbito de archivo de TypeScript utilizando el AST Compiler. Evita escribir diffs de búsqueda y reemplazo grandes o reescribir todo el archivo. Ahorra 95% de tokens de salida.",
|
|
411
|
+
inputSchema: {
|
|
412
|
+
type: "object",
|
|
413
|
+
properties: {
|
|
414
|
+
filePath: { type: "string", description: "Ruta del archivo TypeScript (relativo al root)" },
|
|
415
|
+
symbolName: { type: "string", description: "Nombre de la clase o interfaz donde insertar (opcional)" },
|
|
416
|
+
anchorSymbol: { type: "string", description: "Nombre del método o propiedad como anclaje (ej. 'login')" },
|
|
417
|
+
insertPosition: { type: "string", enum: ["before", "after", "start", "end"], description: "Posición relativa al anclaje (default: 'after')", default: "after" },
|
|
418
|
+
codeToInsert: { type: "string", description: "El código fuente exacto a insertar" },
|
|
419
|
+
},
|
|
420
|
+
required: ["filePath", "codeToInsert"],
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: "local_semantic_autofix",
|
|
425
|
+
description: "Intenta resolver errores de compilación o ejecución localmente consultando The Brain por tracebacks/mensajes similares. Si hay coincidencia, devuelve la solución/parche instantáneamente sin gastar tokens de LLM.",
|
|
426
|
+
inputSchema: {
|
|
427
|
+
type: "object",
|
|
428
|
+
properties: {
|
|
429
|
+
errorMessage: { type: "string", description: "El traceback del error o mensaje de error a resolver" },
|
|
430
|
+
},
|
|
431
|
+
required: ["errorMessage"],
|
|
432
|
+
},
|
|
433
|
+
},
|
|
362
434
|
{
|
|
363
435
|
name: "compress_context",
|
|
364
436
|
description: "Comprime el historial de conversación: resume turns antiguos vía Ollama, mantiene solo los últimos N intactos. Reduce contexto 70-80%. USAR antes de cada turno extendido.",
|
|
@@ -587,6 +659,11 @@ const toolHandlers = {
|
|
|
587
659
|
diff_only: handleDiffOnly,
|
|
588
660
|
batch_tool_calls: handleBatchToolCalls,
|
|
589
661
|
turbosqueeze: handleTurboSqueeze,
|
|
662
|
+
get_symbol_surgical_context: handleGetSymbolSurgicalContext,
|
|
663
|
+
wormhole_squeeze: handleWormholeSqueeze,
|
|
664
|
+
generate_executive_roi_report: handleGenerateExecutiveRoiReport,
|
|
665
|
+
surgical_ast_insert: handleSurgicalAstInsert,
|
|
666
|
+
local_semantic_autofix: handleLocalSemanticAutofix,
|
|
590
667
|
compress_context: handleCompressContext,
|
|
591
668
|
smarter_cache: handleSmarterCache,
|
|
592
669
|
token_budget: handleTokenBudget,
|
|
@@ -611,8 +688,8 @@ function setupToolsHandlers(server, onToolCall) {
|
|
|
611
688
|
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
612
689
|
const { name, arguments: args } = request.params;
|
|
613
690
|
const startTime = Date.now();
|
|
614
|
-
// ── Pro License Gate
|
|
615
|
-
if (!isProUser()) {
|
|
691
|
+
// ── Pro License Gate (granular: free tools work without license) ────────
|
|
692
|
+
if (!isProUser() && !FREE_TOOLS.has(name)) {
|
|
616
693
|
onToolCall?.({
|
|
617
694
|
tool: name,
|
|
618
695
|
args: args || {},
|
|
@@ -675,22 +752,21 @@ async function handleSearchMemory(args) {
|
|
|
675
752
|
if (!query)
|
|
676
753
|
throw new Error("Query is required");
|
|
677
754
|
try {
|
|
678
|
-
const
|
|
679
|
-
const
|
|
680
|
-
const results = response.data.results || [];
|
|
755
|
+
const brain = (0, TheBrainV2_1.getBrain)();
|
|
756
|
+
const results = brain.search(query, limit);
|
|
681
757
|
if (results.length === 0) {
|
|
682
758
|
return { content: [{ type: "text", text: "No relevant memories found in Lemma's Brain." }] };
|
|
683
759
|
}
|
|
684
760
|
const formatted = results
|
|
685
761
|
.map((r, i) => {
|
|
686
|
-
return `Result ${i + 1} (Similarity: ${(r.similarity * 100).toFixed(1)}%)\nPrompt: ${r.
|
|
762
|
+
return `Result ${i + 1} (Similarity: ${(r.similarity * 100).toFixed(1)}%)\nPrompt: ${r.query.substring(0, 300)}...\nResponse: ${typeof r.response === "string" ? r.response : JSON.stringify(r.response, null, 2)}`;
|
|
687
763
|
})
|
|
688
764
|
.join("\n\n---\n\n");
|
|
689
765
|
return { content: [{ type: "text", text: `Lemma found ${results.length} memories:\n\n${formatted}` }] };
|
|
690
766
|
}
|
|
691
767
|
catch (e) {
|
|
692
768
|
(0, utils_1.logError)("search_memory", e);
|
|
693
|
-
return { content: [{ type: "text", text: `
|
|
769
|
+
return { content: [{ type: "text", text: `Local Search failed: ${e.message}` }] };
|
|
694
770
|
}
|
|
695
771
|
}
|
|
696
772
|
async function handleStoreMemory(args) {
|
|
@@ -700,12 +776,8 @@ async function handleStoreMemory(args) {
|
|
|
700
776
|
if (!query || !responseText)
|
|
701
777
|
throw new Error("Query and response are required");
|
|
702
778
|
try {
|
|
703
|
-
const
|
|
704
|
-
const
|
|
705
|
-
query,
|
|
706
|
-
response: responseText,
|
|
707
|
-
provider,
|
|
708
|
-
});
|
|
779
|
+
const brain = (0, TheBrainV2_1.getBrain)();
|
|
780
|
+
const storeRes = brain.store(query, responseText, provider);
|
|
709
781
|
const tokensSaved = Math.max(100, Math.floor(responseText.length / 4));
|
|
710
782
|
(0, reportSavings_1.reportSavings)({
|
|
711
783
|
source: "cache",
|
|
@@ -713,11 +785,11 @@ async function handleStoreMemory(args) {
|
|
|
713
785
|
toolName: "store_memory",
|
|
714
786
|
query: query.substring(0, 100),
|
|
715
787
|
});
|
|
716
|
-
return { content: [{ type: "text", text: `Success: ${
|
|
788
|
+
return { content: [{ type: "text", text: `Success: Memory stored. ${storeRes.reason}` }] };
|
|
717
789
|
}
|
|
718
790
|
catch (e) {
|
|
719
791
|
(0, utils_1.logError)("store_memory", e);
|
|
720
|
-
return { content: [{ type: "text", text: `Failed to store memory in
|
|
792
|
+
return { content: [{ type: "text", text: `Failed to store memory in local Brain: ${e.message}` }] };
|
|
721
793
|
}
|
|
722
794
|
}
|
|
723
795
|
async function handleGetRoutingAdvice(args) {
|
|
@@ -1285,24 +1357,20 @@ async function handleQueryHybridConsensus(args) {
|
|
|
1285
1357
|
const threshold = typeof args?.threshold === "number" ? args.threshold : 0.8;
|
|
1286
1358
|
if (!query)
|
|
1287
1359
|
throw new Error("query is required");
|
|
1288
|
-
const port = (0, utils_1.getProxyPort)();
|
|
1289
1360
|
const fullQuery = context ? `${query}\n\nContext: ${context}` : query;
|
|
1290
1361
|
try {
|
|
1291
|
-
const
|
|
1292
|
-
const results =
|
|
1362
|
+
const brain = (0, TheBrainV2_1.getBrain)();
|
|
1363
|
+
const results = brain.search(fullQuery, 3);
|
|
1293
1364
|
const topHit = results[0];
|
|
1294
1365
|
if (topHit && topHit.similarity >= threshold) {
|
|
1295
|
-
const savedTokens = Math.floor((topHit.response?.
|
|
1366
|
+
const savedTokens = Math.floor((topHit.response?.length || 500) / 4);
|
|
1296
1367
|
(0, reportSavings_1.reportSavings)({
|
|
1297
1368
|
source: "cache",
|
|
1298
1369
|
tokens: savedTokens,
|
|
1299
1370
|
toolName: "query_hybrid_consensus",
|
|
1300
1371
|
query: query.substring(0, 100),
|
|
1301
1372
|
});
|
|
1302
|
-
const responseText =
|
|
1303
|
-
? topHit.response
|
|
1304
|
-
: topHit.response?.choices?.[0]?.message?.content ||
|
|
1305
|
-
JSON.stringify(topHit.response, null, 2);
|
|
1373
|
+
const responseText = topHit.response;
|
|
1306
1374
|
return {
|
|
1307
1375
|
content: [
|
|
1308
1376
|
{
|
|
@@ -1328,7 +1396,7 @@ async function handleQueryHybridConsensus(args) {
|
|
|
1328
1396
|
(0, utils_1.logError)("query_hybrid_consensus", e);
|
|
1329
1397
|
return {
|
|
1330
1398
|
content: [
|
|
1331
|
-
{ type: "text", text: `Hybrid Consensus failed: ${e.message}
|
|
1399
|
+
{ type: "text", text: `Hybrid Consensus failed: ${e.message}` },
|
|
1332
1400
|
],
|
|
1333
1401
|
};
|
|
1334
1402
|
}
|
|
@@ -1339,7 +1407,6 @@ async function handleGetTelepathicHints(args) {
|
|
|
1339
1407
|
const limit = typeof args?.limit === "number" ? args.limit : 5;
|
|
1340
1408
|
if (!activeFile)
|
|
1341
1409
|
throw new Error("activeFile is required");
|
|
1342
|
-
const port = (0, utils_1.getProxyPort)();
|
|
1343
1410
|
const basename = path_1.default.basename(activeFile, path_1.default.extname(activeFile));
|
|
1344
1411
|
const dirContext = path_1.default
|
|
1345
1412
|
.dirname(activeFile)
|
|
@@ -1350,8 +1417,8 @@ async function handleGetTelepathicHints(args) {
|
|
|
1350
1417
|
const ext = path_1.default.extname(activeFile).replace(".", "");
|
|
1351
1418
|
const telepathicQuery = `${basename} ${dirContext} ${ext} patterns solutions architecture`;
|
|
1352
1419
|
try {
|
|
1353
|
-
const
|
|
1354
|
-
const results =
|
|
1420
|
+
const brain = (0, TheBrainV2_1.getBrain)();
|
|
1421
|
+
const results = brain.search(telepathicQuery, limit);
|
|
1355
1422
|
if (results.length === 0) {
|
|
1356
1423
|
return {
|
|
1357
1424
|
content: [
|
|
@@ -1365,11 +1432,8 @@ async function handleGetTelepathicHints(args) {
|
|
|
1365
1432
|
let hintsText = `Telepathic Hints for \`${activeFile}\`\n*(${results.length} relevant memories surfaced from The Brain)*\n\n`;
|
|
1366
1433
|
results.forEach((r, i) => {
|
|
1367
1434
|
const similarity = (r.similarity * 100).toFixed(1);
|
|
1368
|
-
const prompt =
|
|
1369
|
-
const responseContent =
|
|
1370
|
-
? r.response
|
|
1371
|
-
: r.response?.choices?.[0]?.message?.content ||
|
|
1372
|
-
JSON.stringify(r.response).substring(0, 300);
|
|
1435
|
+
const prompt = r.query || "Unknown";
|
|
1436
|
+
const responseContent = r.response || "";
|
|
1373
1437
|
hintsText += `### Hint ${i + 1} (${similarity}% match)\n`;
|
|
1374
1438
|
hintsText += `**Memory:** ${prompt}\n\n`;
|
|
1375
1439
|
hintsText += `${responseContent.substring(0, 400)}${responseContent.length > 400 ? "..." : ""}\n\n---\n\n`;
|
|
@@ -1389,7 +1453,7 @@ async function handleGetTelepathicHints(args) {
|
|
|
1389
1453
|
content: [
|
|
1390
1454
|
{
|
|
1391
1455
|
type: "text",
|
|
1392
|
-
text: `Telepathic Hints failed: ${e.message}
|
|
1456
|
+
text: `Telepathic Hints failed: ${e.message}`,
|
|
1393
1457
|
},
|
|
1394
1458
|
],
|
|
1395
1459
|
};
|
|
@@ -1401,17 +1465,8 @@ async function handleSummarizeLongText(args) {
|
|
|
1401
1465
|
const maxLength = args?.maxLength || 500;
|
|
1402
1466
|
if (!text)
|
|
1403
1467
|
throw new Error("text is required");
|
|
1404
|
-
const host = process.env.OLLAMA_HOST || 'http://localhost:11434';
|
|
1405
|
-
const model = process.env.OLLAMA_GEN_MODEL || 'llama3';
|
|
1406
1468
|
try {
|
|
1407
|
-
const
|
|
1408
|
-
const resp = await axios_1.default.post(`${host}/api/generate`, {
|
|
1409
|
-
model,
|
|
1410
|
-
prompt,
|
|
1411
|
-
stream: false,
|
|
1412
|
-
options: { temperature: 0.1, num_predict: maxLength * 2 },
|
|
1413
|
-
});
|
|
1414
|
-
const summary = resp.data.response.trim();
|
|
1469
|
+
const summary = (0, ContextCollapser_1.collapseContext)(text, { maxLength });
|
|
1415
1470
|
(0, reportSavings_1.reportSavings)({
|
|
1416
1471
|
source: "contextSqueeze",
|
|
1417
1472
|
charsBefore: text.length,
|
|
@@ -1422,7 +1477,7 @@ async function handleSummarizeLongText(args) {
|
|
|
1422
1477
|
}
|
|
1423
1478
|
catch (e) {
|
|
1424
1479
|
(0, utils_1.logError)("summarize_long_text", e);
|
|
1425
|
-
return { content: [{ type: "text", text: `Summarization failed: ${e.message}
|
|
1480
|
+
return { content: [{ type: "text", text: `Summarization failed: ${e.message}` }] };
|
|
1426
1481
|
}
|
|
1427
1482
|
}
|
|
1428
1483
|
// ── Prune Conversation History ──────────────────────────────────────
|
|
@@ -1571,21 +1626,14 @@ async function handleCompressContext(args) {
|
|
|
1571
1626
|
const recent = messages.slice(-keepRecent);
|
|
1572
1627
|
const old = messages.slice(0, -keepRecent);
|
|
1573
1628
|
let summary = "";
|
|
1574
|
-
let summaryMethod = "
|
|
1629
|
+
let summaryMethod = "local-collapser";
|
|
1575
1630
|
try {
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
const summaryPrompt = `Summarize the following conversation history into 2-3 concise sentences. Capture ONLY: key decisions made, code patterns established, unresolved issues. Be extremely brief.\n\n${JSON.stringify(old.map((m) => ({ role: m.role, content: typeof m.content === 'string' ? m.content.substring(0, 400) : '...' })))}`;
|
|
1579
|
-
const resp = await axios_1.default.post(`${host}/api/generate`, {
|
|
1580
|
-
model, prompt: summaryPrompt, stream: false,
|
|
1581
|
-
options: { temperature: 0.1, num_predict: 300 },
|
|
1582
|
-
});
|
|
1583
|
-
summary = resp.data.response.trim();
|
|
1584
|
-
summaryMethod = "ollama";
|
|
1631
|
+
const oldText = old.map((m, i) => `[Turn ${i + 1} / ${m.role}]: ${typeof m.content === 'string' ? m.content : '...'}`).join('\n');
|
|
1632
|
+
summary = (0, ContextCollapser_1.collapseContext)(oldText, { maxLength: 800 });
|
|
1585
1633
|
}
|
|
1586
|
-
catch {
|
|
1634
|
+
catch (err) {
|
|
1587
1635
|
summary = old.map((m, i) => `[Turn ${i + 1} / ${m.role}]: ${typeof m.content === 'string' ? m.content.substring(0, 150).replace(/\n/g, ' ') : '...'}`).join('\n').substring(0, 2000);
|
|
1588
|
-
summaryMethod = "truncate";
|
|
1636
|
+
summaryMethod = "truncate-fallback";
|
|
1589
1637
|
}
|
|
1590
1638
|
const oldLength = JSON.stringify(old).length;
|
|
1591
1639
|
const summaryLength = summary.length;
|
|
@@ -1613,16 +1661,13 @@ async function handleSmarterCache(args) {
|
|
|
1613
1661
|
const threshold = typeof args?.threshold === "number" ? args.threshold : 0.75;
|
|
1614
1662
|
if (!query)
|
|
1615
1663
|
throw new Error("query is required");
|
|
1616
|
-
const port = (0, utils_1.getProxyPort)();
|
|
1617
1664
|
const fullQuery = context ? `${query}\n\nContext: ${context}` : query;
|
|
1618
1665
|
try {
|
|
1619
|
-
const
|
|
1620
|
-
const results =
|
|
1666
|
+
const brain = (0, TheBrainV2_1.getBrain)();
|
|
1667
|
+
const results = brain.search(fullQuery, 5);
|
|
1621
1668
|
const topHit = results[0];
|
|
1622
1669
|
if (topHit && topHit.similarity >= threshold) {
|
|
1623
|
-
const responseText =
|
|
1624
|
-
? topHit.response
|
|
1625
|
-
: topHit.response?.choices?.[0]?.message?.content || JSON.stringify(topHit.response, null, 2);
|
|
1670
|
+
const responseText = topHit.response;
|
|
1626
1671
|
const tokensSaved = Math.floor(responseText.length / 4);
|
|
1627
1672
|
try {
|
|
1628
1673
|
const { reportSavings } = await Promise.resolve().then(() => __importStar(require("../utils/reportSavings")));
|
|
@@ -1667,7 +1712,7 @@ async function handleSmarterCache(args) {
|
|
|
1667
1712
|
hit: false,
|
|
1668
1713
|
error: e.message,
|
|
1669
1714
|
source: "cache-unavailable",
|
|
1670
|
-
hint: "
|
|
1715
|
+
hint: "El cache local del brain falló. El LLM funcionará normalmente sin cache.",
|
|
1671
1716
|
}, null, 2),
|
|
1672
1717
|
}],
|
|
1673
1718
|
};
|
|
@@ -2320,10 +2365,9 @@ async function handleSemanticDedupGuard(args) {
|
|
|
2320
2365
|
const threshold = typeof args?.threshold === "number" ? args.threshold : 0.92;
|
|
2321
2366
|
if (!query || !response)
|
|
2322
2367
|
throw new Error("query and response are required");
|
|
2323
|
-
const port = (0, utils_1.getProxyPort)();
|
|
2324
2368
|
try {
|
|
2325
|
-
const
|
|
2326
|
-
const results =
|
|
2369
|
+
const brain = (0, TheBrainV2_1.getBrain)();
|
|
2370
|
+
const results = brain.search(query, 3);
|
|
2327
2371
|
const topHit = results[0];
|
|
2328
2372
|
if (!topHit) {
|
|
2329
2373
|
return {
|
|
@@ -2334,17 +2378,18 @@ async function handleSemanticDedupGuard(args) {
|
|
|
2334
2378
|
}, null, 2) }],
|
|
2335
2379
|
};
|
|
2336
2380
|
}
|
|
2337
|
-
|
|
2381
|
+
let sim = topHit.similarity || 0;
|
|
2382
|
+
if (topHit.query.trim().toLowerCase() === query.trim().toLowerCase()) {
|
|
2383
|
+
sim = 1.0;
|
|
2384
|
+
}
|
|
2338
2385
|
if (sim >= threshold) {
|
|
2339
2386
|
return {
|
|
2340
2387
|
content: [{ type: "text", text: JSON.stringify({
|
|
2341
2388
|
verdict: "DUPLICATE_REJECTED",
|
|
2342
2389
|
reason: `Duplicado exacto detectado (${(sim * 100).toFixed(1)}% ≥ ${(threshold * 100).toFixed(0)}%). NO hagas store_memory — ya existe.`,
|
|
2343
2390
|
similarity: sim,
|
|
2344
|
-
existingQuery: typeof topHit.
|
|
2345
|
-
existingResponse: (
|
|
2346
|
-
? topHit.response
|
|
2347
|
-
: topHit.response?.choices?.[0]?.message?.content || "").substring(0, 300),
|
|
2391
|
+
existingQuery: typeof topHit.query === "string" ? topHit.query.substring(0, 200) : "N/A",
|
|
2392
|
+
existingResponse: (topHit.response || "").substring(0, 300),
|
|
2348
2393
|
tokensSaved: Math.floor(response.length / 4),
|
|
2349
2394
|
}, null, 2) }],
|
|
2350
2395
|
};
|
|
@@ -2355,7 +2400,7 @@ async function handleSemanticDedupGuard(args) {
|
|
|
2355
2400
|
verdict: "SIMILAR_WARNING",
|
|
2356
2401
|
reason: `Contenido similar detectado (${(sim * 100).toFixed(1)}%). Decide si es distinto suficiente para guardar.`,
|
|
2357
2402
|
similarity: sim,
|
|
2358
|
-
existingQuery: typeof topHit.
|
|
2403
|
+
existingQuery: typeof topHit.query === "string" ? topHit.query.substring(0, 200) : "N/A",
|
|
2359
2404
|
recommendation: "Si aporta info nueva → procede con store_memory. Si es redundante → descarta.",
|
|
2360
2405
|
}, null, 2) }],
|
|
2361
2406
|
};
|
|
@@ -2373,7 +2418,7 @@ async function handleSemanticDedupGuard(args) {
|
|
|
2373
2418
|
return {
|
|
2374
2419
|
content: [{ type: "text", text: JSON.stringify({
|
|
2375
2420
|
verdict: "STORE_ALLOWED",
|
|
2376
|
-
reason: "
|
|
2421
|
+
reason: "El cache local del brain falló. Procediendo con store.",
|
|
2377
2422
|
error: e.message,
|
|
2378
2423
|
}, null, 2) }],
|
|
2379
2424
|
};
|
|
@@ -3279,4 +3324,185 @@ async function handleRefactor(args) {
|
|
|
3279
3324
|
return { content: [{ type: "text", text: `refactor failed: ${err.message}` }] };
|
|
3280
3325
|
}
|
|
3281
3326
|
}
|
|
3327
|
+
// ── Surgical Symbol Context ──────────────────────────────────────────
|
|
3328
|
+
async function handleGetSymbolSurgicalContext(args) {
|
|
3329
|
+
const filePath = args?.filePath;
|
|
3330
|
+
const symbolName = args?.symbolName;
|
|
3331
|
+
if (!filePath || !symbolName)
|
|
3332
|
+
throw new Error("filePath and symbolName are required");
|
|
3333
|
+
const workspaceRoot = process.cwd();
|
|
3334
|
+
try {
|
|
3335
|
+
const res = (0, SymbolSurgicalContext_1.getSymbolSurgicalContext)(workspaceRoot, filePath, symbolName);
|
|
3336
|
+
const lines = [];
|
|
3337
|
+
lines.push(`# 🧬 Surgical Context: Symbol \`${symbolName}\` in \`${filePath}\``);
|
|
3338
|
+
lines.push(`## Implementation`);
|
|
3339
|
+
lines.push(`\`\`\`typescript\n${res.implementation}\n\`\`\``);
|
|
3340
|
+
if (res.dependencies.length > 0) {
|
|
3341
|
+
lines.push(`## Workspace Dependencies (Signatures ONLY)`);
|
|
3342
|
+
for (const dep of res.dependencies) {
|
|
3343
|
+
lines.push(`### \`${dep.name}\` (${dep.type}) declared in \`${dep.filePath}\``);
|
|
3344
|
+
lines.push(`\`\`\`typescript\n${dep.signature}\n\`\`\``);
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
else {
|
|
3348
|
+
lines.push(`*No local workspace dependencies detected for this symbol.*`);
|
|
3349
|
+
}
|
|
3350
|
+
const output = lines.join("\n\n");
|
|
3351
|
+
const originalSize = fs_1.default.readFileSync(path_1.default.resolve(workspaceRoot, filePath), "utf8").length;
|
|
3352
|
+
const totalRawSize = originalSize + res.dependencies.reduce((sum, dep) => {
|
|
3353
|
+
try {
|
|
3354
|
+
return sum + fs_1.default.readFileSync(path_1.default.resolve(workspaceRoot, dep.filePath), "utf8").length;
|
|
3355
|
+
}
|
|
3356
|
+
catch {
|
|
3357
|
+
return sum;
|
|
3358
|
+
}
|
|
3359
|
+
}, 0);
|
|
3360
|
+
(0, reportSavings_1.reportSavings)({
|
|
3361
|
+
source: "contextSqueeze",
|
|
3362
|
+
charsBefore: totalRawSize,
|
|
3363
|
+
charsAfter: output.length,
|
|
3364
|
+
toolName: "get_symbol_surgical_context",
|
|
3365
|
+
filePath
|
|
3366
|
+
});
|
|
3367
|
+
return {
|
|
3368
|
+
content: [{ type: "text", text: output }]
|
|
3369
|
+
};
|
|
3370
|
+
}
|
|
3371
|
+
catch (err) {
|
|
3372
|
+
(0, utils_1.logError)("get_symbol_surgical_context", err);
|
|
3373
|
+
return {
|
|
3374
|
+
content: [{ type: "text", text: `Surgical context extraction failed: ${err.message}` }]
|
|
3375
|
+
};
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
// ── Wormhole Squeeze ────────────────────────────────────────────────
|
|
3379
|
+
async function handleWormholeSqueeze(args) {
|
|
3380
|
+
const text = args?.text;
|
|
3381
|
+
if (!text)
|
|
3382
|
+
throw new Error("text is required");
|
|
3383
|
+
try {
|
|
3384
|
+
const compressed = (0, WormholeCompressor_1.compressWormhole)(text);
|
|
3385
|
+
(0, reportSavings_1.reportSavings)({
|
|
3386
|
+
source: "contextSqueeze",
|
|
3387
|
+
charsBefore: text.length,
|
|
3388
|
+
charsAfter: compressed.length,
|
|
3389
|
+
toolName: "wormhole_squeeze"
|
|
3390
|
+
});
|
|
3391
|
+
return {
|
|
3392
|
+
content: [{ type: "text", text: compressed }]
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
catch (err) {
|
|
3396
|
+
(0, utils_1.logError)("wormhole_squeeze", err);
|
|
3397
|
+
return {
|
|
3398
|
+
content: [{ type: "text", text: `Wormhole compression failed: ${err.message}` }]
|
|
3399
|
+
};
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
// ── Executive ROI Report ──────────────────────────────────────────────
|
|
3403
|
+
async function handleGenerateExecutiveRoiReport(args) {
|
|
3404
|
+
const teamSize = typeof args?.teamSize === "number" ? args.teamSize : 10;
|
|
3405
|
+
const costPerDevHour = typeof args?.costPerDevHour === "number" ? args.costPerDevHour : 50;
|
|
3406
|
+
const targetPath = args?.targetPath || "LEMMA_ROI_REPORT.md";
|
|
3407
|
+
const workspaceRoot = process.cwd();
|
|
3408
|
+
try {
|
|
3409
|
+
const res = (0, ExecutiveReport_1.generateExecutiveReport)(workspaceRoot, {
|
|
3410
|
+
teamSize,
|
|
3411
|
+
costPerDevHour,
|
|
3412
|
+
targetPath
|
|
3413
|
+
});
|
|
3414
|
+
return {
|
|
3415
|
+
content: [{
|
|
3416
|
+
type: "text",
|
|
3417
|
+
text: `Success: ROI Report generated at ${targetPath}.\n\nTotal Business Value Saved: $${res.metrics.totalBusinessValue.toFixed(2)} USD\nEstimated Dev Time Saved: ${res.metrics.devHoursSaved} Hours\nDirect API Cost Saved: $${res.metrics.totalCostSaved.toFixed(4)} USD`
|
|
3418
|
+
}]
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
catch (err) {
|
|
3422
|
+
(0, utils_1.logError)("generate_executive_roi_report", err);
|
|
3423
|
+
return {
|
|
3424
|
+
content: [{ type: "text", text: `ROI Report generation failed: ${err.message}` }]
|
|
3425
|
+
};
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
// ── Surgical AST Insert ──────────────────────────────────────────────
|
|
3429
|
+
async function handleSurgicalAstInsert(args) {
|
|
3430
|
+
const filePath = args?.filePath;
|
|
3431
|
+
const symbolName = args?.symbolName;
|
|
3432
|
+
const anchorSymbol = args?.anchorSymbol;
|
|
3433
|
+
const insertPosition = args?.insertPosition;
|
|
3434
|
+
const codeToInsert = args?.codeToInsert;
|
|
3435
|
+
if (!filePath || !codeToInsert)
|
|
3436
|
+
throw new Error("filePath and codeToInsert are required");
|
|
3437
|
+
const workspaceRoot = process.cwd();
|
|
3438
|
+
try {
|
|
3439
|
+
const resolvedPath = (0, SurgicalASTInsert_1.surgicalASTInsert)(workspaceRoot, {
|
|
3440
|
+
filePath,
|
|
3441
|
+
symbolName,
|
|
3442
|
+
anchorSymbol,
|
|
3443
|
+
insertPosition,
|
|
3444
|
+
codeToInsert
|
|
3445
|
+
});
|
|
3446
|
+
const newTextSize = fs_1.default.readFileSync(resolvedPath, "utf8").length;
|
|
3447
|
+
const outputCharsSaved = Math.max(0, newTextSize - codeToInsert.length);
|
|
3448
|
+
const tokensSaved = Math.floor(outputCharsSaved / 4);
|
|
3449
|
+
(0, reportSavings_1.reportSavings)({
|
|
3450
|
+
source: "contextSqueeze",
|
|
3451
|
+
tokens: tokensSaved,
|
|
3452
|
+
toolName: "surgical_ast_insert",
|
|
3453
|
+
filePath
|
|
3454
|
+
});
|
|
3455
|
+
return {
|
|
3456
|
+
content: [{
|
|
3457
|
+
type: "text",
|
|
3458
|
+
text: `Success: Surgically inserted code into ${filePath} using AST compiler.`
|
|
3459
|
+
}]
|
|
3460
|
+
};
|
|
3461
|
+
}
|
|
3462
|
+
catch (err) {
|
|
3463
|
+
(0, utils_1.logError)("surgical_ast_insert", err);
|
|
3464
|
+
return {
|
|
3465
|
+
content: [{ type: "text", text: `Surgical AST insertion failed: ${err.message}` }]
|
|
3466
|
+
};
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
// ── Local Semantic Autofix ────────────────────────────────────────────
|
|
3470
|
+
async function handleLocalSemanticAutofix(args) {
|
|
3471
|
+
const errorMessage = args?.errorMessage;
|
|
3472
|
+
if (!errorMessage)
|
|
3473
|
+
throw new Error("errorMessage is required");
|
|
3474
|
+
try {
|
|
3475
|
+
const brain = (0, TheBrainV2_1.getBrain)();
|
|
3476
|
+
const results = brain.search(errorMessage, 3, 0.70);
|
|
3477
|
+
const topHit = results[0];
|
|
3478
|
+
if (topHit) {
|
|
3479
|
+
const responseText = topHit.response;
|
|
3480
|
+
const tokensSaved = Math.max(100, Math.floor(responseText.length / 4));
|
|
3481
|
+
(0, reportSavings_1.reportSavings)({
|
|
3482
|
+
source: "cache",
|
|
3483
|
+
tokens: tokensSaved,
|
|
3484
|
+
toolName: "local_semantic_autofix",
|
|
3485
|
+
query: errorMessage.substring(0, 100)
|
|
3486
|
+
});
|
|
3487
|
+
return {
|
|
3488
|
+
content: [{
|
|
3489
|
+
type: "text",
|
|
3490
|
+
text: `🎯 Local Auto-Fix HIT (similarity: ${(topHit.similarity * 100).toFixed(1)}%)\n\nFound matching resolution in global memory. Cost: 0 tokens.\n\n---\n\n${responseText}`
|
|
3491
|
+
}]
|
|
3492
|
+
};
|
|
3493
|
+
}
|
|
3494
|
+
return {
|
|
3495
|
+
content: [{
|
|
3496
|
+
type: "text",
|
|
3497
|
+
text: `Brain MISS (No matching resolutions for this error in local memory).\n\nPlease proceed to ask the LLM for guidance. Once resolved, make sure to call store_memory with:\n- query: "[Error description/traceback]"\n- response: "[Exact code patch or steps used to fix]"\n\nThis will allow resolving this error instantly with 0 tokens next time!`
|
|
3498
|
+
}]
|
|
3499
|
+
};
|
|
3500
|
+
}
|
|
3501
|
+
catch (err) {
|
|
3502
|
+
(0, utils_1.logError)("local_semantic_autofix", err);
|
|
3503
|
+
return {
|
|
3504
|
+
content: [{ type: "text", text: `Local auto-fix failed: ${err.message}` }]
|
|
3505
|
+
};
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3282
3508
|
//# sourceMappingURL=tools.js.map
|