@nxuss/lemma 1.11.0 → 1.12.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 +92 -819
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +14 -0
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/infra/mcp-tools.d.ts +7 -0
- package/dist/cjs/infra/mcp-tools.d.ts.map +1 -1
- package/dist/cjs/infra/mcp-tools.js +22 -0
- package/dist/cjs/infra/mcp-tools.js.map +1 -1
- package/dist/cjs/mcp/index.js +32 -7
- package/dist/cjs/mcp/index.js.map +1 -1
- package/dist/cjs/mcp/prompts.d.ts +1 -0
- package/dist/cjs/mcp/prompts.d.ts.map +1 -1
- package/dist/cjs/mcp/prompts.js +98 -0
- package/dist/cjs/mcp/prompts.js.map +1 -1
- package/dist/cjs/mcp/resources.d.ts +2 -0
- package/dist/cjs/mcp/resources.d.ts.map +1 -1
- package/dist/cjs/mcp/resources.js +26 -0
- package/dist/cjs/mcp/resources.js.map +1 -1
- package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/cjs/mcp/tool-profiles.js +29 -50
- package/dist/cjs/mcp/tool-profiles.js.map +1 -1
- package/dist/cjs/mcp/tools.d.ts.map +1 -1
- package/dist/cjs/mcp/tools.js +285 -57
- package/dist/cjs/mcp/tools.js.map +1 -1
- package/dist/cjs/proxy/Gatekeeper.d.ts +29 -0
- package/dist/cjs/proxy/Gatekeeper.d.ts.map +1 -1
- package/dist/cjs/proxy/Gatekeeper.js +122 -1
- package/dist/cjs/proxy/Gatekeeper.js.map +1 -1
- package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +14 -0
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/infra/mcp-tools.d.ts +7 -0
- package/dist/esm/infra/mcp-tools.d.ts.map +1 -1
- package/dist/esm/infra/mcp-tools.js +22 -0
- package/dist/esm/infra/mcp-tools.js.map +1 -1
- package/dist/esm/mcp/index.js +34 -9
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/mcp/prompts.d.ts +1 -0
- package/dist/esm/mcp/prompts.d.ts.map +1 -1
- package/dist/esm/mcp/prompts.js +95 -1
- package/dist/esm/mcp/prompts.js.map +1 -1
- package/dist/esm/mcp/resources.d.ts +2 -0
- package/dist/esm/mcp/resources.d.ts.map +1 -1
- package/dist/esm/mcp/resources.js +27 -2
- package/dist/esm/mcp/resources.js.map +1 -1
- package/dist/esm/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/esm/mcp/tool-profiles.js +29 -50
- package/dist/esm/mcp/tool-profiles.js.map +1 -1
- package/dist/esm/mcp/tools.d.ts.map +1 -1
- package/dist/esm/mcp/tools.js +285 -57
- package/dist/esm/mcp/tools.js.map +1 -1
- package/dist/esm/proxy/Gatekeeper.d.ts +29 -0
- package/dist/esm/proxy/Gatekeeper.d.ts.map +1 -1
- package/dist/esm/proxy/Gatekeeper.js +120 -0
- package/dist/esm/proxy/Gatekeeper.js.map +1 -1
- package/package.json +2 -2
package/dist/cjs/mcp/tools.js
CHANGED
|
@@ -135,9 +135,55 @@ const PRO_GATE_MESSAGE = [
|
|
|
135
135
|
"🚀 Get Lemma Pro:",
|
|
136
136
|
" https://lemma.nxus.studio/upgrade",
|
|
137
137
|
].join("\n");
|
|
138
|
+
// ── Elicitation (spec 2026-07-28's recommended pattern for destructive actions) ────
|
|
139
|
+
//
|
|
140
|
+
// Set once by setupToolsHandlers so the standalone handler functions below — which don't
|
|
141
|
+
// otherwise see the Server instance — can ask the connected client to confirm before a
|
|
142
|
+
// mutating tool runs.
|
|
143
|
+
let mcpServerRef = null;
|
|
144
|
+
/**
|
|
145
|
+
* Ask the client to confirm a destructive action before it happens.
|
|
146
|
+
*
|
|
147
|
+
* Mirrors the defensive shape of trySamplingContext() in src/mcp/index.ts: most clients
|
|
148
|
+
* today don't support elicitation, so any failure (unsupported capability, timeout,
|
|
149
|
+
* malformed response) must fall back to "proceed without confirmation" rather than block
|
|
150
|
+
* or error out the tool. This is a courtesy prompt for clients that support it, not a
|
|
151
|
+
* security boundary — the allowlist/path-safety checks each handler already does are that.
|
|
152
|
+
*/
|
|
153
|
+
async function tryElicitConfirmation(summary) {
|
|
154
|
+
if (!mcpServerRef)
|
|
155
|
+
return { supported: false, confirmed: true };
|
|
156
|
+
try {
|
|
157
|
+
const result = await mcpServerRef.elicitInput({
|
|
158
|
+
message: summary,
|
|
159
|
+
requestedSchema: {
|
|
160
|
+
type: "object",
|
|
161
|
+
properties: {
|
|
162
|
+
confirm: {
|
|
163
|
+
type: "boolean",
|
|
164
|
+
title: "Proceed?",
|
|
165
|
+
description: "Confirm this action should be applied.",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
required: ["confirm"],
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
if (result.action !== "accept") {
|
|
172
|
+
// "decline" or "cancel" — the user (or client policy) said no.
|
|
173
|
+
return { supported: true, confirmed: false };
|
|
174
|
+
}
|
|
175
|
+
const confirmed = result.content?.confirm !== false;
|
|
176
|
+
return { supported: true, confirmed };
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
(0, utils_1.logWarn)("elicitation", "Client does not support elicitation (or the request failed) — proceeding without confirmation");
|
|
180
|
+
return { supported: false, confirmed: true };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
138
183
|
const toolDefinitions = [
|
|
139
184
|
{
|
|
140
185
|
name: "scrub_privacy",
|
|
186
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
141
187
|
description: "Mask sensitive data (PII, API Keys, Credentials) from a text block using Lemma's Privacy Firewall.",
|
|
142
188
|
inputSchema: {
|
|
143
189
|
type: "object",
|
|
@@ -149,6 +195,7 @@ const toolDefinitions = [
|
|
|
149
195
|
},
|
|
150
196
|
{
|
|
151
197
|
name: "search_memory",
|
|
198
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
152
199
|
description: "Search Lemma's semantic memory (The Brain) before investigating something from scratch — retrieves past solutions, fixes, and context from ALL your projects globally. Results are split into 'fresh' (safe to reuse — either untracked general knowledge, or every file it depended on still hashes the same) and 'stale' (a similar question was answered before, but a tracked file changed since — re-verify against current state before reusing). Never treat a stale result as current.",
|
|
153
200
|
inputSchema: {
|
|
154
201
|
type: "object",
|
|
@@ -166,6 +213,7 @@ const toolDefinitions = [
|
|
|
166
213
|
},
|
|
167
214
|
{
|
|
168
215
|
name: "store_memory",
|
|
216
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
169
217
|
description: "Persist a technical solution, bug fix, architecture decision, or key fact into Lemma's Brain — so future questions on the same topic (even phrased differently) don't require re-investigating the repo. Pass filePaths for anything derived from specific files (e.g. 'where is X implemented', 'how does Y work') so the memory auto-invalidates the moment those files change, instead of risking a stale answer being reused. If the answer is really about one function/class rather than the whole file, pass `symbols` instead (or in addition) so an unrelated edit elsewhere in that file doesn't stale it out. Pass outcome='failed' for an approach that was tried and did NOT work — that's just as worth remembering as a fix, so the Brain can warn 'already tried, didn't work' instead of only ever suggesting reuse.",
|
|
170
218
|
inputSchema: {
|
|
171
219
|
type: "object",
|
|
@@ -217,6 +265,7 @@ const toolDefinitions = [
|
|
|
217
265
|
},
|
|
218
266
|
{
|
|
219
267
|
name: "get_routing_advice",
|
|
268
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
220
269
|
description: "Analyzes a prompt and suggests the best model based on Lemma's Complexity Router.",
|
|
221
270
|
inputSchema: {
|
|
222
271
|
type: "object",
|
|
@@ -226,9 +275,20 @@ const toolDefinitions = [
|
|
|
226
275
|
},
|
|
227
276
|
required: ["prompt"],
|
|
228
277
|
},
|
|
278
|
+
outputSchema: {
|
|
279
|
+
type: "object",
|
|
280
|
+
properties: {
|
|
281
|
+
recommendedModel: { type: "string", description: "The model the router recommends" },
|
|
282
|
+
complexity: { type: "string", enum: ["low", "high"], description: "Estimated complexity of the prompt" },
|
|
283
|
+
intendedModel: { type: ["string", "null"], description: "The model passed in intended_model, or null if omitted" },
|
|
284
|
+
switched: { type: "boolean", description: "true if recommendedModel differs from intendedModel" },
|
|
285
|
+
},
|
|
286
|
+
required: ["recommendedModel", "complexity", "switched"],
|
|
287
|
+
},
|
|
229
288
|
},
|
|
230
289
|
{
|
|
231
290
|
name: "auto_heal",
|
|
291
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
232
292
|
description: "Diagnose and auto-heal the latest local server crash registered in Lemma's context logs.",
|
|
233
293
|
inputSchema: {
|
|
234
294
|
type: "object",
|
|
@@ -239,6 +299,7 @@ const toolDefinitions = [
|
|
|
239
299
|
},
|
|
240
300
|
{
|
|
241
301
|
name: "read_workspace_file",
|
|
302
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
242
303
|
description: "Read a file inside the workspace. Compresses comments/whitespace and scrubs API keys. Every line carries its REAL line number in the original file (compression removes lines, it never renumbers them), so you can cite file:line and patch precisely without re-reading. The header lists which imports were compressed away. Use offset/limit to read a line range of a large file. If this exact view was already read this session and hasn't changed on disk, returns an UNCHANGED marker plus a symbol outline. If it changed only slightly, returns a CHANGED marker with a unified diff to apply to the copy you already hold. Either way, pass force:true to get the full file when it is no longer in your context.",
|
|
243
304
|
inputSchema: {
|
|
244
305
|
type: "object",
|
|
@@ -255,6 +316,7 @@ const toolDefinitions = [
|
|
|
255
316
|
},
|
|
256
317
|
{
|
|
257
318
|
name: "write_workspace_file",
|
|
319
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
258
320
|
description: "Write full contents to a file inside the workspace. Creates parent directories automatically.",
|
|
259
321
|
inputSchema: {
|
|
260
322
|
type: "object",
|
|
@@ -267,6 +329,7 @@ const toolDefinitions = [
|
|
|
267
329
|
},
|
|
268
330
|
{
|
|
269
331
|
name: "create_workspace_file",
|
|
332
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
270
333
|
description: "Create a NEW file in the workspace. Fails immediately if the file already exists — use write_workspace_file or apply_workspace_patch to modify existing files. Creates parent directories automatically. Returns a minimal token-free ACK: no file content is echoed back, saving provider output tokens.",
|
|
271
334
|
inputSchema: {
|
|
272
335
|
type: "object",
|
|
@@ -279,6 +342,7 @@ const toolDefinitions = [
|
|
|
279
342
|
},
|
|
280
343
|
{
|
|
281
344
|
name: "apply_workspace_patch",
|
|
345
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
282
346
|
description: "Apply a smart search-and-replace patch to an existing file. Falls back through whitespace-normalized, indent-anchored, and fuzzy matching if the exact block isn't found — so a searchContent built from a compacted read still applies. Safe against duplicate matches. On total failure, the error includes a diff against the closest candidate block.",
|
|
283
347
|
inputSchema: {
|
|
284
348
|
type: "object",
|
|
@@ -292,6 +356,7 @@ const toolDefinitions = [
|
|
|
292
356
|
},
|
|
293
357
|
{
|
|
294
358
|
name: "run_workspace_command",
|
|
359
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
295
360
|
description: "Execute a bash command in the workspace root. Default timeout 120s — raise it with timeoutMs for a full test or build run (max 600s). Captures both stdout and stderr, and returns whatever was produced even if the command times out. Long test/build/lint output is distilled deterministically — for jest, vitest and tsc it returns the failing tests or diagnostics with their locations and reasons, dropping code frames and node_modules stack frames; anything else falls back to head/tail plus error-matching lines. The complete output is always stored first and the reply carries a handle: nothing is lost, use output_region to retrieve any part verbatim. Pass raw:true to skip distillation.",
|
|
296
361
|
inputSchema: {
|
|
297
362
|
type: "object",
|
|
@@ -313,6 +378,7 @@ const toolDefinitions = [
|
|
|
313
378
|
},
|
|
314
379
|
{
|
|
315
380
|
name: "output_region",
|
|
381
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
316
382
|
description: "Retrieve any part of a command output previously stored by run_workspace_command, verbatim. This is what makes distillation lossless — use it when the distilled view isn't enough. Pass all:true for the whole output, section for one named block (a failing test title or a file path, as listed in the distilled view), or startLine/endLine for a range.",
|
|
317
383
|
inputSchema: {
|
|
318
384
|
type: "object",
|
|
@@ -328,6 +394,7 @@ const toolDefinitions = [
|
|
|
328
394
|
},
|
|
329
395
|
{
|
|
330
396
|
name: "list_workspace_dir",
|
|
397
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
331
398
|
description: "List files and subdirectories recursively to navigate the repository structure.",
|
|
332
399
|
inputSchema: {
|
|
333
400
|
type: "object",
|
|
@@ -339,6 +406,7 @@ const toolDefinitions = [
|
|
|
339
406
|
},
|
|
340
407
|
{
|
|
341
408
|
name: "search_workspace",
|
|
409
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
342
410
|
description: "Perform a fast local text search (grep) across all files in the project workspace. Respects .gitignore, skips binaries and files over 500KB, groups results by file (capped per file), and stops at maxResults. For code files (.ts/.tsx/.js/.jsx), matches inside the same function/class/method are collapsed into one entry with its signature and line range instead of raw duplicate lines. Repeating an identical query while the workspace is unchanged returns the cached result instantly instead of re-scanning.",
|
|
343
411
|
inputSchema: {
|
|
344
412
|
type: "object",
|
|
@@ -352,6 +420,7 @@ const toolDefinitions = [
|
|
|
352
420
|
},
|
|
353
421
|
{
|
|
354
422
|
name: "squeeze_prompt",
|
|
423
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
355
424
|
description: "Compress code blocks, comments, and boilerplate in any prompt. Saves up to 80% tokens.",
|
|
356
425
|
inputSchema: {
|
|
357
426
|
type: "object",
|
|
@@ -364,6 +433,7 @@ const toolDefinitions = [
|
|
|
364
433
|
},
|
|
365
434
|
{
|
|
366
435
|
name: "get_project_onboarding",
|
|
436
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
367
437
|
description: "Fetch dynamic architectural and stack overview of the current project in markdown.",
|
|
368
438
|
inputSchema: {
|
|
369
439
|
type: "object",
|
|
@@ -372,6 +442,7 @@ const toolDefinitions = [
|
|
|
372
442
|
},
|
|
373
443
|
{
|
|
374
444
|
name: "get_project_history",
|
|
445
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
375
446
|
description: "Answers 'what have we done in this project': merges recent git commits, session checkpoints, and The Brain's memories (decisions, fixes, prior PR reviews) for the current project into one narrative. Use this instead of piecing the same picture together from search_memory + git log + reading checkpoint files separately.",
|
|
376
447
|
inputSchema: {
|
|
377
448
|
type: "object",
|
|
@@ -382,6 +453,7 @@ const toolDefinitions = [
|
|
|
382
453
|
},
|
|
383
454
|
{
|
|
384
455
|
name: "get_ast_hologram",
|
|
456
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
385
457
|
description: "Generate a dense, token-efficient Holographic AST Map of the workspace using the TypeScript compiler. Returns structured JSON of all exported symbols with file paths and line numbers.",
|
|
386
458
|
inputSchema: {
|
|
387
459
|
type: "object",
|
|
@@ -398,6 +470,7 @@ const toolDefinitions = [
|
|
|
398
470
|
},
|
|
399
471
|
{
|
|
400
472
|
name: "validate_patch_sandbox",
|
|
473
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
401
474
|
description: "Validate proposed code in isolated sandbox via tsc + syntax check before applying.",
|
|
402
475
|
inputSchema: {
|
|
403
476
|
type: "object",
|
|
@@ -410,6 +483,7 @@ const toolDefinitions = [
|
|
|
410
483
|
},
|
|
411
484
|
{
|
|
412
485
|
name: "query_hybrid_consensus",
|
|
486
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
413
487
|
description: "Search The Brain before reasoning. Brain HIT returns cached answer instantly. Brain MISS proceeds to cloud.",
|
|
414
488
|
inputSchema: {
|
|
415
489
|
type: "object",
|
|
@@ -423,6 +497,7 @@ const toolDefinitions = [
|
|
|
423
497
|
},
|
|
424
498
|
{
|
|
425
499
|
name: "get_telepathic_hints",
|
|
500
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
426
501
|
description: "Surface relevant past solutions from The Brain based on the active file path.",
|
|
427
502
|
inputSchema: {
|
|
428
503
|
type: "object",
|
|
@@ -435,6 +510,7 @@ const toolDefinitions = [
|
|
|
435
510
|
},
|
|
436
511
|
{
|
|
437
512
|
name: "summarize_long_text",
|
|
513
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
438
514
|
description: "Summarize long text locally using Ollama. Compresses verbose content into concise summaries, saving context window for subsequent turns.",
|
|
439
515
|
inputSchema: {
|
|
440
516
|
type: "object",
|
|
@@ -447,6 +523,7 @@ const toolDefinitions = [
|
|
|
447
523
|
},
|
|
448
524
|
{
|
|
449
525
|
name: "prune_conversation_history",
|
|
526
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
450
527
|
description: "Compress conversation history by decimating old turns and compacting large code blocks in historical messages. Keeps recent context intact. Refuses to mutate anything by default because pruning invalidates the prompt-cache prefix, which is usually a net loss — pass force:true to actually apply it, or call without force to just get the estimated cache-invalidation cost back.",
|
|
451
528
|
inputSchema: {
|
|
452
529
|
type: "object",
|
|
@@ -467,6 +544,7 @@ const toolDefinitions = [
|
|
|
467
544
|
},
|
|
468
545
|
{
|
|
469
546
|
name: "diff_only",
|
|
547
|
+
annotations: { readOnlyHint: true, idempotentHint: false, openWorldHint: false },
|
|
470
548
|
description: "Compute a compact line-by-line diff between the last known state of a file and its current content. Tracks file state in memory per session.",
|
|
471
549
|
inputSchema: {
|
|
472
550
|
type: "object",
|
|
@@ -479,6 +557,7 @@ const toolDefinitions = [
|
|
|
479
557
|
},
|
|
480
558
|
{
|
|
481
559
|
name: "batch_tool_calls",
|
|
560
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
482
561
|
description: "Execute multiple tools in a single MCP call. Accepts an array of { tool, args } and runs them in parallel via Promise.all. Results include per-tool success/error status.",
|
|
483
562
|
inputSchema: {
|
|
484
563
|
type: "object",
|
|
@@ -501,6 +580,7 @@ const toolDefinitions = [
|
|
|
501
580
|
},
|
|
502
581
|
{
|
|
503
582
|
name: "turbosqueeze",
|
|
583
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
504
584
|
description: "COMPRIME prompts hasta 90%. Elimina comentarios, imports redundantes, whitespace excesivo, y compacta JSON. Usar ANTES de enviar código largo al LLM para maximizar ahorro de tokens.",
|
|
505
585
|
inputSchema: {
|
|
506
586
|
type: "object",
|
|
@@ -515,6 +595,7 @@ const toolDefinitions = [
|
|
|
515
595
|
},
|
|
516
596
|
{
|
|
517
597
|
name: "get_symbol_surgical_context",
|
|
598
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
518
599
|
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.",
|
|
519
600
|
inputSchema: {
|
|
520
601
|
type: "object",
|
|
@@ -527,6 +608,7 @@ const toolDefinitions = [
|
|
|
527
608
|
},
|
|
528
609
|
{
|
|
529
610
|
name: "wormhole_squeeze",
|
|
611
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
530
612
|
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.",
|
|
531
613
|
inputSchema: {
|
|
532
614
|
type: "object",
|
|
@@ -538,6 +620,7 @@ const toolDefinitions = [
|
|
|
538
620
|
},
|
|
539
621
|
{
|
|
540
622
|
name: "generate_executive_roi_report",
|
|
623
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
541
624
|
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.",
|
|
542
625
|
inputSchema: {
|
|
543
626
|
type: "object",
|
|
@@ -550,6 +633,7 @@ const toolDefinitions = [
|
|
|
550
633
|
},
|
|
551
634
|
{
|
|
552
635
|
name: "surgical_ast_insert",
|
|
636
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
553
637
|
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.",
|
|
554
638
|
inputSchema: {
|
|
555
639
|
type: "object",
|
|
@@ -565,6 +649,7 @@ const toolDefinitions = [
|
|
|
565
649
|
},
|
|
566
650
|
{
|
|
567
651
|
name: "local_semantic_autofix",
|
|
652
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
568
653
|
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.",
|
|
569
654
|
inputSchema: {
|
|
570
655
|
type: "object",
|
|
@@ -576,6 +661,7 @@ const toolDefinitions = [
|
|
|
576
661
|
},
|
|
577
662
|
{
|
|
578
663
|
name: "compress_context",
|
|
664
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
579
665
|
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.",
|
|
580
666
|
inputSchema: {
|
|
581
667
|
type: "object",
|
|
@@ -592,6 +678,7 @@ const toolDefinitions = [
|
|
|
592
678
|
},
|
|
593
679
|
{
|
|
594
680
|
name: "smarter_cache",
|
|
681
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
595
682
|
description: "Cache predictivo con threshold bajo (75%). Busca en The Brain antes de cualquier razonamiento. Si hay hit >= 75%, devuelve respuesta instantánea sin gastar tokens en LLM.",
|
|
596
683
|
inputSchema: {
|
|
597
684
|
type: "object",
|
|
@@ -602,9 +689,25 @@ const toolDefinitions = [
|
|
|
602
689
|
},
|
|
603
690
|
required: ["query"],
|
|
604
691
|
},
|
|
692
|
+
outputSchema: {
|
|
693
|
+
type: "object",
|
|
694
|
+
properties: {
|
|
695
|
+
hit: { type: "boolean", description: "true si hubo un hit por encima del threshold" },
|
|
696
|
+
source: { type: "string", enum: ["lemma-brain", "llm-call-required", "cache-unavailable"], description: "De dónde salió (o no) la respuesta" },
|
|
697
|
+
similarity: { type: "number", description: "Similitud del mejor match (0.0-1.0). Ausente en la rama de error." },
|
|
698
|
+
threshold: { type: "number", description: "Threshold usado. Ausente en la rama de error." },
|
|
699
|
+
answer: { type: ["string", "null"], description: "Respuesta cacheada si hit=true; null si no. Ausente en la rama de error." },
|
|
700
|
+
tokensSaved: { type: "number", description: "Solo presente cuando hit=true" },
|
|
701
|
+
tokensSavedFormatted: { type: "string", description: "Solo presente cuando hit=true" },
|
|
702
|
+
hint: { type: "string", description: "Sugerencia de siguiente paso; presente en miss y en error" },
|
|
703
|
+
error: { type: "string", description: "Mensaje de error; solo presente si el brain local falló" },
|
|
704
|
+
},
|
|
705
|
+
required: ["hit", "source"],
|
|
706
|
+
},
|
|
605
707
|
},
|
|
606
708
|
{
|
|
607
709
|
name: "state_hash_cache",
|
|
710
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
608
711
|
description: "Cache de razonamiento con invalidación EXACTA (no probabilística): la respuesta queda atada al sha256 del contenido de los archivos de los que depende. Si esos archivos no cambiaron, el hit es matemáticamente válido, no una adivinanza por similitud. Usa action='lookup' antes de razonar sobre una pregunta que dependa de archivos específicos, y action='store' después de responder para cachearla.",
|
|
609
712
|
inputSchema: {
|
|
610
713
|
type: "object",
|
|
@@ -616,9 +719,24 @@ const toolDefinitions = [
|
|
|
616
719
|
},
|
|
617
720
|
required: ["action", "query", "filePaths"],
|
|
618
721
|
},
|
|
722
|
+
outputSchema: {
|
|
723
|
+
type: "object",
|
|
724
|
+
properties: {
|
|
725
|
+
status: { type: "string", enum: ["hit", "miss", "stored"], description: "Resultado de lookup (hit/miss) o de store (stored)" },
|
|
726
|
+
answer: { type: "string", description: "Solo presente cuando status='hit'" },
|
|
727
|
+
note: { type: "string", description: "Solo presente cuando status='hit'" },
|
|
728
|
+
tokensSaved: { type: "number", description: "Solo presente cuando status='hit'" },
|
|
729
|
+
reason: { type: "string", description: "Solo presente cuando status='miss'" },
|
|
730
|
+
hint: { type: "string", description: "Solo presente cuando status='miss'" },
|
|
731
|
+
id: { type: "string", description: "Solo presente cuando status='stored'" },
|
|
732
|
+
filesTracked: { type: "array", items: { type: "string" }, description: "Solo presente cuando status='stored'" },
|
|
733
|
+
},
|
|
734
|
+
required: ["status"],
|
|
735
|
+
},
|
|
619
736
|
},
|
|
620
737
|
{
|
|
621
738
|
name: "token_receipt",
|
|
739
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
622
740
|
description: "Recibo auditable de la sesión: de dónde vino cada respuesta (cache exacto, cache semántico, lectura de archivo, o razonamiento fresco). No es una estadística de 'ahorro' — es una bitácora verificable para que el usuario pueda comprobar que no se le está mintiendo con un cache hit inventado. Llamar con action='summary' para ver el reporte.",
|
|
623
741
|
inputSchema: {
|
|
624
742
|
type: "object",
|
|
@@ -627,9 +745,42 @@ const toolDefinitions = [
|
|
|
627
745
|
limit: { type: "number", description: "Cuántos eventos recientes incluir en el detalle", default: 20 },
|
|
628
746
|
},
|
|
629
747
|
},
|
|
748
|
+
outputSchema: {
|
|
749
|
+
type: "object",
|
|
750
|
+
properties: {
|
|
751
|
+
totalEvents: { type: "number", description: "Total de eventos registrados en la sesión" },
|
|
752
|
+
byType: {
|
|
753
|
+
type: "object",
|
|
754
|
+
properties: {
|
|
755
|
+
exact_cache_hit: { type: "number" },
|
|
756
|
+
semantic_cache_hit: { type: "number" },
|
|
757
|
+
file_read: { type: "number" },
|
|
758
|
+
reasoning: { type: "number" },
|
|
759
|
+
tool_call: { type: "number" },
|
|
760
|
+
},
|
|
761
|
+
description: "Conteo de eventos por tipo de origen",
|
|
762
|
+
},
|
|
763
|
+
recentEvents: {
|
|
764
|
+
type: "array",
|
|
765
|
+
items: {
|
|
766
|
+
type: "object",
|
|
767
|
+
properties: {
|
|
768
|
+
type: { type: "string" },
|
|
769
|
+
label: { type: "string" },
|
|
770
|
+
timestamp: { type: "number" },
|
|
771
|
+
meta: { type: "object" },
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
description: "Últimos `limit` eventos, en orden cronológico",
|
|
775
|
+
},
|
|
776
|
+
note: { type: "string" },
|
|
777
|
+
},
|
|
778
|
+
required: ["totalEvents", "byType", "recentEvents"],
|
|
779
|
+
},
|
|
630
780
|
},
|
|
631
781
|
{
|
|
632
782
|
name: "token_budget",
|
|
783
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
633
784
|
description: "Muestra el dashboard de consumo: multiplicador actual de membresía, tokens ahorrados, tools usadas, y recomendaciones para optimizar más. Consultar periódicamente para auto-regular consumo.",
|
|
634
785
|
inputSchema: {
|
|
635
786
|
type: "object",
|
|
@@ -638,6 +789,7 @@ const toolDefinitions = [
|
|
|
638
789
|
},
|
|
639
790
|
{
|
|
640
791
|
name: "entropy_score",
|
|
792
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
641
793
|
description: "Calcula la entropía matemática (complejidad) de uno o más archivos usando el TypeScript Compiler API. Sin LLM. Sin tokens. Devuelve: cyclomatic complexity, nesting depth, ratio de 'any', tamaño de funciones, y un score compuesto 0-100 (0=limpio, 100=caos puro). Usa esto para identificar qué archivos necesitan refactor ANTES de tocarlos.",
|
|
642
794
|
inputSchema: {
|
|
643
795
|
type: "object",
|
|
@@ -649,6 +801,7 @@ const toolDefinitions = [
|
|
|
649
801
|
},
|
|
650
802
|
{
|
|
651
803
|
name: "coupling_radar",
|
|
804
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
652
805
|
description: "Construye un grafo de acoplamiento entre módulos analizando imports/exports con el TypeScript Compiler. Sin LLM. Sin tokens. Detecta: ciclos de dependencia, módulos 'dios' (importados por todo), islas muertas (nadie los importa), y fan-in/fan-out por módulo. Esencial antes de refactors grandes.",
|
|
653
806
|
inputSchema: {
|
|
654
807
|
type: "object",
|
|
@@ -660,6 +813,7 @@ const toolDefinitions = [
|
|
|
660
813
|
},
|
|
661
814
|
{
|
|
662
815
|
name: "pattern_fossil",
|
|
816
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
663
817
|
description: "Detecta código zombie: patrones que ya fueron reemplazados en la mayoría del codebase pero siguen vivos en archivos viejos. Sin LLM. Sin tokens. Detecta: callbacks vs async/await, var vs let/const, require() vs import, any vs generics. Devuelve porcentaje de adopción del patrón nuevo vs. los fósiles que quedan.",
|
|
664
818
|
inputSchema: {
|
|
665
819
|
type: "object",
|
|
@@ -670,6 +824,7 @@ const toolDefinitions = [
|
|
|
670
824
|
},
|
|
671
825
|
{
|
|
672
826
|
name: "git_heatmap_risk",
|
|
827
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
673
828
|
description: "Analiza el historial de git para identificar zonas de alto riesgo. Sin LLM. Sin tokens. Calcula: churn rate (frecuencia de cambios), co-edición oculta (archivos que siempre cambian juntos = acoplamiento implícito), y un risk score compuesto por archivo. Detecta los archivos que estadísticamente tienen más probabilidad de tener un bug.",
|
|
674
829
|
inputSchema: {
|
|
675
830
|
type: "object",
|
|
@@ -681,6 +836,7 @@ const toolDefinitions = [
|
|
|
681
836
|
},
|
|
682
837
|
{
|
|
683
838
|
name: "precrime_static",
|
|
839
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
684
840
|
description: "MINORITY REPORT para tu código. Combina entropy_score + coupling_radar + git_heatmap_risk en un predictor de riesgo compuesto. Sin LLM. Sin tokens. Devuelve un ranking de archivos y funciones con mayor probabilidad de causar un bug, con justificación matemática de cada factor. Úsalo antes de un deploy o un PR review.",
|
|
685
841
|
inputSchema: {
|
|
686
842
|
type: "object",
|
|
@@ -693,6 +849,7 @@ const toolDefinitions = [
|
|
|
693
849
|
},
|
|
694
850
|
{
|
|
695
851
|
name: "semantic_dedup_guard",
|
|
852
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
696
853
|
description: "Firewall anti-redundancia para The Brain. Antes de hacer store_memory, pasa el contenido por aquí. Consulta ChromaDB localmente sin gastar tokens. Si hay un hit >= 92% → rechaza el store y devuelve el duplicado. Si 75-91% → advierte y muestra el similar. Mantiene The Brain denso y limpio.",
|
|
697
854
|
inputSchema: {
|
|
698
855
|
type: "object",
|
|
@@ -706,6 +863,7 @@ const toolDefinitions = [
|
|
|
706
863
|
},
|
|
707
864
|
{
|
|
708
865
|
name: "dead_export_necromancer",
|
|
866
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
709
867
|
description: "Resucita el código muerto. Usa el TypeScript Compiler para mapear TODOS los exports del workspace y los cruza contra TODOS los imports. Lo que se exporta pero nadie importa = código zombie que está inflando tu contexto y desperdiciando tokens. Sin LLM. Sin tokens. Devuelve lista de exports muertos con estimación de tokens desperdiciados.",
|
|
710
868
|
inputSchema: {
|
|
711
869
|
type: "object",
|
|
@@ -716,6 +874,7 @@ const toolDefinitions = [
|
|
|
716
874
|
},
|
|
717
875
|
{
|
|
718
876
|
name: "review_diff",
|
|
877
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
719
878
|
description: "Analyze a raw git diff locally for regressions, security issues, code quality, and static analysis. Zero-cost static analysis using local TypeScript compiler API — no LLM calls. Returns score, verdict, and detailed findings with suggestions.",
|
|
720
879
|
inputSchema: {
|
|
721
880
|
type: "object",
|
|
@@ -729,6 +888,7 @@ const toolDefinitions = [
|
|
|
729
888
|
},
|
|
730
889
|
{
|
|
731
890
|
name: "review_pr",
|
|
891
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
732
892
|
description: "Analyze a Pull Request via GitHub, Azure DevOps, or GitLab API. Detects regressions, security issues, code quality problems, and breaking changes. Posts results as PR comments and can auto-approve if quality threshold is met. Pro license required.",
|
|
733
893
|
inputSchema: {
|
|
734
894
|
type: "object",
|
|
@@ -745,6 +905,7 @@ const toolDefinitions = [
|
|
|
745
905
|
},
|
|
746
906
|
{
|
|
747
907
|
name: "pr_status",
|
|
908
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
748
909
|
description: "Get the status and detailed results of a previously reviewed PR. Returns score, verdict, findings, and approval status.",
|
|
749
910
|
inputSchema: {
|
|
750
911
|
type: "object",
|
|
@@ -758,6 +919,7 @@ const toolDefinitions = [
|
|
|
758
919
|
},
|
|
759
920
|
{
|
|
760
921
|
name: "generate_pr_workflow",
|
|
922
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
761
923
|
description: "Generate a CI/CD workflow file (GitHub Actions or Azure Pipelines) that runs Lemma PR Review on every pull request. Writes the file to the workspace.",
|
|
762
924
|
inputSchema: {
|
|
763
925
|
type: "object",
|
|
@@ -772,6 +934,7 @@ const toolDefinitions = [
|
|
|
772
934
|
},
|
|
773
935
|
{
|
|
774
936
|
name: "depgraph",
|
|
937
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
775
938
|
description: "Builds a real-time dependency graph for any file in the workspace using the TypeScript Compiler API. Shows what a file imports, what imports it (reverse deps), and what it exports. Zero LLM calls. Essential before any refactor to understand blast radius.",
|
|
776
939
|
inputSchema: {
|
|
777
940
|
type: "object",
|
|
@@ -788,6 +951,7 @@ const toolDefinitions = [
|
|
|
788
951
|
},
|
|
789
952
|
{
|
|
790
953
|
name: "affected_tests",
|
|
954
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
791
955
|
description: "Given the current diff (or an explicit file list), returns only the test files that transitively import the changed code, plus the exact command to run just those. Uses the TypeScript import graph — zero LLM calls. Run this instead of the full suite after an edit: fewer minutes, and far less test output in context. Reports honestly when the full suite is the right answer (unbounded change, runtime-only coupling, most of the suite affected).",
|
|
792
956
|
inputSchema: {
|
|
793
957
|
type: "object",
|
|
@@ -796,9 +960,24 @@ const toolDefinitions = [
|
|
|
796
960
|
baseRef: { type: "string", description: "Additional git ref to diff against, e.g. 'main'. The working tree is always included." },
|
|
797
961
|
},
|
|
798
962
|
},
|
|
963
|
+
outputSchema: {
|
|
964
|
+
type: "object",
|
|
965
|
+
properties: {
|
|
966
|
+
changed: { type: "array", items: { type: "string" }, description: "Files the diff touched, repo-relative" },
|
|
967
|
+
directTests: { type: "array", items: { type: "string" }, description: "Changed files that are themselves tests" },
|
|
968
|
+
affected: { type: "array", items: { type: "string" }, description: "Test files that transitively import a changed file" },
|
|
969
|
+
totalTests: { type: "number", description: "Total test files in the repo" },
|
|
970
|
+
runner: { type: "string", enum: ["jest", "vitest", "unknown"] },
|
|
971
|
+
command: { type: ["string", "null"], description: "Command to run just the affected tests, or null if the full suite is recommended" },
|
|
972
|
+
fullSuiteReason: { type: ["string", "null"], description: "Why the full suite is recommended instead, when command is null" },
|
|
973
|
+
ungraphed: { type: "array", items: { type: "string" }, description: "Changed files absent from the import graph — blast radius unknown" },
|
|
974
|
+
},
|
|
975
|
+
required: ["changed", "directTests", "affected", "totalTests", "runner", "command", "fullSuiteReason", "ungraphed"],
|
|
976
|
+
},
|
|
799
977
|
},
|
|
800
978
|
{
|
|
801
979
|
name: "refactor",
|
|
980
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
802
981
|
description: "Declarative multi-file codemod engine. Rename symbols across the entire workspace or move files updating all imports. Uses TypeScript Compiler API — zero LLM calls. Supports dry-run diff preview and post-refactor tsc verification.",
|
|
803
982
|
inputSchema: {
|
|
804
983
|
type: "object",
|
|
@@ -816,6 +995,7 @@ const toolDefinitions = [
|
|
|
816
995
|
},
|
|
817
996
|
{
|
|
818
997
|
name: "smart_file_slice",
|
|
998
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
819
999
|
description: "Read only the lines semánticamente relevantes around a search query in a file, avoiding sending the entire file context. Returns lines around the matches.",
|
|
820
1000
|
inputSchema: {
|
|
821
1001
|
type: "object",
|
|
@@ -829,6 +1009,7 @@ const toolDefinitions = [
|
|
|
829
1009
|
},
|
|
830
1010
|
{
|
|
831
1011
|
name: "test_oracle",
|
|
1012
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
832
1013
|
description: "Execute unit tests locally and output ONLY the failing tests and compressed stack traces, keeping context clean.",
|
|
833
1014
|
inputSchema: {
|
|
834
1015
|
type: "object",
|
|
@@ -839,6 +1020,7 @@ const toolDefinitions = [
|
|
|
839
1020
|
},
|
|
840
1021
|
{
|
|
841
1022
|
name: "schema_extract",
|
|
1023
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
842
1024
|
description: "Extract high-level schemas, Zod types, interfaces, or database models from a file using the TypeScript AST, removing all implementation code.",
|
|
843
1025
|
inputSchema: {
|
|
844
1026
|
type: "object",
|
|
@@ -850,6 +1032,7 @@ const toolDefinitions = [
|
|
|
850
1032
|
},
|
|
851
1033
|
{
|
|
852
1034
|
name: "changelog_auto",
|
|
1035
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
853
1036
|
description: "Generate a clean, token-efficient changelog summary from git logs based on Conventional Commits.",
|
|
854
1037
|
inputSchema: {
|
|
855
1038
|
type: "object",
|
|
@@ -860,6 +1043,7 @@ const toolDefinitions = [
|
|
|
860
1043
|
},
|
|
861
1044
|
{
|
|
862
1045
|
name: "spec_to_stub",
|
|
1046
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
863
1047
|
description: "Parse a TypeScript interface/type definition from a file and output a template mock/stub object configuration using pure AST analysis.",
|
|
864
1048
|
inputSchema: {
|
|
865
1049
|
type: "object",
|
|
@@ -872,6 +1056,7 @@ const toolDefinitions = [
|
|
|
872
1056
|
},
|
|
873
1057
|
{
|
|
874
1058
|
name: "env_snapshot",
|
|
1059
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
875
1060
|
description: "Capture environmental state like Node version, dependencies in package.json, and variables declared in .env files (hiding actual values/credentials).",
|
|
876
1061
|
inputSchema: {
|
|
877
1062
|
type: "object",
|
|
@@ -880,6 +1065,7 @@ const toolDefinitions = [
|
|
|
880
1065
|
},
|
|
881
1066
|
{
|
|
882
1067
|
name: "migration_tracer",
|
|
1068
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
883
1069
|
description: "Inspect changes in database schemas (tables, columns) across local migrations (e.g. Prisma migration directories).",
|
|
884
1070
|
inputSchema: {
|
|
885
1071
|
type: "object",
|
|
@@ -890,6 +1076,7 @@ const toolDefinitions = [
|
|
|
890
1076
|
},
|
|
891
1077
|
{
|
|
892
1078
|
name: "multi_file_patch",
|
|
1079
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
893
1080
|
description: "Apply search-and-replace patches to multiple workspace files in a single turn. Decreases round-trips for multi-file refactoring.",
|
|
894
1081
|
inputSchema: {
|
|
895
1082
|
type: "object",
|
|
@@ -914,6 +1101,7 @@ const toolDefinitions = [
|
|
|
914
1101
|
},
|
|
915
1102
|
{
|
|
916
1103
|
name: "file_intent_index",
|
|
1104
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
917
1105
|
description: "Map and index files inside the workspace by domain intent (e.g., auth, billing, routes, config) to speed up navigation.",
|
|
918
1106
|
inputSchema: {
|
|
919
1107
|
type: "object",
|
|
@@ -924,6 +1112,7 @@ const toolDefinitions = [
|
|
|
924
1112
|
},
|
|
925
1113
|
{
|
|
926
1114
|
name: "cognitive_map",
|
|
1115
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
927
1116
|
description: "Access and interact with the Auto-Cognitive Mind Map of the workspace (persisted globally). Holds structured high-level system domains, patterns, decisions, and bugs.",
|
|
928
1117
|
inputSchema: {
|
|
929
1118
|
type: "object",
|
|
@@ -946,6 +1135,7 @@ const toolDefinitions = [
|
|
|
946
1135
|
},
|
|
947
1136
|
{
|
|
948
1137
|
name: "semantic_grep",
|
|
1138
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
949
1139
|
description: "Search the codebase for conceptual terms using natural language and BM25 token relevance instead of exact substring matching.",
|
|
950
1140
|
inputSchema: {
|
|
951
1141
|
type: "object",
|
|
@@ -959,6 +1149,7 @@ const toolDefinitions = [
|
|
|
959
1149
|
},
|
|
960
1150
|
{
|
|
961
1151
|
name: "imports_skeleton_resolver",
|
|
1152
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
962
1153
|
description: "Resolve all imported local files inside a file and print only their signatures (classes, functions, interfaces) in a consolidated view.",
|
|
963
1154
|
inputSchema: {
|
|
964
1155
|
type: "object",
|
|
@@ -970,6 +1161,7 @@ const toolDefinitions = [
|
|
|
970
1161
|
},
|
|
971
1162
|
{
|
|
972
1163
|
name: "ast_flow_visualizer",
|
|
1164
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
973
1165
|
description: "Analyze a function's control flow statements (ifs, loops, try/catch) inside a file using AST compilation and output a visual flowchart in Mermaid syntax.",
|
|
974
1166
|
inputSchema: {
|
|
975
1167
|
type: "object",
|
|
@@ -982,6 +1174,7 @@ const toolDefinitions = [
|
|
|
982
1174
|
},
|
|
983
1175
|
{
|
|
984
1176
|
name: "file_fingerprint",
|
|
1177
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
985
1178
|
description: "Compute SHA256 fingerprint of a file. Returns 'unchanged' if hash matches cache, avoiding full re-reads. Saves 60-70% tokens on repeated file access.",
|
|
986
1179
|
inputSchema: {
|
|
987
1180
|
type: "object",
|
|
@@ -994,6 +1187,7 @@ const toolDefinitions = [
|
|
|
994
1187
|
},
|
|
995
1188
|
{
|
|
996
1189
|
name: "git_blame_heat",
|
|
1190
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
997
1191
|
description: "Analyze git blame history to identify high-risk lines and authors. Zero LLM cost. Returns risk scores per line, hotspots, and top contributors by risk.",
|
|
998
1192
|
inputSchema: {
|
|
999
1193
|
type: "object",
|
|
@@ -1005,6 +1199,7 @@ const toolDefinitions = [
|
|
|
1005
1199
|
},
|
|
1006
1200
|
{
|
|
1007
1201
|
name: "type_coverage_report",
|
|
1202
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1008
1203
|
description: "Analyze TypeScript type coverage of a file using AST. Returns declaration count, typed vs untyped ratio, any-usage count, and a safety score 0-100. Zero LLM cost.",
|
|
1009
1204
|
inputSchema: {
|
|
1010
1205
|
type: "object",
|
|
@@ -1016,6 +1211,7 @@ const toolDefinitions = [
|
|
|
1016
1211
|
},
|
|
1017
1212
|
{
|
|
1018
1213
|
name: "import_tree_context",
|
|
1214
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1019
1215
|
description: "Build a bidirectional import graph for a file. Shows what it imports, what imports it, and external dependencies. Zero LLM cost. Essential before refactors.",
|
|
1020
1216
|
inputSchema: {
|
|
1021
1217
|
type: "object",
|
|
@@ -1028,6 +1224,7 @@ const toolDefinitions = [
|
|
|
1028
1224
|
},
|
|
1029
1225
|
{
|
|
1030
1226
|
name: "read_token_budgeted",
|
|
1227
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1031
1228
|
description: "Read a file with a hard token budget. Returns only the most relevant sections up to maxTokens. Saves 70-90% vs full reads.",
|
|
1032
1229
|
inputSchema: {
|
|
1033
1230
|
type: "object",
|
|
@@ -1041,6 +1238,7 @@ const toolDefinitions = [
|
|
|
1041
1238
|
},
|
|
1042
1239
|
{
|
|
1043
1240
|
name: "bulk_file_digest",
|
|
1241
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1044
1242
|
description: "Read multiple files in one call and return a compressed digest. Saves 80% round-trips vs reading files individually.",
|
|
1045
1243
|
inputSchema: {
|
|
1046
1244
|
type: "object",
|
|
@@ -1058,6 +1256,7 @@ const toolDefinitions = [
|
|
|
1058
1256
|
},
|
|
1059
1257
|
{
|
|
1060
1258
|
name: "auto_context_bundle",
|
|
1259
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1061
1260
|
description: "Get everything about a file in one call: compressed content, import graph, schema types, and telepathic hints from The Brain. Replaces 4-5 separate tool calls.",
|
|
1062
1261
|
inputSchema: {
|
|
1063
1262
|
type: "object",
|
|
@@ -1069,6 +1268,7 @@ const toolDefinitions = [
|
|
|
1069
1268
|
},
|
|
1070
1269
|
{
|
|
1071
1270
|
name: "repeat_guard",
|
|
1271
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1072
1272
|
description: "Check if a draft output is repetitive compared to recent responses. Returns similarity score and suggestion. Prevents duplicate explanations.",
|
|
1073
1273
|
inputSchema: {
|
|
1074
1274
|
type: "object",
|
|
@@ -1081,6 +1281,7 @@ const toolDefinitions = [
|
|
|
1081
1281
|
},
|
|
1082
1282
|
{
|
|
1083
1283
|
name: "conversation_checkpoint",
|
|
1284
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1084
1285
|
description: "Save or load session state. Prevents re-explaining context across turns. Save current progress or load last checkpoint.",
|
|
1085
1286
|
inputSchema: {
|
|
1086
1287
|
type: "object",
|
|
@@ -1096,6 +1297,7 @@ const toolDefinitions = [
|
|
|
1096
1297
|
},
|
|
1097
1298
|
{
|
|
1098
1299
|
name: "test_autofix_interceptor",
|
|
1300
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
1099
1301
|
description: "Run tests and auto-search The Brain for fixes to failures. Returns failure list with suggested fixes from past sessions. Saves 4-5 turns of debugging.",
|
|
1100
1302
|
inputSchema: {
|
|
1101
1303
|
type: "object",
|
|
@@ -1106,6 +1308,7 @@ const toolDefinitions = [
|
|
|
1106
1308
|
},
|
|
1107
1309
|
{
|
|
1108
1310
|
name: "prompt_pattern_cache",
|
|
1311
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1109
1312
|
description: "Find or store optimized prompt templates. Reuses past prompt patterns to save 20-30% tokens on repetitive operations.",
|
|
1110
1313
|
inputSchema: {
|
|
1111
1314
|
type: "object",
|
|
@@ -1125,6 +1328,7 @@ const _infraToolDefs = (0, mcp_tools_1.getInfraToolDefinitions)();
|
|
|
1125
1328
|
// than in the main array because it must never itself be hidden.
|
|
1126
1329
|
const TOOLBOX_TOOL_DEFINITION = {
|
|
1127
1330
|
name: "lemma_toolbox",
|
|
1331
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
1128
1332
|
description: "Discover and invoke Lemma tools whose schemas are not loaded this session. Use action='list' (optionally with a query) to see what exists, 'schema' to read one tool's inputs, and 'call' to run it. Every Lemma capability is reachable here.",
|
|
1129
1333
|
inputSchema: {
|
|
1130
1334
|
type: "object",
|
|
@@ -1330,6 +1534,7 @@ function receiptLabelMeta(name, args) {
|
|
|
1330
1534
|
return meta;
|
|
1331
1535
|
}
|
|
1332
1536
|
function setupToolsHandlers(server, onToolCall) {
|
|
1537
|
+
mcpServerRef = server;
|
|
1333
1538
|
(0, SpeculativeWarmer_1.registerSymbolExtractor)(extractSymbolsWithTsCompiler);
|
|
1334
1539
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
1335
1540
|
tools: toolDefinitionsArray,
|
|
@@ -1526,7 +1731,8 @@ async function handleGetRoutingAdvice(args) {
|
|
|
1526
1731
|
const intendedModel = args?.intended_model;
|
|
1527
1732
|
const router = new ComplexityRouter_1.ComplexityRouter();
|
|
1528
1733
|
const decision = router.evaluate(prompt, intendedModel);
|
|
1529
|
-
|
|
1734
|
+
const switched = !!intendedModel && decision.model !== intendedModel;
|
|
1735
|
+
if (decision.complexity === "low" && switched) {
|
|
1530
1736
|
const estimatedPromptTokens = Math.floor((prompt?.length || 0) / 4);
|
|
1531
1737
|
(0, reportSavings_1.reportSavings)({
|
|
1532
1738
|
source: "complexityRouting",
|
|
@@ -1535,6 +1741,12 @@ async function handleGetRoutingAdvice(args) {
|
|
|
1535
1741
|
query: prompt?.substring(0, 100),
|
|
1536
1742
|
});
|
|
1537
1743
|
}
|
|
1744
|
+
const structured = {
|
|
1745
|
+
recommendedModel: decision.model,
|
|
1746
|
+
complexity: decision.complexity,
|
|
1747
|
+
intendedModel: intendedModel || null,
|
|
1748
|
+
switched,
|
|
1749
|
+
};
|
|
1538
1750
|
return {
|
|
1539
1751
|
content: [
|
|
1540
1752
|
{
|
|
@@ -1542,6 +1754,7 @@ async function handleGetRoutingAdvice(args) {
|
|
|
1542
1754
|
text: `Lemma Routing Advice: Use ${decision.model}. Reason: Complexity is ${decision.complexity}.`,
|
|
1543
1755
|
},
|
|
1544
1756
|
],
|
|
1757
|
+
structuredContent: structured,
|
|
1545
1758
|
};
|
|
1546
1759
|
}
|
|
1547
1760
|
async function handleAutoHeal(args) {
|
|
@@ -1814,6 +2027,11 @@ async function handleApplyWorkspacePatch(args) {
|
|
|
1814
2027
|
const updatedLines = [...contentLines.slice(0, match.startLine), ...finalReplace.split("\n"), ...contentLines.slice(match.endLine + 1)];
|
|
1815
2028
|
updatedContent = updatedLines.join("\n");
|
|
1816
2029
|
}
|
|
2030
|
+
const elicited = await tryElicitConfirmation(`Apply a patch to ${filePath}? This replaces ${searchContent.length} char(s) with ${replaceContent.length} char(s) ` +
|
|
2031
|
+
`starting at line ${match.startLine + 1}.`);
|
|
2032
|
+
if (elicited.supported && !elicited.confirmed) {
|
|
2033
|
+
return { content: [{ type: "text", text: `Patch to ${filePath} was not applied: declined during confirmation.` }] };
|
|
2034
|
+
}
|
|
1817
2035
|
fs_1.default.writeFileSync(resolved, updatedContent, "utf8");
|
|
1818
2036
|
const note = match.strategy === "exact" ? "" : ` (matched via ${match.strategy}, score ${match.score.toFixed(2)})`;
|
|
1819
2037
|
return { content: [{ type: "text", text: `Success: Patch successfully applied to ${filePath}${note}` }] };
|
|
@@ -1863,6 +2081,10 @@ async function handleRunWorkspaceCommand(args) {
|
|
|
1863
2081
|
const MAX_TIMEOUT_MS = 600000;
|
|
1864
2082
|
const requested = typeof args?.timeoutMs === "number" && args.timeoutMs > 0 ? args.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
1865
2083
|
const timeoutMs = Math.min(Math.floor(requested), MAX_TIMEOUT_MS);
|
|
2084
|
+
const elicited = await tryElicitConfirmation(`Run this command in the workspace root?\n\n${command}`);
|
|
2085
|
+
if (elicited.supported && !elicited.confirmed) {
|
|
2086
|
+
return { content: [{ type: "text", text: `Command was not run: declined during confirmation.\n\nCommand: ${command}` }] };
|
|
2087
|
+
}
|
|
1866
2088
|
const result = (0, child_process_1.spawnSync)(command, {
|
|
1867
2089
|
cwd: workspaceRoot,
|
|
1868
2090
|
shell: true,
|
|
@@ -2885,47 +3107,44 @@ async function handleSmarterCache(args) {
|
|
|
2885
3107
|
reportSavings({ source: "cache", tokens: tokensSaved, toolName: "smarter_cache", query: query.substring(0, 100) });
|
|
2886
3108
|
}
|
|
2887
3109
|
catch { }
|
|
3110
|
+
const structured = {
|
|
3111
|
+
hit: true,
|
|
3112
|
+
similarity: topHit.similarity,
|
|
3113
|
+
threshold,
|
|
3114
|
+
answer: responseText,
|
|
3115
|
+
source: "lemma-brain",
|
|
3116
|
+
tokensSaved,
|
|
3117
|
+
tokensSavedFormatted: `~${tokensSaved.toLocaleString()} tokens`,
|
|
3118
|
+
};
|
|
2888
3119
|
return {
|
|
2889
|
-
content: [{
|
|
2890
|
-
|
|
2891
|
-
text: JSON.stringify({
|
|
2892
|
-
hit: true,
|
|
2893
|
-
similarity: topHit.similarity,
|
|
2894
|
-
threshold,
|
|
2895
|
-
answer: responseText,
|
|
2896
|
-
source: "lemma-brain",
|
|
2897
|
-
tokensSaved,
|
|
2898
|
-
tokensSavedFormatted: `~${tokensSaved.toLocaleString()} tokens`,
|
|
2899
|
-
}, null, 2),
|
|
2900
|
-
}],
|
|
3120
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3121
|
+
structuredContent: structured,
|
|
2901
3122
|
};
|
|
2902
3123
|
}
|
|
3124
|
+
const structured = {
|
|
3125
|
+
hit: false,
|
|
3126
|
+
similarity: topHit?.similarity || 0,
|
|
3127
|
+
threshold,
|
|
3128
|
+
answer: null,
|
|
3129
|
+
source: "llm-call-required",
|
|
3130
|
+
hint: "Después de resolver, llama store_memory para cachear y no gastar tokens en esto otra vez.",
|
|
3131
|
+
};
|
|
2903
3132
|
return {
|
|
2904
|
-
content: [{
|
|
2905
|
-
|
|
2906
|
-
text: JSON.stringify({
|
|
2907
|
-
hit: false,
|
|
2908
|
-
similarity: topHit?.similarity || 0,
|
|
2909
|
-
threshold,
|
|
2910
|
-
answer: null,
|
|
2911
|
-
source: "llm-call-required",
|
|
2912
|
-
hint: "Después de resolver, llama store_memory para cachear y no gastar tokens en esto otra vez.",
|
|
2913
|
-
}, null, 2),
|
|
2914
|
-
}],
|
|
3133
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3134
|
+
structuredContent: structured,
|
|
2915
3135
|
};
|
|
2916
3136
|
}
|
|
2917
3137
|
catch (e) {
|
|
2918
3138
|
(0, utils_1.logError)("smarter_cache", e);
|
|
3139
|
+
const structured = {
|
|
3140
|
+
hit: false,
|
|
3141
|
+
error: e.message,
|
|
3142
|
+
source: "cache-unavailable",
|
|
3143
|
+
hint: "El cache local del brain falló. El LLM funcionará normalmente sin cache.",
|
|
3144
|
+
};
|
|
2919
3145
|
return {
|
|
2920
|
-
content: [{
|
|
2921
|
-
|
|
2922
|
-
text: JSON.stringify({
|
|
2923
|
-
hit: false,
|
|
2924
|
-
error: e.message,
|
|
2925
|
-
source: "cache-unavailable",
|
|
2926
|
-
hint: "El cache local del brain falló. El LLM funcionará normalmente sin cache.",
|
|
2927
|
-
}, null, 2),
|
|
2928
|
-
}],
|
|
3146
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3147
|
+
structuredContent: structured,
|
|
2929
3148
|
};
|
|
2930
3149
|
}
|
|
2931
3150
|
}
|
|
@@ -2946,23 +3165,21 @@ async function handleStateHashCache(args) {
|
|
|
2946
3165
|
(0, reportSavings_1.reportSavings)({ source: "cache", tokens: tokensSaved, toolName: "state_hash_cache", query: query.substring(0, 100) });
|
|
2947
3166
|
}
|
|
2948
3167
|
catch { }
|
|
3168
|
+
const structured = {
|
|
3169
|
+
status: "hit",
|
|
3170
|
+
answer: result.entry.answer,
|
|
3171
|
+
note: "Respuesta desde caché exacto (hash de archivos sin cambios) — no necesita re-razonar.",
|
|
3172
|
+
tokensSaved,
|
|
3173
|
+
};
|
|
2949
3174
|
return {
|
|
2950
|
-
content: [{
|
|
2951
|
-
|
|
2952
|
-
text: JSON.stringify({
|
|
2953
|
-
status: "hit",
|
|
2954
|
-
answer: result.entry.answer,
|
|
2955
|
-
note: "Respuesta desde caché exacto (hash de archivos sin cambios) — no necesita re-razonar.",
|
|
2956
|
-
tokensSaved,
|
|
2957
|
-
}, null, 2),
|
|
2958
|
-
}],
|
|
3175
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3176
|
+
structuredContent: structured,
|
|
2959
3177
|
};
|
|
2960
3178
|
}
|
|
3179
|
+
const structured = { status: "miss", reason: result.reason, hint: "Razona normalmente y luego llama action='store' con la respuesta." };
|
|
2961
3180
|
return {
|
|
2962
|
-
content: [{
|
|
2963
|
-
|
|
2964
|
-
text: JSON.stringify({ status: "miss", reason: result.reason, hint: "Razona normalmente y luego llama action='store' con la respuesta." }, null, 2),
|
|
2965
|
-
}],
|
|
3181
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3182
|
+
structuredContent: structured,
|
|
2966
3183
|
};
|
|
2967
3184
|
}
|
|
2968
3185
|
if (action === "store") {
|
|
@@ -2970,8 +3187,10 @@ async function handleStateHashCache(args) {
|
|
|
2970
3187
|
if (!answer)
|
|
2971
3188
|
throw new Error("answer is required for action='store'");
|
|
2972
3189
|
const entry = (0, StateHashCache_1.storeStateHash)(workspaceRoot, query, answer, filePaths);
|
|
3190
|
+
const structured = { status: "stored", id: entry.id, filesTracked: Object.keys(entry.fileHashes) };
|
|
2973
3191
|
return {
|
|
2974
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
3192
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3193
|
+
structuredContent: structured,
|
|
2975
3194
|
};
|
|
2976
3195
|
}
|
|
2977
3196
|
throw new Error(`Unknown action: ${action}. Use 'lookup' or 'store'.`);
|
|
@@ -2980,16 +3199,15 @@ async function handleStateHashCache(args) {
|
|
|
2980
3199
|
async function handleTokenReceipt(args) {
|
|
2981
3200
|
const limit = typeof args?.limit === "number" ? args.limit : 20;
|
|
2982
3201
|
const summary = (0, TokenReceipt_1.getReceiptSummary)(limit);
|
|
3202
|
+
const structured = {
|
|
3203
|
+
totalEvents: summary.totalEvents,
|
|
3204
|
+
byType: summary.byType,
|
|
3205
|
+
recentEvents: summary.recent,
|
|
3206
|
+
note: "Esto es una bitácora real de esta sesión, no una proyección de ahorro. Si byType.reasoning es alto, no hubo mucho cache — y está bien, es honesto.",
|
|
3207
|
+
};
|
|
2983
3208
|
return {
|
|
2984
|
-
content: [{
|
|
2985
|
-
|
|
2986
|
-
text: JSON.stringify({
|
|
2987
|
-
totalEvents: summary.totalEvents,
|
|
2988
|
-
byType: summary.byType,
|
|
2989
|
-
recentEvents: summary.recent,
|
|
2990
|
-
note: "Esto es una bitácora real de esta sesión, no una proyección de ahorro. Si byType.reasoning es alto, no hubo mucho cache — y está bien, es honesto.",
|
|
2991
|
-
}, null, 2),
|
|
2992
|
-
}],
|
|
3209
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3210
|
+
structuredContent: structured,
|
|
2993
3211
|
};
|
|
2994
3212
|
}
|
|
2995
3213
|
// ── Token Budget ─────────────────────────────────────────────────
|
|
@@ -4273,7 +4491,17 @@ async function handleAffectedTests(args) {
|
|
|
4273
4491
|
totalTests: result.totalTests,
|
|
4274
4492
|
narrowed: result.command !== null,
|
|
4275
4493
|
});
|
|
4276
|
-
|
|
4494
|
+
const structured = {
|
|
4495
|
+
changed: result.changed,
|
|
4496
|
+
directTests: result.directTests,
|
|
4497
|
+
affected: result.affected,
|
|
4498
|
+
totalTests: result.totalTests,
|
|
4499
|
+
runner: result.runner,
|
|
4500
|
+
command: result.command,
|
|
4501
|
+
fullSuiteReason: result.fullSuiteReason,
|
|
4502
|
+
ungraphed: result.ungraphed,
|
|
4503
|
+
};
|
|
4504
|
+
return { content: [{ type: "text", text }], structuredContent: structured };
|
|
4277
4505
|
}
|
|
4278
4506
|
catch (err) {
|
|
4279
4507
|
(0, utils_1.logError)("affected_tests", err);
|