@nxuss/lemma 1.10.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 +21 -2
- 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 +64 -45
- 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 +309 -65
- 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/cjs/utils/AstSymbolCache.d.ts +29 -0
- package/dist/cjs/utils/AstSymbolCache.d.ts.map +1 -0
- package/dist/cjs/utils/AstSymbolCache.js +66 -0
- package/dist/cjs/utils/AstSymbolCache.js.map +1 -0
- package/dist/cjs/utils/ContextSqueezer.d.ts +24 -4
- package/dist/cjs/utils/ContextSqueezer.d.ts.map +1 -1
- package/dist/cjs/utils/ContextSqueezer.js +32 -3
- package/dist/cjs/utils/ContextSqueezer.js.map +1 -1
- package/dist/cjs/utils/OutputLedger.d.ts.map +1 -1
- package/dist/cjs/utils/OutputLedger.js +20 -2
- package/dist/cjs/utils/OutputLedger.js.map +1 -1
- package/dist/cjs/utils/ReadWorkspaceCache.d.ts.map +1 -1
- package/dist/cjs/utils/ReadWorkspaceCache.js +25 -2
- package/dist/cjs/utils/ReadWorkspaceCache.js.map +1 -1
- package/dist/cjs/utils/RoiLedger.d.ts +31 -0
- package/dist/cjs/utils/RoiLedger.d.ts.map +1 -0
- package/dist/cjs/utils/RoiLedger.js +113 -0
- package/dist/cjs/utils/RoiLedger.js.map +1 -0
- package/dist/cjs/utils/SharedLedgerStore.d.ts +26 -0
- package/dist/cjs/utils/SharedLedgerStore.d.ts.map +1 -0
- package/dist/cjs/utils/SharedLedgerStore.js +130 -0
- package/dist/cjs/utils/SharedLedgerStore.js.map +1 -0
- package/dist/cjs/utils/SpeculativeWarmer.d.ts +24 -0
- package/dist/cjs/utils/SpeculativeWarmer.d.ts.map +1 -0
- package/dist/cjs/utils/SpeculativeWarmer.js +73 -0
- package/dist/cjs/utils/SpeculativeWarmer.js.map +1 -0
- package/dist/cjs/utils/reportSavings.d.ts.map +1 -1
- package/dist/cjs/utils/reportSavings.js +11 -0
- package/dist/cjs/utils/reportSavings.js.map +1 -1
- package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +21 -2
- 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 +64 -45
- 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 +309 -65
- 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/dist/esm/utils/AstSymbolCache.d.ts +29 -0
- package/dist/esm/utils/AstSymbolCache.d.ts.map +1 -0
- package/dist/esm/utils/AstSymbolCache.js +58 -0
- package/dist/esm/utils/AstSymbolCache.js.map +1 -0
- package/dist/esm/utils/ContextSqueezer.d.ts +24 -4
- package/dist/esm/utils/ContextSqueezer.d.ts.map +1 -1
- package/dist/esm/utils/ContextSqueezer.js +32 -3
- package/dist/esm/utils/ContextSqueezer.js.map +1 -1
- package/dist/esm/utils/OutputLedger.d.ts.map +1 -1
- package/dist/esm/utils/OutputLedger.js +20 -2
- package/dist/esm/utils/OutputLedger.js.map +1 -1
- package/dist/esm/utils/ReadWorkspaceCache.d.ts.map +1 -1
- package/dist/esm/utils/ReadWorkspaceCache.js +25 -2
- package/dist/esm/utils/ReadWorkspaceCache.js.map +1 -1
- package/dist/esm/utils/RoiLedger.d.ts +31 -0
- package/dist/esm/utils/RoiLedger.d.ts.map +1 -0
- package/dist/esm/utils/RoiLedger.js +104 -0
- package/dist/esm/utils/RoiLedger.js.map +1 -0
- package/dist/esm/utils/SharedLedgerStore.d.ts +26 -0
- package/dist/esm/utils/SharedLedgerStore.d.ts.map +1 -0
- package/dist/esm/utils/SharedLedgerStore.js +122 -0
- package/dist/esm/utils/SharedLedgerStore.js.map +1 -0
- package/dist/esm/utils/SpeculativeWarmer.d.ts +24 -0
- package/dist/esm/utils/SpeculativeWarmer.d.ts.map +1 -0
- package/dist/esm/utils/SpeculativeWarmer.js +66 -0
- package/dist/esm/utils/SpeculativeWarmer.js.map +1 -0
- package/dist/esm/utils/reportSavings.d.ts.map +1 -1
- package/dist/esm/utils/reportSavings.js +11 -0
- package/dist/esm/utils/reportSavings.js.map +1 -1
- package/package.json +2 -2
package/dist/cjs/mcp/tools.js
CHANGED
|
@@ -85,6 +85,8 @@ const ReadWorkspaceCache_1 = require("../utils/ReadWorkspaceCache");
|
|
|
85
85
|
const OutputDelta_1 = require("../utils/OutputDelta");
|
|
86
86
|
const OutputLedger_1 = require("../utils/OutputLedger");
|
|
87
87
|
const DepGraph_1 = require("../utils/DepGraph");
|
|
88
|
+
const AstSymbolCache_1 = require("../utils/AstSymbolCache");
|
|
89
|
+
const SpeculativeWarmer_1 = require("../utils/SpeculativeWarmer");
|
|
88
90
|
const AffectedTests_1 = require("../utils/AffectedTests");
|
|
89
91
|
const LineNumberMap_1 = require("../utils/LineNumberMap");
|
|
90
92
|
const FileOutline_1 = require("../utils/FileOutline");
|
|
@@ -133,9 +135,55 @@ const PRO_GATE_MESSAGE = [
|
|
|
133
135
|
"🚀 Get Lemma Pro:",
|
|
134
136
|
" https://lemma.nxus.studio/upgrade",
|
|
135
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
|
+
}
|
|
136
183
|
const toolDefinitions = [
|
|
137
184
|
{
|
|
138
185
|
name: "scrub_privacy",
|
|
186
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
139
187
|
description: "Mask sensitive data (PII, API Keys, Credentials) from a text block using Lemma's Privacy Firewall.",
|
|
140
188
|
inputSchema: {
|
|
141
189
|
type: "object",
|
|
@@ -147,6 +195,7 @@ const toolDefinitions = [
|
|
|
147
195
|
},
|
|
148
196
|
{
|
|
149
197
|
name: "search_memory",
|
|
198
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
150
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.",
|
|
151
200
|
inputSchema: {
|
|
152
201
|
type: "object",
|
|
@@ -164,6 +213,7 @@ const toolDefinitions = [
|
|
|
164
213
|
},
|
|
165
214
|
{
|
|
166
215
|
name: "store_memory",
|
|
216
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
167
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.",
|
|
168
218
|
inputSchema: {
|
|
169
219
|
type: "object",
|
|
@@ -215,6 +265,7 @@ const toolDefinitions = [
|
|
|
215
265
|
},
|
|
216
266
|
{
|
|
217
267
|
name: "get_routing_advice",
|
|
268
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
218
269
|
description: "Analyzes a prompt and suggests the best model based on Lemma's Complexity Router.",
|
|
219
270
|
inputSchema: {
|
|
220
271
|
type: "object",
|
|
@@ -224,9 +275,20 @@ const toolDefinitions = [
|
|
|
224
275
|
},
|
|
225
276
|
required: ["prompt"],
|
|
226
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
|
+
},
|
|
227
288
|
},
|
|
228
289
|
{
|
|
229
290
|
name: "auto_heal",
|
|
291
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
230
292
|
description: "Diagnose and auto-heal the latest local server crash registered in Lemma's context logs.",
|
|
231
293
|
inputSchema: {
|
|
232
294
|
type: "object",
|
|
@@ -237,6 +299,7 @@ const toolDefinitions = [
|
|
|
237
299
|
},
|
|
238
300
|
{
|
|
239
301
|
name: "read_workspace_file",
|
|
302
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
240
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.",
|
|
241
304
|
inputSchema: {
|
|
242
305
|
type: "object",
|
|
@@ -253,6 +316,7 @@ const toolDefinitions = [
|
|
|
253
316
|
},
|
|
254
317
|
{
|
|
255
318
|
name: "write_workspace_file",
|
|
319
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
256
320
|
description: "Write full contents to a file inside the workspace. Creates parent directories automatically.",
|
|
257
321
|
inputSchema: {
|
|
258
322
|
type: "object",
|
|
@@ -265,6 +329,7 @@ const toolDefinitions = [
|
|
|
265
329
|
},
|
|
266
330
|
{
|
|
267
331
|
name: "create_workspace_file",
|
|
332
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
268
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.",
|
|
269
334
|
inputSchema: {
|
|
270
335
|
type: "object",
|
|
@@ -277,6 +342,7 @@ const toolDefinitions = [
|
|
|
277
342
|
},
|
|
278
343
|
{
|
|
279
344
|
name: "apply_workspace_patch",
|
|
345
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
280
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.",
|
|
281
347
|
inputSchema: {
|
|
282
348
|
type: "object",
|
|
@@ -290,6 +356,7 @@ const toolDefinitions = [
|
|
|
290
356
|
},
|
|
291
357
|
{
|
|
292
358
|
name: "run_workspace_command",
|
|
359
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
293
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.",
|
|
294
361
|
inputSchema: {
|
|
295
362
|
type: "object",
|
|
@@ -311,6 +378,7 @@ const toolDefinitions = [
|
|
|
311
378
|
},
|
|
312
379
|
{
|
|
313
380
|
name: "output_region",
|
|
381
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
314
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.",
|
|
315
383
|
inputSchema: {
|
|
316
384
|
type: "object",
|
|
@@ -326,6 +394,7 @@ const toolDefinitions = [
|
|
|
326
394
|
},
|
|
327
395
|
{
|
|
328
396
|
name: "list_workspace_dir",
|
|
397
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
329
398
|
description: "List files and subdirectories recursively to navigate the repository structure.",
|
|
330
399
|
inputSchema: {
|
|
331
400
|
type: "object",
|
|
@@ -337,6 +406,7 @@ const toolDefinitions = [
|
|
|
337
406
|
},
|
|
338
407
|
{
|
|
339
408
|
name: "search_workspace",
|
|
409
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
340
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.",
|
|
341
411
|
inputSchema: {
|
|
342
412
|
type: "object",
|
|
@@ -350,6 +420,7 @@ const toolDefinitions = [
|
|
|
350
420
|
},
|
|
351
421
|
{
|
|
352
422
|
name: "squeeze_prompt",
|
|
423
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
353
424
|
description: "Compress code blocks, comments, and boilerplate in any prompt. Saves up to 80% tokens.",
|
|
354
425
|
inputSchema: {
|
|
355
426
|
type: "object",
|
|
@@ -362,6 +433,7 @@ const toolDefinitions = [
|
|
|
362
433
|
},
|
|
363
434
|
{
|
|
364
435
|
name: "get_project_onboarding",
|
|
436
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
365
437
|
description: "Fetch dynamic architectural and stack overview of the current project in markdown.",
|
|
366
438
|
inputSchema: {
|
|
367
439
|
type: "object",
|
|
@@ -370,6 +442,7 @@ const toolDefinitions = [
|
|
|
370
442
|
},
|
|
371
443
|
{
|
|
372
444
|
name: "get_project_history",
|
|
445
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
373
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.",
|
|
374
447
|
inputSchema: {
|
|
375
448
|
type: "object",
|
|
@@ -380,6 +453,7 @@ const toolDefinitions = [
|
|
|
380
453
|
},
|
|
381
454
|
{
|
|
382
455
|
name: "get_ast_hologram",
|
|
456
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
383
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.",
|
|
384
458
|
inputSchema: {
|
|
385
459
|
type: "object",
|
|
@@ -396,6 +470,7 @@ const toolDefinitions = [
|
|
|
396
470
|
},
|
|
397
471
|
{
|
|
398
472
|
name: "validate_patch_sandbox",
|
|
473
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
399
474
|
description: "Validate proposed code in isolated sandbox via tsc + syntax check before applying.",
|
|
400
475
|
inputSchema: {
|
|
401
476
|
type: "object",
|
|
@@ -408,6 +483,7 @@ const toolDefinitions = [
|
|
|
408
483
|
},
|
|
409
484
|
{
|
|
410
485
|
name: "query_hybrid_consensus",
|
|
486
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
411
487
|
description: "Search The Brain before reasoning. Brain HIT returns cached answer instantly. Brain MISS proceeds to cloud.",
|
|
412
488
|
inputSchema: {
|
|
413
489
|
type: "object",
|
|
@@ -421,6 +497,7 @@ const toolDefinitions = [
|
|
|
421
497
|
},
|
|
422
498
|
{
|
|
423
499
|
name: "get_telepathic_hints",
|
|
500
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
424
501
|
description: "Surface relevant past solutions from The Brain based on the active file path.",
|
|
425
502
|
inputSchema: {
|
|
426
503
|
type: "object",
|
|
@@ -433,6 +510,7 @@ const toolDefinitions = [
|
|
|
433
510
|
},
|
|
434
511
|
{
|
|
435
512
|
name: "summarize_long_text",
|
|
513
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
436
514
|
description: "Summarize long text locally using Ollama. Compresses verbose content into concise summaries, saving context window for subsequent turns.",
|
|
437
515
|
inputSchema: {
|
|
438
516
|
type: "object",
|
|
@@ -445,7 +523,8 @@ const toolDefinitions = [
|
|
|
445
523
|
},
|
|
446
524
|
{
|
|
447
525
|
name: "prune_conversation_history",
|
|
448
|
-
|
|
526
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
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.",
|
|
449
528
|
inputSchema: {
|
|
450
529
|
type: "object",
|
|
451
530
|
properties: {
|
|
@@ -454,12 +533,18 @@ const toolDefinitions = [
|
|
|
454
533
|
items: { type: "object" },
|
|
455
534
|
description: "Array of conversation messages with role + content",
|
|
456
535
|
},
|
|
536
|
+
force: {
|
|
537
|
+
type: "boolean",
|
|
538
|
+
description: "Actually apply the prune despite the prompt-cache-invalidation cost. Default false — the call is a dry run that reports cacheInvalidationEstimate instead.",
|
|
539
|
+
default: false,
|
|
540
|
+
},
|
|
457
541
|
},
|
|
458
542
|
required: ["messages"],
|
|
459
543
|
},
|
|
460
544
|
},
|
|
461
545
|
{
|
|
462
546
|
name: "diff_only",
|
|
547
|
+
annotations: { readOnlyHint: true, idempotentHint: false, openWorldHint: false },
|
|
463
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.",
|
|
464
549
|
inputSchema: {
|
|
465
550
|
type: "object",
|
|
@@ -472,6 +557,7 @@ const toolDefinitions = [
|
|
|
472
557
|
},
|
|
473
558
|
{
|
|
474
559
|
name: "batch_tool_calls",
|
|
560
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
475
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.",
|
|
476
562
|
inputSchema: {
|
|
477
563
|
type: "object",
|
|
@@ -494,6 +580,7 @@ const toolDefinitions = [
|
|
|
494
580
|
},
|
|
495
581
|
{
|
|
496
582
|
name: "turbosqueeze",
|
|
583
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
497
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.",
|
|
498
585
|
inputSchema: {
|
|
499
586
|
type: "object",
|
|
@@ -508,6 +595,7 @@ const toolDefinitions = [
|
|
|
508
595
|
},
|
|
509
596
|
{
|
|
510
597
|
name: "get_symbol_surgical_context",
|
|
598
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
511
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.",
|
|
512
600
|
inputSchema: {
|
|
513
601
|
type: "object",
|
|
@@ -520,6 +608,7 @@ const toolDefinitions = [
|
|
|
520
608
|
},
|
|
521
609
|
{
|
|
522
610
|
name: "wormhole_squeeze",
|
|
611
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
523
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.",
|
|
524
613
|
inputSchema: {
|
|
525
614
|
type: "object",
|
|
@@ -531,6 +620,7 @@ const toolDefinitions = [
|
|
|
531
620
|
},
|
|
532
621
|
{
|
|
533
622
|
name: "generate_executive_roi_report",
|
|
623
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
534
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.",
|
|
535
625
|
inputSchema: {
|
|
536
626
|
type: "object",
|
|
@@ -543,6 +633,7 @@ const toolDefinitions = [
|
|
|
543
633
|
},
|
|
544
634
|
{
|
|
545
635
|
name: "surgical_ast_insert",
|
|
636
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
546
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.",
|
|
547
638
|
inputSchema: {
|
|
548
639
|
type: "object",
|
|
@@ -558,6 +649,7 @@ const toolDefinitions = [
|
|
|
558
649
|
},
|
|
559
650
|
{
|
|
560
651
|
name: "local_semantic_autofix",
|
|
652
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
561
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.",
|
|
562
654
|
inputSchema: {
|
|
563
655
|
type: "object",
|
|
@@ -569,6 +661,7 @@ const toolDefinitions = [
|
|
|
569
661
|
},
|
|
570
662
|
{
|
|
571
663
|
name: "compress_context",
|
|
664
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
572
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.",
|
|
573
666
|
inputSchema: {
|
|
574
667
|
type: "object",
|
|
@@ -585,6 +678,7 @@ const toolDefinitions = [
|
|
|
585
678
|
},
|
|
586
679
|
{
|
|
587
680
|
name: "smarter_cache",
|
|
681
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
588
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.",
|
|
589
683
|
inputSchema: {
|
|
590
684
|
type: "object",
|
|
@@ -595,9 +689,25 @@ const toolDefinitions = [
|
|
|
595
689
|
},
|
|
596
690
|
required: ["query"],
|
|
597
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
|
+
},
|
|
598
707
|
},
|
|
599
708
|
{
|
|
600
709
|
name: "state_hash_cache",
|
|
710
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
601
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.",
|
|
602
712
|
inputSchema: {
|
|
603
713
|
type: "object",
|
|
@@ -609,9 +719,24 @@ const toolDefinitions = [
|
|
|
609
719
|
},
|
|
610
720
|
required: ["action", "query", "filePaths"],
|
|
611
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
|
+
},
|
|
612
736
|
},
|
|
613
737
|
{
|
|
614
738
|
name: "token_receipt",
|
|
739
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
615
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.",
|
|
616
741
|
inputSchema: {
|
|
617
742
|
type: "object",
|
|
@@ -620,9 +745,42 @@ const toolDefinitions = [
|
|
|
620
745
|
limit: { type: "number", description: "Cuántos eventos recientes incluir en el detalle", default: 20 },
|
|
621
746
|
},
|
|
622
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
|
+
},
|
|
623
780
|
},
|
|
624
781
|
{
|
|
625
782
|
name: "token_budget",
|
|
783
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
626
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.",
|
|
627
785
|
inputSchema: {
|
|
628
786
|
type: "object",
|
|
@@ -631,6 +789,7 @@ const toolDefinitions = [
|
|
|
631
789
|
},
|
|
632
790
|
{
|
|
633
791
|
name: "entropy_score",
|
|
792
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
634
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.",
|
|
635
794
|
inputSchema: {
|
|
636
795
|
type: "object",
|
|
@@ -642,6 +801,7 @@ const toolDefinitions = [
|
|
|
642
801
|
},
|
|
643
802
|
{
|
|
644
803
|
name: "coupling_radar",
|
|
804
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
645
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.",
|
|
646
806
|
inputSchema: {
|
|
647
807
|
type: "object",
|
|
@@ -653,6 +813,7 @@ const toolDefinitions = [
|
|
|
653
813
|
},
|
|
654
814
|
{
|
|
655
815
|
name: "pattern_fossil",
|
|
816
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
656
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.",
|
|
657
818
|
inputSchema: {
|
|
658
819
|
type: "object",
|
|
@@ -663,6 +824,7 @@ const toolDefinitions = [
|
|
|
663
824
|
},
|
|
664
825
|
{
|
|
665
826
|
name: "git_heatmap_risk",
|
|
827
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
666
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.",
|
|
667
829
|
inputSchema: {
|
|
668
830
|
type: "object",
|
|
@@ -674,6 +836,7 @@ const toolDefinitions = [
|
|
|
674
836
|
},
|
|
675
837
|
{
|
|
676
838
|
name: "precrime_static",
|
|
839
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
677
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.",
|
|
678
841
|
inputSchema: {
|
|
679
842
|
type: "object",
|
|
@@ -686,6 +849,7 @@ const toolDefinitions = [
|
|
|
686
849
|
},
|
|
687
850
|
{
|
|
688
851
|
name: "semantic_dedup_guard",
|
|
852
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
689
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.",
|
|
690
854
|
inputSchema: {
|
|
691
855
|
type: "object",
|
|
@@ -699,6 +863,7 @@ const toolDefinitions = [
|
|
|
699
863
|
},
|
|
700
864
|
{
|
|
701
865
|
name: "dead_export_necromancer",
|
|
866
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
702
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.",
|
|
703
868
|
inputSchema: {
|
|
704
869
|
type: "object",
|
|
@@ -709,6 +874,7 @@ const toolDefinitions = [
|
|
|
709
874
|
},
|
|
710
875
|
{
|
|
711
876
|
name: "review_diff",
|
|
877
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
712
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.",
|
|
713
879
|
inputSchema: {
|
|
714
880
|
type: "object",
|
|
@@ -722,6 +888,7 @@ const toolDefinitions = [
|
|
|
722
888
|
},
|
|
723
889
|
{
|
|
724
890
|
name: "review_pr",
|
|
891
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
725
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.",
|
|
726
893
|
inputSchema: {
|
|
727
894
|
type: "object",
|
|
@@ -738,6 +905,7 @@ const toolDefinitions = [
|
|
|
738
905
|
},
|
|
739
906
|
{
|
|
740
907
|
name: "pr_status",
|
|
908
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
741
909
|
description: "Get the status and detailed results of a previously reviewed PR. Returns score, verdict, findings, and approval status.",
|
|
742
910
|
inputSchema: {
|
|
743
911
|
type: "object",
|
|
@@ -751,6 +919,7 @@ const toolDefinitions = [
|
|
|
751
919
|
},
|
|
752
920
|
{
|
|
753
921
|
name: "generate_pr_workflow",
|
|
922
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
754
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.",
|
|
755
924
|
inputSchema: {
|
|
756
925
|
type: "object",
|
|
@@ -765,6 +934,7 @@ const toolDefinitions = [
|
|
|
765
934
|
},
|
|
766
935
|
{
|
|
767
936
|
name: "depgraph",
|
|
937
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
768
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.",
|
|
769
939
|
inputSchema: {
|
|
770
940
|
type: "object",
|
|
@@ -781,6 +951,7 @@ const toolDefinitions = [
|
|
|
781
951
|
},
|
|
782
952
|
{
|
|
783
953
|
name: "affected_tests",
|
|
954
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
784
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).",
|
|
785
956
|
inputSchema: {
|
|
786
957
|
type: "object",
|
|
@@ -789,9 +960,24 @@ const toolDefinitions = [
|
|
|
789
960
|
baseRef: { type: "string", description: "Additional git ref to diff against, e.g. 'main'. The working tree is always included." },
|
|
790
961
|
},
|
|
791
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
|
+
},
|
|
792
977
|
},
|
|
793
978
|
{
|
|
794
979
|
name: "refactor",
|
|
980
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
795
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.",
|
|
796
982
|
inputSchema: {
|
|
797
983
|
type: "object",
|
|
@@ -809,6 +995,7 @@ const toolDefinitions = [
|
|
|
809
995
|
},
|
|
810
996
|
{
|
|
811
997
|
name: "smart_file_slice",
|
|
998
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
812
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.",
|
|
813
1000
|
inputSchema: {
|
|
814
1001
|
type: "object",
|
|
@@ -822,6 +1009,7 @@ const toolDefinitions = [
|
|
|
822
1009
|
},
|
|
823
1010
|
{
|
|
824
1011
|
name: "test_oracle",
|
|
1012
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
825
1013
|
description: "Execute unit tests locally and output ONLY the failing tests and compressed stack traces, keeping context clean.",
|
|
826
1014
|
inputSchema: {
|
|
827
1015
|
type: "object",
|
|
@@ -832,6 +1020,7 @@ const toolDefinitions = [
|
|
|
832
1020
|
},
|
|
833
1021
|
{
|
|
834
1022
|
name: "schema_extract",
|
|
1023
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
835
1024
|
description: "Extract high-level schemas, Zod types, interfaces, or database models from a file using the TypeScript AST, removing all implementation code.",
|
|
836
1025
|
inputSchema: {
|
|
837
1026
|
type: "object",
|
|
@@ -843,6 +1032,7 @@ const toolDefinitions = [
|
|
|
843
1032
|
},
|
|
844
1033
|
{
|
|
845
1034
|
name: "changelog_auto",
|
|
1035
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
846
1036
|
description: "Generate a clean, token-efficient changelog summary from git logs based on Conventional Commits.",
|
|
847
1037
|
inputSchema: {
|
|
848
1038
|
type: "object",
|
|
@@ -853,6 +1043,7 @@ const toolDefinitions = [
|
|
|
853
1043
|
},
|
|
854
1044
|
{
|
|
855
1045
|
name: "spec_to_stub",
|
|
1046
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
856
1047
|
description: "Parse a TypeScript interface/type definition from a file and output a template mock/stub object configuration using pure AST analysis.",
|
|
857
1048
|
inputSchema: {
|
|
858
1049
|
type: "object",
|
|
@@ -865,6 +1056,7 @@ const toolDefinitions = [
|
|
|
865
1056
|
},
|
|
866
1057
|
{
|
|
867
1058
|
name: "env_snapshot",
|
|
1059
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
868
1060
|
description: "Capture environmental state like Node version, dependencies in package.json, and variables declared in .env files (hiding actual values/credentials).",
|
|
869
1061
|
inputSchema: {
|
|
870
1062
|
type: "object",
|
|
@@ -873,6 +1065,7 @@ const toolDefinitions = [
|
|
|
873
1065
|
},
|
|
874
1066
|
{
|
|
875
1067
|
name: "migration_tracer",
|
|
1068
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
876
1069
|
description: "Inspect changes in database schemas (tables, columns) across local migrations (e.g. Prisma migration directories).",
|
|
877
1070
|
inputSchema: {
|
|
878
1071
|
type: "object",
|
|
@@ -883,6 +1076,7 @@ const toolDefinitions = [
|
|
|
883
1076
|
},
|
|
884
1077
|
{
|
|
885
1078
|
name: "multi_file_patch",
|
|
1079
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
886
1080
|
description: "Apply search-and-replace patches to multiple workspace files in a single turn. Decreases round-trips for multi-file refactoring.",
|
|
887
1081
|
inputSchema: {
|
|
888
1082
|
type: "object",
|
|
@@ -907,6 +1101,7 @@ const toolDefinitions = [
|
|
|
907
1101
|
},
|
|
908
1102
|
{
|
|
909
1103
|
name: "file_intent_index",
|
|
1104
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
910
1105
|
description: "Map and index files inside the workspace by domain intent (e.g., auth, billing, routes, config) to speed up navigation.",
|
|
911
1106
|
inputSchema: {
|
|
912
1107
|
type: "object",
|
|
@@ -917,6 +1112,7 @@ const toolDefinitions = [
|
|
|
917
1112
|
},
|
|
918
1113
|
{
|
|
919
1114
|
name: "cognitive_map",
|
|
1115
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
920
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.",
|
|
921
1117
|
inputSchema: {
|
|
922
1118
|
type: "object",
|
|
@@ -939,6 +1135,7 @@ const toolDefinitions = [
|
|
|
939
1135
|
},
|
|
940
1136
|
{
|
|
941
1137
|
name: "semantic_grep",
|
|
1138
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
942
1139
|
description: "Search the codebase for conceptual terms using natural language and BM25 token relevance instead of exact substring matching.",
|
|
943
1140
|
inputSchema: {
|
|
944
1141
|
type: "object",
|
|
@@ -952,6 +1149,7 @@ const toolDefinitions = [
|
|
|
952
1149
|
},
|
|
953
1150
|
{
|
|
954
1151
|
name: "imports_skeleton_resolver",
|
|
1152
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
955
1153
|
description: "Resolve all imported local files inside a file and print only their signatures (classes, functions, interfaces) in a consolidated view.",
|
|
956
1154
|
inputSchema: {
|
|
957
1155
|
type: "object",
|
|
@@ -963,6 +1161,7 @@ const toolDefinitions = [
|
|
|
963
1161
|
},
|
|
964
1162
|
{
|
|
965
1163
|
name: "ast_flow_visualizer",
|
|
1164
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
966
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.",
|
|
967
1166
|
inputSchema: {
|
|
968
1167
|
type: "object",
|
|
@@ -975,6 +1174,7 @@ const toolDefinitions = [
|
|
|
975
1174
|
},
|
|
976
1175
|
{
|
|
977
1176
|
name: "file_fingerprint",
|
|
1177
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
978
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.",
|
|
979
1179
|
inputSchema: {
|
|
980
1180
|
type: "object",
|
|
@@ -987,6 +1187,7 @@ const toolDefinitions = [
|
|
|
987
1187
|
},
|
|
988
1188
|
{
|
|
989
1189
|
name: "git_blame_heat",
|
|
1190
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
990
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.",
|
|
991
1192
|
inputSchema: {
|
|
992
1193
|
type: "object",
|
|
@@ -998,6 +1199,7 @@ const toolDefinitions = [
|
|
|
998
1199
|
},
|
|
999
1200
|
{
|
|
1000
1201
|
name: "type_coverage_report",
|
|
1202
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1001
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.",
|
|
1002
1204
|
inputSchema: {
|
|
1003
1205
|
type: "object",
|
|
@@ -1009,6 +1211,7 @@ const toolDefinitions = [
|
|
|
1009
1211
|
},
|
|
1010
1212
|
{
|
|
1011
1213
|
name: "import_tree_context",
|
|
1214
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1012
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.",
|
|
1013
1216
|
inputSchema: {
|
|
1014
1217
|
type: "object",
|
|
@@ -1021,6 +1224,7 @@ const toolDefinitions = [
|
|
|
1021
1224
|
},
|
|
1022
1225
|
{
|
|
1023
1226
|
name: "read_token_budgeted",
|
|
1227
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1024
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.",
|
|
1025
1229
|
inputSchema: {
|
|
1026
1230
|
type: "object",
|
|
@@ -1034,6 +1238,7 @@ const toolDefinitions = [
|
|
|
1034
1238
|
},
|
|
1035
1239
|
{
|
|
1036
1240
|
name: "bulk_file_digest",
|
|
1241
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1037
1242
|
description: "Read multiple files in one call and return a compressed digest. Saves 80% round-trips vs reading files individually.",
|
|
1038
1243
|
inputSchema: {
|
|
1039
1244
|
type: "object",
|
|
@@ -1051,6 +1256,7 @@ const toolDefinitions = [
|
|
|
1051
1256
|
},
|
|
1052
1257
|
{
|
|
1053
1258
|
name: "auto_context_bundle",
|
|
1259
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1054
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.",
|
|
1055
1261
|
inputSchema: {
|
|
1056
1262
|
type: "object",
|
|
@@ -1062,6 +1268,7 @@ const toolDefinitions = [
|
|
|
1062
1268
|
},
|
|
1063
1269
|
{
|
|
1064
1270
|
name: "repeat_guard",
|
|
1271
|
+
annotations: { readOnlyHint: true, openWorldHint: false },
|
|
1065
1272
|
description: "Check if a draft output is repetitive compared to recent responses. Returns similarity score and suggestion. Prevents duplicate explanations.",
|
|
1066
1273
|
inputSchema: {
|
|
1067
1274
|
type: "object",
|
|
@@ -1074,6 +1281,7 @@ const toolDefinitions = [
|
|
|
1074
1281
|
},
|
|
1075
1282
|
{
|
|
1076
1283
|
name: "conversation_checkpoint",
|
|
1284
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
1077
1285
|
description: "Save or load session state. Prevents re-explaining context across turns. Save current progress or load last checkpoint.",
|
|
1078
1286
|
inputSchema: {
|
|
1079
1287
|
type: "object",
|
|
@@ -1089,6 +1297,7 @@ const toolDefinitions = [
|
|
|
1089
1297
|
},
|
|
1090
1298
|
{
|
|
1091
1299
|
name: "test_autofix_interceptor",
|
|
1300
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
1092
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.",
|
|
1093
1302
|
inputSchema: {
|
|
1094
1303
|
type: "object",
|
|
@@ -1099,6 +1308,7 @@ const toolDefinitions = [
|
|
|
1099
1308
|
},
|
|
1100
1309
|
{
|
|
1101
1310
|
name: "prompt_pattern_cache",
|
|
1311
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
1102
1312
|
description: "Find or store optimized prompt templates. Reuses past prompt patterns to save 20-30% tokens on repetitive operations.",
|
|
1103
1313
|
inputSchema: {
|
|
1104
1314
|
type: "object",
|
|
@@ -1118,6 +1328,7 @@ const _infraToolDefs = (0, mcp_tools_1.getInfraToolDefinitions)();
|
|
|
1118
1328
|
// than in the main array because it must never itself be hidden.
|
|
1119
1329
|
const TOOLBOX_TOOL_DEFINITION = {
|
|
1120
1330
|
name: "lemma_toolbox",
|
|
1331
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
1121
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.",
|
|
1122
1333
|
inputSchema: {
|
|
1123
1334
|
type: "object",
|
|
@@ -1323,6 +1534,8 @@ function receiptLabelMeta(name, args) {
|
|
|
1323
1534
|
return meta;
|
|
1324
1535
|
}
|
|
1325
1536
|
function setupToolsHandlers(server, onToolCall) {
|
|
1537
|
+
mcpServerRef = server;
|
|
1538
|
+
(0, SpeculativeWarmer_1.registerSymbolExtractor)(extractSymbolsWithTsCompiler);
|
|
1326
1539
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
1327
1540
|
tools: toolDefinitionsArray,
|
|
1328
1541
|
}));
|
|
@@ -1518,7 +1731,8 @@ async function handleGetRoutingAdvice(args) {
|
|
|
1518
1731
|
const intendedModel = args?.intended_model;
|
|
1519
1732
|
const router = new ComplexityRouter_1.ComplexityRouter();
|
|
1520
1733
|
const decision = router.evaluate(prompt, intendedModel);
|
|
1521
|
-
|
|
1734
|
+
const switched = !!intendedModel && decision.model !== intendedModel;
|
|
1735
|
+
if (decision.complexity === "low" && switched) {
|
|
1522
1736
|
const estimatedPromptTokens = Math.floor((prompt?.length || 0) / 4);
|
|
1523
1737
|
(0, reportSavings_1.reportSavings)({
|
|
1524
1738
|
source: "complexityRouting",
|
|
@@ -1527,6 +1741,12 @@ async function handleGetRoutingAdvice(args) {
|
|
|
1527
1741
|
query: prompt?.substring(0, 100),
|
|
1528
1742
|
});
|
|
1529
1743
|
}
|
|
1744
|
+
const structured = {
|
|
1745
|
+
recommendedModel: decision.model,
|
|
1746
|
+
complexity: decision.complexity,
|
|
1747
|
+
intendedModel: intendedModel || null,
|
|
1748
|
+
switched,
|
|
1749
|
+
};
|
|
1530
1750
|
return {
|
|
1531
1751
|
content: [
|
|
1532
1752
|
{
|
|
@@ -1534,6 +1754,7 @@ async function handleGetRoutingAdvice(args) {
|
|
|
1534
1754
|
text: `Lemma Routing Advice: Use ${decision.model}. Reason: Complexity is ${decision.complexity}.`,
|
|
1535
1755
|
},
|
|
1536
1756
|
],
|
|
1757
|
+
structuredContent: structured,
|
|
1537
1758
|
};
|
|
1538
1759
|
}
|
|
1539
1760
|
async function handleAutoHeal(args) {
|
|
@@ -1654,6 +1875,7 @@ async function handleReadWorkspaceFile(args) {
|
|
|
1654
1875
|
filePath,
|
|
1655
1876
|
});
|
|
1656
1877
|
(0, ReadWorkspaceCache_1.recordSent)(resolved, view, dedup.hash, content);
|
|
1878
|
+
(0, SpeculativeWarmer_1.warmNeighbors)(workspaceRoot, resolved);
|
|
1657
1879
|
return {
|
|
1658
1880
|
content: [
|
|
1659
1881
|
{
|
|
@@ -1672,6 +1894,7 @@ async function handleReadWorkspaceFile(args) {
|
|
|
1672
1894
|
}
|
|
1673
1895
|
}
|
|
1674
1896
|
(0, ReadWorkspaceCache_1.recordSent)(resolved, view, dedup.hash, content);
|
|
1897
|
+
(0, SpeculativeWarmer_1.warmNeighbors)(workspaceRoot, resolved);
|
|
1675
1898
|
return { content: [{ type: "text", text: rangeHeader + content }] };
|
|
1676
1899
|
}
|
|
1677
1900
|
catch (err) {
|
|
@@ -1804,6 +2027,11 @@ async function handleApplyWorkspacePatch(args) {
|
|
|
1804
2027
|
const updatedLines = [...contentLines.slice(0, match.startLine), ...finalReplace.split("\n"), ...contentLines.slice(match.endLine + 1)];
|
|
1805
2028
|
updatedContent = updatedLines.join("\n");
|
|
1806
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
|
+
}
|
|
1807
2035
|
fs_1.default.writeFileSync(resolved, updatedContent, "utf8");
|
|
1808
2036
|
const note = match.strategy === "exact" ? "" : ` (matched via ${match.strategy}, score ${match.score.toFixed(2)})`;
|
|
1809
2037
|
return { content: [{ type: "text", text: `Success: Patch successfully applied to ${filePath}${note}` }] };
|
|
@@ -1853,6 +2081,10 @@ async function handleRunWorkspaceCommand(args) {
|
|
|
1853
2081
|
const MAX_TIMEOUT_MS = 600000;
|
|
1854
2082
|
const requested = typeof args?.timeoutMs === "number" && args.timeoutMs > 0 ? args.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
1855
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
|
+
}
|
|
1856
2088
|
const result = (0, child_process_1.spawnSync)(command, {
|
|
1857
2089
|
cwd: workspaceRoot,
|
|
1858
2090
|
shell: true,
|
|
@@ -2409,7 +2641,7 @@ function walkDirForHologram(dir, rel, extensions, stats) {
|
|
|
2409
2641
|
const ext = entry.name.split(".").pop() || "";
|
|
2410
2642
|
if (extensions.includes(ext)) {
|
|
2411
2643
|
stats.filesScanned++;
|
|
2412
|
-
allSymbols = allSymbols.concat(
|
|
2644
|
+
allSymbols = allSymbols.concat((0, AstSymbolCache_1.getOrExtractSymbols)(fullPath, relPath, extractSymbolsWithTsCompiler, stats.parseErrors));
|
|
2413
2645
|
}
|
|
2414
2646
|
}
|
|
2415
2647
|
}
|
|
@@ -2680,13 +2912,16 @@ async function handlePruneConversationHistory(args) {
|
|
|
2680
2912
|
const messages = args?.messages;
|
|
2681
2913
|
if (!messages)
|
|
2682
2914
|
throw new Error("messages is required");
|
|
2915
|
+
const force = !!args?.force;
|
|
2683
2916
|
try {
|
|
2684
|
-
const result = (0, ContextSqueezer_1.pruneHistoryMessages)(messages);
|
|
2685
|
-
(
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2917
|
+
const result = (0, ContextSqueezer_1.pruneHistoryMessages)(messages, { force });
|
|
2918
|
+
if (result.applied) {
|
|
2919
|
+
(0, reportSavings_1.reportSavings)({
|
|
2920
|
+
source: "historyPrune",
|
|
2921
|
+
tokens: result.tokensSavedEstimate,
|
|
2922
|
+
toolName: "prune_conversation_history",
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2690
2925
|
return {
|
|
2691
2926
|
content: [
|
|
2692
2927
|
{
|
|
@@ -2694,6 +2929,9 @@ async function handlePruneConversationHistory(args) {
|
|
|
2694
2929
|
text: JSON.stringify({
|
|
2695
2930
|
messages: result.messages,
|
|
2696
2931
|
tokensSavedEstimate: result.tokensSavedEstimate,
|
|
2932
|
+
cacheInvalidationEstimate: result.cacheInvalidationEstimate,
|
|
2933
|
+
applied: result.applied,
|
|
2934
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
2697
2935
|
}, null, 2),
|
|
2698
2936
|
},
|
|
2699
2937
|
],
|
|
@@ -2869,47 +3107,44 @@ async function handleSmarterCache(args) {
|
|
|
2869
3107
|
reportSavings({ source: "cache", tokens: tokensSaved, toolName: "smarter_cache", query: query.substring(0, 100) });
|
|
2870
3108
|
}
|
|
2871
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
|
+
};
|
|
2872
3119
|
return {
|
|
2873
|
-
content: [{
|
|
2874
|
-
|
|
2875
|
-
text: JSON.stringify({
|
|
2876
|
-
hit: true,
|
|
2877
|
-
similarity: topHit.similarity,
|
|
2878
|
-
threshold,
|
|
2879
|
-
answer: responseText,
|
|
2880
|
-
source: "lemma-brain",
|
|
2881
|
-
tokensSaved,
|
|
2882
|
-
tokensSavedFormatted: `~${tokensSaved.toLocaleString()} tokens`,
|
|
2883
|
-
}, null, 2),
|
|
2884
|
-
}],
|
|
3120
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3121
|
+
structuredContent: structured,
|
|
2885
3122
|
};
|
|
2886
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
|
+
};
|
|
2887
3132
|
return {
|
|
2888
|
-
content: [{
|
|
2889
|
-
|
|
2890
|
-
text: JSON.stringify({
|
|
2891
|
-
hit: false,
|
|
2892
|
-
similarity: topHit?.similarity || 0,
|
|
2893
|
-
threshold,
|
|
2894
|
-
answer: null,
|
|
2895
|
-
source: "llm-call-required",
|
|
2896
|
-
hint: "Después de resolver, llama store_memory para cachear y no gastar tokens en esto otra vez.",
|
|
2897
|
-
}, null, 2),
|
|
2898
|
-
}],
|
|
3133
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3134
|
+
structuredContent: structured,
|
|
2899
3135
|
};
|
|
2900
3136
|
}
|
|
2901
3137
|
catch (e) {
|
|
2902
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
|
+
};
|
|
2903
3145
|
return {
|
|
2904
|
-
content: [{
|
|
2905
|
-
|
|
2906
|
-
text: JSON.stringify({
|
|
2907
|
-
hit: false,
|
|
2908
|
-
error: e.message,
|
|
2909
|
-
source: "cache-unavailable",
|
|
2910
|
-
hint: "El cache local del brain falló. El LLM funcionará normalmente sin cache.",
|
|
2911
|
-
}, null, 2),
|
|
2912
|
-
}],
|
|
3146
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3147
|
+
structuredContent: structured,
|
|
2913
3148
|
};
|
|
2914
3149
|
}
|
|
2915
3150
|
}
|
|
@@ -2930,23 +3165,21 @@ async function handleStateHashCache(args) {
|
|
|
2930
3165
|
(0, reportSavings_1.reportSavings)({ source: "cache", tokens: tokensSaved, toolName: "state_hash_cache", query: query.substring(0, 100) });
|
|
2931
3166
|
}
|
|
2932
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
|
+
};
|
|
2933
3174
|
return {
|
|
2934
|
-
content: [{
|
|
2935
|
-
|
|
2936
|
-
text: JSON.stringify({
|
|
2937
|
-
status: "hit",
|
|
2938
|
-
answer: result.entry.answer,
|
|
2939
|
-
note: "Respuesta desde caché exacto (hash de archivos sin cambios) — no necesita re-razonar.",
|
|
2940
|
-
tokensSaved,
|
|
2941
|
-
}, null, 2),
|
|
2942
|
-
}],
|
|
3175
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3176
|
+
structuredContent: structured,
|
|
2943
3177
|
};
|
|
2944
3178
|
}
|
|
3179
|
+
const structured = { status: "miss", reason: result.reason, hint: "Razona normalmente y luego llama action='store' con la respuesta." };
|
|
2945
3180
|
return {
|
|
2946
|
-
content: [{
|
|
2947
|
-
|
|
2948
|
-
text: JSON.stringify({ status: "miss", reason: result.reason, hint: "Razona normalmente y luego llama action='store' con la respuesta." }, null, 2),
|
|
2949
|
-
}],
|
|
3181
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3182
|
+
structuredContent: structured,
|
|
2950
3183
|
};
|
|
2951
3184
|
}
|
|
2952
3185
|
if (action === "store") {
|
|
@@ -2954,8 +3187,10 @@ async function handleStateHashCache(args) {
|
|
|
2954
3187
|
if (!answer)
|
|
2955
3188
|
throw new Error("answer is required for action='store'");
|
|
2956
3189
|
const entry = (0, StateHashCache_1.storeStateHash)(workspaceRoot, query, answer, filePaths);
|
|
3190
|
+
const structured = { status: "stored", id: entry.id, filesTracked: Object.keys(entry.fileHashes) };
|
|
2957
3191
|
return {
|
|
2958
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
3192
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3193
|
+
structuredContent: structured,
|
|
2959
3194
|
};
|
|
2960
3195
|
}
|
|
2961
3196
|
throw new Error(`Unknown action: ${action}. Use 'lookup' or 'store'.`);
|
|
@@ -2964,16 +3199,15 @@ async function handleStateHashCache(args) {
|
|
|
2964
3199
|
async function handleTokenReceipt(args) {
|
|
2965
3200
|
const limit = typeof args?.limit === "number" ? args.limit : 20;
|
|
2966
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
|
+
};
|
|
2967
3208
|
return {
|
|
2968
|
-
content: [{
|
|
2969
|
-
|
|
2970
|
-
text: JSON.stringify({
|
|
2971
|
-
totalEvents: summary.totalEvents,
|
|
2972
|
-
byType: summary.byType,
|
|
2973
|
-
recentEvents: summary.recent,
|
|
2974
|
-
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.",
|
|
2975
|
-
}, null, 2),
|
|
2976
|
-
}],
|
|
3209
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
3210
|
+
structuredContent: structured,
|
|
2977
3211
|
};
|
|
2978
3212
|
}
|
|
2979
3213
|
// ── Token Budget ─────────────────────────────────────────────────
|
|
@@ -4257,7 +4491,17 @@ async function handleAffectedTests(args) {
|
|
|
4257
4491
|
totalTests: result.totalTests,
|
|
4258
4492
|
narrowed: result.command !== null,
|
|
4259
4493
|
});
|
|
4260
|
-
|
|
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 };
|
|
4261
4505
|
}
|
|
4262
4506
|
catch (err) {
|
|
4263
4507
|
(0, utils_1.logError)("affected_tests", err);
|