@nxuss/lemma 1.6.0 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +114 -5
  2. package/bin/checkpoint.js +49 -0
  3. package/bin/init.js +21 -2
  4. package/dist/cjs/mcp/prompts.d.ts +1 -2
  5. package/dist/cjs/mcp/prompts.d.ts.map +1 -1
  6. package/dist/cjs/mcp/prompts.js +29 -126
  7. package/dist/cjs/mcp/prompts.js.map +1 -1
  8. package/dist/cjs/mcp/resources.d.ts.map +1 -1
  9. package/dist/cjs/mcp/resources.js +4 -4
  10. package/dist/cjs/mcp/resources.js.map +1 -1
  11. package/dist/cjs/mcp/tool-profiles.d.ts +55 -0
  12. package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -0
  13. package/dist/cjs/mcp/tool-profiles.js +204 -0
  14. package/dist/cjs/mcp/tool-profiles.js.map +1 -0
  15. package/dist/cjs/mcp/tools.d.ts +12 -0
  16. package/dist/cjs/mcp/tools.d.ts.map +1 -1
  17. package/dist/cjs/mcp/tools.js +175 -26
  18. package/dist/cjs/mcp/tools.js.map +1 -1
  19. package/dist/cjs/subconscious/TheBrainV2.d.ts +36 -2
  20. package/dist/cjs/subconscious/TheBrainV2.d.ts.map +1 -1
  21. package/dist/cjs/subconscious/TheBrainV2.js +104 -23
  22. package/dist/cjs/subconscious/TheBrainV2.js.map +1 -1
  23. package/dist/cjs/utils/AutoContextBundle.d.ts.map +1 -1
  24. package/dist/cjs/utils/AutoContextBundle.js +17 -20
  25. package/dist/cjs/utils/AutoContextBundle.js.map +1 -1
  26. package/dist/cjs/utils/ConversationCheckpoint.d.ts +13 -0
  27. package/dist/cjs/utils/ConversationCheckpoint.d.ts.map +1 -1
  28. package/dist/cjs/utils/ConversationCheckpoint.js +58 -0
  29. package/dist/cjs/utils/ConversationCheckpoint.js.map +1 -1
  30. package/dist/cjs/utils/TestAutofixInterceptor.d.ts.map +1 -1
  31. package/dist/cjs/utils/TestAutofixInterceptor.js +15 -26
  32. package/dist/cjs/utils/TestAutofixInterceptor.js.map +1 -1
  33. package/dist/esm/api/dashboardRoutes.d.ts +3 -3
  34. package/dist/esm/api/dashboardRoutes.js +1 -1
  35. package/dist/esm/api/server.d.ts +4 -4
  36. package/dist/esm/api/server.js +3 -3
  37. package/dist/esm/autopilot/AutopilotWatcher.js +3 -3
  38. package/dist/esm/autopilot/Notifier.d.ts +1 -1
  39. package/dist/esm/cli/lemma-proxy.js +14 -14
  40. package/dist/esm/cli/stack.d.ts +4 -4
  41. package/dist/esm/cli/stack.js +5 -5
  42. package/dist/esm/cloud/KeyManager.d.ts +1 -1
  43. package/dist/esm/cloud/KeyManager.js +1 -1
  44. package/dist/esm/cloud/TenantCache.d.ts +1 -1
  45. package/dist/esm/cloud/TenantCache.js +1 -1
  46. package/dist/esm/cloud/index.d.ts +3 -3
  47. package/dist/esm/cloud/index.js +3 -3
  48. package/dist/esm/cloud/server.d.ts +3 -3
  49. package/dist/esm/cloud/server.js +5 -5
  50. package/dist/esm/config/index.d.ts +1 -1
  51. package/dist/esm/config/index.js +1 -1
  52. package/dist/esm/consensus/index.d.ts +2 -2
  53. package/dist/esm/consensus/index.js +2 -2
  54. package/dist/esm/core/AgentRegistry.d.ts +1 -1
  55. package/dist/esm/core/AgentRegistry.js +1 -1
  56. package/dist/esm/core/DashboardBroadcaster.d.ts +3 -3
  57. package/dist/esm/core/DashboardBroadcaster.js +2 -2
  58. package/dist/esm/core/DashboardWebSocketServer.d.ts +1 -1
  59. package/dist/esm/core/DashboardWebSocketServer.js +1 -1
  60. package/dist/esm/core/GhostAlchemist.js +1 -1
  61. package/dist/esm/core/GhostListener.js +2 -2
  62. package/dist/esm/core/OrchestrationEngine.d.ts +3 -3
  63. package/dist/esm/core/OrchestrationEngine.js +2 -2
  64. package/dist/esm/core/SubconsciousEngine.d.ts +1 -1
  65. package/dist/esm/core/SubconsciousEngine.js +2 -2
  66. package/dist/esm/core/WebSocketServer.d.ts +2 -2
  67. package/dist/esm/core/WebSocketServer.js +1 -1
  68. package/dist/esm/core/index.d.ts +6 -6
  69. package/dist/esm/core/index.js +6 -6
  70. package/dist/esm/core/router.d.ts +3 -3
  71. package/dist/esm/core/router.js +1 -1
  72. package/dist/esm/db/database.js +1 -1
  73. package/dist/esm/embed.d.ts +2 -2
  74. package/dist/esm/embed.js +2 -2
  75. package/dist/esm/examples/basic-usage.js +1 -1
  76. package/dist/esm/index.d.ts +14 -14
  77. package/dist/esm/index.js +14 -14
  78. package/dist/esm/infra/analyzers/DriftDetector.d.ts +1 -1
  79. package/dist/esm/infra/analyzers/InfraAnalyzer.d.ts +1 -1
  80. package/dist/esm/infra/analyzers/MonolithAnalyzer.d.ts +1 -1
  81. package/dist/esm/infra/analyzers/index.d.ts +3 -3
  82. package/dist/esm/infra/analyzers/index.js +3 -3
  83. package/dist/esm/infra/cli.js +2 -2
  84. package/dist/esm/infra/config.d.ts +1 -1
  85. package/dist/esm/infra/index.d.ts +5 -5
  86. package/dist/esm/infra/index.js +5 -5
  87. package/dist/esm/infra/mcp-tools.js +5 -5
  88. package/dist/esm/infra/providers/InfraProvider.d.ts +1 -1
  89. package/dist/esm/infra/providers/JenkinsProvider.d.ts +2 -2
  90. package/dist/esm/infra/providers/OKDProvider.d.ts +2 -2
  91. package/dist/esm/infra/providers/TerraformProvider.d.ts +2 -2
  92. package/dist/esm/infra/providers/index.d.ts +5 -5
  93. package/dist/esm/infra/providers/index.js +3 -3
  94. package/dist/esm/mcp/index.js +5 -5
  95. package/dist/esm/mcp/prompts.d.ts +1 -2
  96. package/dist/esm/mcp/prompts.d.ts.map +1 -1
  97. package/dist/esm/mcp/prompts.js +28 -125
  98. package/dist/esm/mcp/prompts.js.map +1 -1
  99. package/dist/esm/mcp/resources.d.ts.map +1 -1
  100. package/dist/esm/mcp/resources.js +5 -5
  101. package/dist/esm/mcp/resources.js.map +1 -1
  102. package/dist/esm/mcp/tool-profiles.d.ts +55 -0
  103. package/dist/esm/mcp/tool-profiles.d.ts.map +1 -0
  104. package/dist/esm/mcp/tool-profiles.js +196 -0
  105. package/dist/esm/mcp/tool-profiles.js.map +1 -0
  106. package/dist/esm/mcp/tools.d.ts +12 -0
  107. package/dist/esm/mcp/tools.d.ts.map +1 -1
  108. package/dist/esm/mcp/tools.js +209 -63
  109. package/dist/esm/mcp/tools.js.map +1 -1
  110. package/dist/esm/observability/IdeContextSync.d.ts +1 -1
  111. package/dist/esm/observability/IdeContextSync.js +5 -5
  112. package/dist/esm/pr-review/PRReviewEngine.d.ts +1 -1
  113. package/dist/esm/pr-review/PRReviewEngine.js +4 -4
  114. package/dist/esm/pr-review/ReviewStore.d.ts +1 -1
  115. package/dist/esm/pr-review/adapters/AzureDevOpsAdapter.d.ts +2 -2
  116. package/dist/esm/pr-review/adapters/AzureDevOpsAdapter.js +1 -1
  117. package/dist/esm/pr-review/adapters/GitHubAdapter.d.ts +2 -2
  118. package/dist/esm/pr-review/adapters/GitHubAdapter.js +1 -1
  119. package/dist/esm/pr-review/adapters/GitLabAdapter.d.ts +2 -2
  120. package/dist/esm/pr-review/adapters/GitLabAdapter.js +1 -1
  121. package/dist/esm/pr-review/adapters/GitPlatformAdapter.d.ts +1 -1
  122. package/dist/esm/pr-review/adapters/index.d.ts +4 -4
  123. package/dist/esm/pr-review/adapters/index.js +3 -3
  124. package/dist/esm/pr-review/analyzers/QualityGrader.d.ts +1 -1
  125. package/dist/esm/pr-review/analyzers/RegressionDetector.d.ts +1 -1
  126. package/dist/esm/pr-review/analyzers/SecurityScanner.d.ts +1 -1
  127. package/dist/esm/pr-review/analyzers/StaticAnalyzer.d.ts +1 -1
  128. package/dist/esm/pr-review/analyzers/index.d.ts +8 -8
  129. package/dist/esm/pr-review/analyzers/index.js +4 -4
  130. package/dist/esm/pr-review/bridge/BrainBridge.d.ts +1 -1
  131. package/dist/esm/pr-review/bridge/BrainBridge.js +1 -1
  132. package/dist/esm/pr-review/bridge/index.d.ts +1 -1
  133. package/dist/esm/pr-review/bridge/index.js +1 -1
  134. package/dist/esm/pr-review/index.d.ts +4 -4
  135. package/dist/esm/pr-review/index.js +2 -2
  136. package/dist/esm/pr-review/reviewRunner.d.ts +2 -2
  137. package/dist/esm/pr-review/reviewRunner.js +6 -6
  138. package/dist/esm/pr-review/webhook/WebhookServer.js +1 -1
  139. package/dist/esm/pr-review/webhook/index.d.ts +1 -1
  140. package/dist/esm/pr-review/webhook/index.js +1 -1
  141. package/dist/esm/protocol/flows.d.ts +1 -1
  142. package/dist/esm/protocol/flows.js +1 -1
  143. package/dist/esm/protocol/index.d.ts +4 -4
  144. package/dist/esm/protocol/index.js +4 -4
  145. package/dist/esm/protocol/utils.d.ts +1 -1
  146. package/dist/esm/protocol/utils.js +2 -2
  147. package/dist/esm/protocol/validators.d.ts +1 -1
  148. package/dist/esm/protocol/validators.js +1 -1
  149. package/dist/esm/proxy/ContextInjector.d.ts +1 -1
  150. package/dist/esm/proxy/Gatekeeper.d.ts +1 -1
  151. package/dist/esm/security/SecurityMiddleware.d.ts +1 -1
  152. package/dist/esm/security/SecurityMiddleware.js +3 -3
  153. package/dist/esm/security/index.d.ts +4 -4
  154. package/dist/esm/security/index.js +4 -4
  155. package/dist/esm/speculative/SpeculativeCache.d.ts +2 -2
  156. package/dist/esm/speculative/SpeculativeEngine.d.ts +2 -2
  157. package/dist/esm/speculative/SpeculativeEngine.js +3 -3
  158. package/dist/esm/speculative/WorkerPool.d.ts +1 -1
  159. package/dist/esm/speculative/index.d.ts +4 -4
  160. package/dist/esm/speculative/index.js +4 -4
  161. package/dist/esm/subconscious/SemanticCache.d.ts +2 -2
  162. package/dist/esm/subconscious/SemanticCache.js +2 -2
  163. package/dist/esm/subconscious/SubconsciousEngine.d.ts +1 -1
  164. package/dist/esm/subconscious/SubconsciousEngine.js +3 -3
  165. package/dist/esm/subconscious/TheBrainV2.d.ts +36 -2
  166. package/dist/esm/subconscious/TheBrainV2.d.ts.map +1 -1
  167. package/dist/esm/subconscious/TheBrainV2.js +103 -23
  168. package/dist/esm/subconscious/TheBrainV2.js.map +1 -1
  169. package/dist/esm/subconscious/index.d.ts +4 -4
  170. package/dist/esm/subconscious/index.js +4 -4
  171. package/dist/esm/utils/AutoContextBundle.d.ts +1 -1
  172. package/dist/esm/utils/AutoContextBundle.d.ts.map +1 -1
  173. package/dist/esm/utils/AutoContextBundle.js +20 -23
  174. package/dist/esm/utils/AutoContextBundle.js.map +1 -1
  175. package/dist/esm/utils/BulkFileDigest.js +2 -2
  176. package/dist/esm/utils/ClipboardWatcher.js +2 -2
  177. package/dist/esm/utils/ContextCollapser.js +1 -1
  178. package/dist/esm/utils/ConversationCheckpoint.d.ts +13 -0
  179. package/dist/esm/utils/ConversationCheckpoint.d.ts.map +1 -1
  180. package/dist/esm/utils/ConversationCheckpoint.js +57 -0
  181. package/dist/esm/utils/ConversationCheckpoint.js.map +1 -1
  182. package/dist/esm/utils/ExecutiveReport.js +1 -1
  183. package/dist/esm/utils/StructuralSearch.d.ts +1 -1
  184. package/dist/esm/utils/SurgicalASTInsert.js +1 -1
  185. package/dist/esm/utils/SymbolSurgicalContext.js +1 -1
  186. package/dist/esm/utils/TestAutofixInterceptor.d.ts.map +1 -1
  187. package/dist/esm/utils/TestAutofixInterceptor.js +15 -26
  188. package/dist/esm/utils/TestAutofixInterceptor.js.map +1 -1
  189. package/dist/esm/utils/WorkspaceSearch.js +0 -0
  190. package/dist/esm/utils/logger.js +1 -1
  191. package/package.json +9 -10
  192. package/sdks/crewai/README.md +178 -0
  193. package/sdks/crewai/dist/index.d.ts +130 -0
  194. package/sdks/crewai/dist/index.js +308 -0
@@ -5,39 +5,40 @@ import { execSync, spawnSync } from "child_process";
5
5
  import os from "os";
6
6
  import axios from "axios";
7
7
  import * as ts from "typescript";
8
- import { SemanticScrubber } from "../security/SemanticScrubber";
9
- import { ComplexityRouter } from "../proxy/ComplexityRouter";
10
- import { squeezeCode, squeezePrompt, pruneHistoryMessages, generateAstDiff } from "../utils/ContextSqueezer";
11
- import { reportSavings, reportCost } from "../utils/reportSavings";
12
- import { getProxyPort, safeResolvePath, logError, logWarn, estimateTokensFromResult, isCommandAllowed, loadAllowedCommands } from "./utils";
13
- import { computeFingerprint } from "../utils/FileFingerprint";
14
- import { analyzeBlameHeat } from "../utils/GitBlameHeat";
15
- import { analyzeTypeCoverage } from "../utils/TypeCoverage";
16
- import { buildImportTree } from "../utils/ImportTreeContext";
17
- import { readTokenBudgeted } from "../utils/TokenBudgetedReader";
18
- import { bulkFileDigest } from "../utils/BulkFileDigest";
19
- import { autoContextBundle } from "../utils/AutoContextBundle";
20
- import { checkRepeat, recordOutput } from "../utils/RepeatGuard";
21
- import { saveCheckpoint, loadLatestCheckpoint, buildCheckpointSummary } from "../utils/ConversationCheckpoint";
22
- import { runTestAutofix } from "../utils/TestAutofixInterceptor";
23
- import { findPattern, storePattern, getPatternStats } from "../utils/PromptPatternCache";
24
- import { runAnalysis, parseDiff } from "../pr-review/PRReviewEngine";
25
- import { ReviewStore } from "../pr-review/ReviewStore";
26
- import { runPRReview } from "../pr-review/reviewRunner";
27
- import { getBrain } from "../subconscious/TheBrainV2";
28
- import { collapseContext } from "../utils/ContextCollapser";
29
- import { getSymbolSurgicalContext } from "../utils/SymbolSurgicalContext";
30
- import { compressWormhole } from "../utils/WormholeCompressor";
31
- import { generateExecutiveReport } from "../utils/ExecutiveReport";
32
- import { surgicalASTInsert } from "../utils/SurgicalASTInsert";
33
- import { getInfraToolDefinitions, getInfraToolHandlers, INFRA_TOOL_NAMES } from "../infra/mcp-tools";
34
- import { lookupStateHash, storeStateHash } from "../utils/StateHashCache";
35
- import { recordReceiptEvent, getReceiptSummary } from "../utils/TokenReceipt";
36
- import { distillCommandOutput, buildDistillFooter, readRegion } from "../utils/CommandOutputDistiller";
37
- import { findMatch, reindentReplacement } from "../utils/PatchMatcher";
38
- import { searchWorkspace, groupSearchResults, parseExtensionFilter } from "../utils/WorkspaceSearch";
39
- import { checkAlreadySent, recordSent } from "../utils/ReadWorkspaceCache";
40
- import { getCachedSearch, setCachedSearch } from "../utils/SearchCache";
8
+ import { SemanticScrubber } from "../security/SemanticScrubber.js";
9
+ import { ComplexityRouter } from "../proxy/ComplexityRouter.js";
10
+ import { squeezeCode, squeezePrompt, pruneHistoryMessages, generateAstDiff } from "../utils/ContextSqueezer.js";
11
+ import { reportSavings, reportCost } from "../utils/reportSavings.js";
12
+ import { getProxyPort, safeResolvePath, logError, logWarn, estimateTokensFromResult, isCommandAllowed, loadAllowedCommands } from "./utils.js";
13
+ import { computeFingerprint } from "../utils/FileFingerprint.js";
14
+ import { analyzeBlameHeat } from "../utils/GitBlameHeat.js";
15
+ import { analyzeTypeCoverage } from "../utils/TypeCoverage.js";
16
+ import { buildImportTree } from "../utils/ImportTreeContext.js";
17
+ import { readTokenBudgeted } from "../utils/TokenBudgetedReader.js";
18
+ import { bulkFileDigest } from "../utils/BulkFileDigest.js";
19
+ import { autoContextBundle } from "../utils/AutoContextBundle.js";
20
+ import { checkRepeat, recordOutput } from "../utils/RepeatGuard.js";
21
+ import { saveCheckpoint, loadLatestCheckpoint, buildCheckpointSummary } from "../utils/ConversationCheckpoint.js";
22
+ import { runTestAutofix } from "../utils/TestAutofixInterceptor.js";
23
+ import { findPattern, storePattern, getPatternStats } from "../utils/PromptPatternCache.js";
24
+ import { runAnalysis, parseDiff } from "../pr-review/PRReviewEngine.js";
25
+ import { ReviewStore } from "../pr-review/ReviewStore.js";
26
+ import { runPRReview } from "../pr-review/reviewRunner.js";
27
+ import { getBrain, deriveProjectId } from "../subconscious/TheBrainV2.js";
28
+ import { collapseContext } from "../utils/ContextCollapser.js";
29
+ import { getSymbolSurgicalContext } from "../utils/SymbolSurgicalContext.js";
30
+ import { compressWormhole } from "../utils/WormholeCompressor.js";
31
+ import { generateExecutiveReport } from "../utils/ExecutiveReport.js";
32
+ import { surgicalASTInsert } from "../utils/SurgicalASTInsert.js";
33
+ import { getInfraToolDefinitions, getInfraToolHandlers, INFRA_TOOL_NAMES } from "../infra/mcp-tools.js";
34
+ import { resolveToolSurface, buildToolboxCatalog } from "./tool-profiles.js";
35
+ import { lookupStateHash, storeStateHash } from "../utils/StateHashCache.js";
36
+ import { recordReceiptEvent, getReceiptSummary } from "../utils/TokenReceipt.js";
37
+ import { distillCommandOutput, buildDistillFooter, readRegion } from "../utils/CommandOutputDistiller.js";
38
+ import { findMatch, reindentReplacement } from "../utils/PatchMatcher.js";
39
+ import { searchWorkspace, groupSearchResults, parseExtensionFilter } from "../utils/WorkspaceSearch.js";
40
+ import { checkAlreadySent, recordSent } from "../utils/ReadWorkspaceCache.js";
41
+ import { getCachedSearch, setCachedSearch } from "../utils/SearchCache.js";
41
42
  // ── Pro License Gate ──────────────────────────────────────────────────────────
42
43
  const LICENSE_FILE = path.join(os.homedir(), '.lemma-cache', 'license.json');
43
44
  let _proStatusCache = null;
@@ -62,6 +63,8 @@ const FREE_TOOLS = new Set([
62
63
  "token_budget", "squeeze_prompt", "turbosqueeze",
63
64
  // Utility — just enough to function
64
65
  "read_workspace_file", "scrub_privacy", "summarize_long_text",
66
+ // Discovery of withheld schemas — gating this would hide tools with no way back
67
+ "lemma_toolbox",
65
68
  // Phase 1 zero-cost analysis tools
66
69
  "file_fingerprint", "git_blame_heat", "type_coverage_report", "import_tree_context",
67
70
  // Phase 2 smart reading & dedup tools
@@ -990,8 +993,32 @@ const toolDefinitions = [
990
993
  ];
991
994
  // ── Infrastructure Tool Definitions ──────────────────────────────────────────
992
995
  const _infraToolDefs = getInfraToolDefinitions();
993
- const allToolDefinitions = [...toolDefinitions, ..._infraToolDefs];
994
- const toolDefinitionsArray = _infraToolDefs.length > 0 ? allToolDefinitions : toolDefinitions;
996
+ // `lemma_toolbox` keeps every non-advertised tool reachable. It is defined here rather
997
+ // than in the main array because it must never itself be hidden.
998
+ const TOOLBOX_TOOL_DEFINITION = {
999
+ name: "lemma_toolbox",
1000
+ 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.",
1001
+ inputSchema: {
1002
+ type: "object",
1003
+ properties: {
1004
+ action: { type: "string", enum: ["list", "schema", "call"], description: "list | schema | call", default: "list" },
1005
+ query: { type: "string", description: "Filter terms for action='list' (e.g. 'terraform drift')" },
1006
+ tool: { type: "string", description: "Tool name, for action='schema' or 'call'" },
1007
+ args: { type: "object", description: "Arguments passed through to the tool, for action='call'" },
1008
+ },
1009
+ required: ["action"],
1010
+ },
1011
+ };
1012
+ const allToolDefinitions = [
1013
+ ...toolDefinitions,
1014
+ ..._infraToolDefs,
1015
+ TOOLBOX_TOOL_DEFINITION,
1016
+ ];
1017
+ // Narrow what gets advertised to what this project can actually use. Hidden tools stay
1018
+ // registered in `toolHandlers` and callable — both directly and through `lemma_toolbox`.
1019
+ const _toolSurface = resolveToolSurface(allToolDefinitions, process.cwd());
1020
+ const toolDefinitionsArray = _toolSurface.advertised;
1021
+ const hiddenToolDefinitions = _toolSurface.hidden;
995
1022
  /** Serialized size of every tool schema sent to the client, for cost accounting. */
996
1023
  export function getToolSchemaChars() {
997
1024
  try {
@@ -1001,6 +1028,24 @@ export function getToolSchemaChars() {
1001
1028
  return 0;
1002
1029
  }
1003
1030
  }
1031
+ /** Every tool name with a registered handler, advertised or not. */
1032
+ export function getRegisteredToolNames() {
1033
+ return Object.keys(toolHandlers);
1034
+ }
1035
+ /** Tool names whose schemas this session sends to the client. */
1036
+ export function getAdvertisedToolNames() {
1037
+ return toolDefinitionsArray.map((d) => d.name);
1038
+ }
1039
+ /** Which tools this session advertises, and why — surfaced by `token_budget`. */
1040
+ export function getToolSurfaceSummary() {
1041
+ return {
1042
+ profile: _toolSurface.profile,
1043
+ advertised: toolDefinitionsArray.length,
1044
+ hidden: hiddenToolDefinitions.length,
1045
+ total: allToolDefinitions.length,
1046
+ activeGroups: _toolSurface.activeGroups,
1047
+ };
1048
+ }
1004
1049
  const toolHandlers = {
1005
1050
  scrub_privacy: handleScrubPrivacy,
1006
1051
  search_memory: handleSearchMemory,
@@ -1073,8 +1118,44 @@ const toolHandlers = {
1073
1118
  conversation_checkpoint: handleConversationCheckpoint,
1074
1119
  test_autofix_interceptor: handleTestAutofixInterceptor,
1075
1120
  prompt_pattern_cache: handlePromptPatternCache,
1121
+ lemma_toolbox: handleToolbox,
1076
1122
  ...getInfraToolHandlers(),
1077
1123
  };
1124
+ /**
1125
+ * Reaches the tools whose schemas were withheld from the session listing.
1126
+ *
1127
+ * `call` proxies through this server rather than relying on the client to emit a name it
1128
+ * never saw advertised — some clients validate outgoing tool names against tools/list, so
1129
+ * the proxy is what makes hiding a schema safe rather than a silent capability loss.
1130
+ */
1131
+ async function handleToolbox(args) {
1132
+ const action = args?.action || "list";
1133
+ if (action === "list") {
1134
+ return {
1135
+ content: [{ type: "text", text: buildToolboxCatalog(hiddenToolDefinitions, args?.query) }],
1136
+ };
1137
+ }
1138
+ const toolName = args?.tool;
1139
+ if (!toolName)
1140
+ throw new Error("`tool` is required for action '" + action + "'");
1141
+ if (action === "schema") {
1142
+ const def = allToolDefinitions.find((d) => d.name === toolName);
1143
+ if (!def)
1144
+ throw new Error(`Unknown tool: ${toolName}`);
1145
+ return { content: [{ type: "text", text: JSON.stringify(def, null, 2) }] };
1146
+ }
1147
+ if (action === "call") {
1148
+ const handler = toolHandlers[toolName];
1149
+ if (!handler)
1150
+ throw new Error(`Unknown tool: ${toolName}`);
1151
+ // The proxy must not become a way around the license gate the direct path enforces.
1152
+ if (!isProUser() && !FREE_TOOLS.has(toolName)) {
1153
+ return { content: [{ type: "text", text: PRO_GATE_MESSAGE }] };
1154
+ }
1155
+ return handler((args?.args || {}));
1156
+ }
1157
+ throw new Error(`Unknown action: ${action}. Use 'list', 'schema', or 'call'.`);
1158
+ }
1078
1159
  export function setupToolsHandlers(server, onToolCall) {
1079
1160
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
1080
1161
  tools: toolDefinitionsArray,
@@ -1151,7 +1232,7 @@ async function handleSearchMemory(args) {
1151
1232
  throw new Error("Query is required");
1152
1233
  try {
1153
1234
  const brain = getBrain();
1154
- const results = brain.search(query, limit);
1235
+ const results = brain.search(query, limit, 0, { projectId: deriveProjectId() });
1155
1236
  if (results.length === 0) {
1156
1237
  return { content: [{ type: "text", text: "No relevant memories found in Lemma's Brain." }] };
1157
1238
  }
@@ -1233,7 +1314,7 @@ async function handleGetRoutingAdvice(args) {
1233
1314
  async function handleAutoHeal(args) {
1234
1315
  const apply = !!args?.apply;
1235
1316
  try {
1236
- const { performAutoHeal: heal } = await import("../cli/lemma-proxy");
1317
+ const { performAutoHeal: heal } = await import("../cli/lemma-proxy.js");
1237
1318
  const res = await heal(apply);
1238
1319
  return { content: [{ type: "text", text: JSON.stringify(res, null, 2) }] };
1239
1320
  }
@@ -1636,30 +1717,88 @@ async function handleSqueezePrompt(args) {
1636
1717
  return { content: [{ type: "text", text: `Error squeezing prompt: ${err.message}` }] };
1637
1718
  }
1638
1719
  }
1720
+ /**
1721
+ * Onboarding built from the filesystem alone.
1722
+ *
1723
+ * The proxy produces a richer map, but this tool is in the core surface and CLAUDE.md tells
1724
+ * the model to call it first — so when the proxy is down, the first call of every session
1725
+ * used to return an error and the session started with no context at all. A local answer is
1726
+ * strictly better than that, and it never fails.
1727
+ */
1728
+ function buildLocalOnboarding(cwd) {
1729
+ const lines = [];
1730
+ const projectName = path.basename(cwd);
1731
+ lines.push(`# ${projectName}`, "");
1732
+ let pkg = null;
1733
+ try {
1734
+ pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8"));
1735
+ }
1736
+ catch { /* not a node project */ }
1737
+ if (pkg) {
1738
+ if (pkg.description)
1739
+ lines.push(pkg.description, "");
1740
+ lines.push(`**Version:** ${pkg.version || "unknown"}`);
1741
+ const deps = Object.keys(pkg.dependencies || {});
1742
+ if (deps.length)
1743
+ lines.push(`**Dependencies:** ${deps.slice(0, 12).join(", ")}${deps.length > 12 ? ` (+${deps.length - 12})` : ""}`);
1744
+ const scripts = Object.keys(pkg.scripts || {});
1745
+ if (scripts.length)
1746
+ lines.push(`**Scripts:** ${scripts.slice(0, 12).join(", ")}`);
1747
+ }
1748
+ // Current branch straight from .git/HEAD — no subprocess, works on a detached HEAD too.
1749
+ try {
1750
+ const head = fs.readFileSync(path.join(cwd, ".git", "HEAD"), "utf8").trim();
1751
+ const branch = head.startsWith("ref: ") ? head.replace("ref: refs/heads/", "") : `detached @ ${head.slice(0, 8)}`;
1752
+ lines.push(`**Branch:** ${branch}`);
1753
+ }
1754
+ catch { /* not a git repo */ }
1755
+ try {
1756
+ const entries = fs
1757
+ .readdirSync(cwd, { withFileTypes: true })
1758
+ .filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules")
1759
+ .map((e) => e.name);
1760
+ if (entries.length)
1761
+ lines.push(`**Top-level directories:** ${entries.slice(0, 15).join(", ")}`);
1762
+ }
1763
+ catch { /* unreadable cwd */ }
1764
+ try {
1765
+ const memories = getBrain().getEntriesForProject(deriveProjectId(cwd));
1766
+ lines.push("", `**Brain memories for this project:** ${memories.length}`);
1767
+ if (memories.length) {
1768
+ const recent = memories
1769
+ .slice()
1770
+ .sort((a, b) => (a.timestamp < b.timestamp ? 1 : -1))
1771
+ .slice(0, 5);
1772
+ lines.push("", "Most recent:");
1773
+ for (const m of recent)
1774
+ lines.push(`- ${m.query.slice(0, 100)}`);
1775
+ lines.push("", "Use `search_memory` to retrieve any of these in full.");
1776
+ }
1777
+ }
1778
+ catch { /* brain unavailable */ }
1779
+ return lines.join("\n");
1780
+ }
1639
1781
  async function handleGetProjectOnboarding(_args) {
1782
+ const cwd = process.cwd();
1640
1783
  try {
1641
1784
  const port = getProxyPort();
1642
- const response = await axios.get(`http://localhost:${port}/api/project/onboarding`);
1643
- return {
1644
- content: [
1645
- {
1646
- type: "text",
1647
- text: response.data.markdown || "# Onboarding mental model could not be generated.",
1648
- },
1649
- ],
1650
- };
1785
+ const response = await axios.get(`http://localhost:${port}/api/project/onboarding`, { timeout: 2000 });
1786
+ if (response.data?.markdown) {
1787
+ return { content: [{ type: "text", text: response.data.markdown }] };
1788
+ }
1651
1789
  }
1652
1790
  catch (error) {
1653
- logError("get_project_onboarding", error);
1654
- return {
1655
- content: [
1656
- {
1657
- type: "text",
1658
- text: `Failed to fetch codebase onboarding mental model: ${error.message}. Is Lemma proxy running?`,
1659
- },
1660
- ],
1661
- };
1791
+ // Proxy down or slow — fall through. Not worth surfacing as an error to the model.
1792
+ logError("get_project_onboarding (falling back to local)", error);
1662
1793
  }
1794
+ return {
1795
+ content: [
1796
+ {
1797
+ type: "text",
1798
+ text: `${buildLocalOnboarding(cwd)}\n\n---\n*Generated locally. Start the Lemma proxy (\`lemma start\`) for the full indexed architecture map.*`,
1799
+ },
1800
+ ],
1801
+ };
1663
1802
  }
1664
1803
  function extractSymbolsWithTsCompiler(filePath, relPath) {
1665
1804
  try {
@@ -1887,7 +2026,7 @@ async function handleQueryHybridConsensus(args) {
1887
2026
  const fullQuery = context ? `${query}\n\nContext: ${context}` : query;
1888
2027
  try {
1889
2028
  const brain = getBrain();
1890
- const results = brain.search(fullQuery, 3);
2029
+ const results = brain.search(fullQuery, 3, 0, { projectId: deriveProjectId() });
1891
2030
  const topHit = results[0];
1892
2031
  if (topHit && topHit.similarity >= threshold) {
1893
2032
  const savedTokens = Math.floor((topHit.response?.length || 500) / 4);
@@ -1945,7 +2084,7 @@ async function handleGetTelepathicHints(args) {
1945
2084
  const telepathicQuery = `${basename} ${dirContext} ${ext} patterns solutions architecture`;
1946
2085
  try {
1947
2086
  const brain = getBrain();
1948
- const results = brain.search(telepathicQuery, limit);
2087
+ const results = brain.search(telepathicQuery, limit, 0, { projectId: deriveProjectId() });
1949
2088
  if (results.length === 0) {
1950
2089
  return {
1951
2090
  content: [
@@ -2125,7 +2264,7 @@ async function handleTurboSqueeze(args) {
2125
2264
  const ratio = text.length > 0 ? ((1 - squeezed.length / text.length) * 100).toFixed(1) : "0";
2126
2265
  const saved = Math.max(0, text.length - squeezed.length);
2127
2266
  try {
2128
- const { reportSavings } = await import("../utils/reportSavings");
2267
+ const { reportSavings } = await import("../utils/reportSavings.js");
2129
2268
  reportSavings({ source: "contextSqueeze", charsBefore: text.length, charsAfter: squeezed.length, toolName: "turbosqueeze" });
2130
2269
  }
2131
2270
  catch { }
@@ -2170,7 +2309,7 @@ async function handleCompressContext(args) {
2170
2309
  ...recent,
2171
2310
  ];
2172
2311
  try {
2173
- const { reportSavings } = await import("../utils/reportSavings");
2312
+ const { reportSavings } = await import("../utils/reportSavings.js");
2174
2313
  reportSavings({ source: "historyPrune", tokens: tokensSaved, toolName: "compress_context" });
2175
2314
  }
2176
2315
  catch { }
@@ -2191,13 +2330,13 @@ async function handleSmarterCache(args) {
2191
2330
  const fullQuery = context ? `${query}\n\nContext: ${context}` : query;
2192
2331
  try {
2193
2332
  const brain = getBrain();
2194
- const results = brain.search(fullQuery, 5);
2333
+ const results = brain.search(fullQuery, 5, 0, { projectId: deriveProjectId() });
2195
2334
  const topHit = results[0];
2196
2335
  if (topHit && topHit.similarity >= threshold) {
2197
2336
  const responseText = topHit.response;
2198
2337
  const tokensSaved = Math.floor(responseText.length / 4);
2199
2338
  try {
2200
- const { reportSavings } = await import("../utils/reportSavings");
2339
+ const { reportSavings } = await import("../utils/reportSavings.js");
2201
2340
  reportSavings({ source: "cache", tokens: tokensSaved, toolName: "smarter_cache", query: query.substring(0, 100) });
2202
2341
  }
2203
2342
  catch { }
@@ -2344,6 +2483,12 @@ async function handleTokenBudget(_args) {
2344
2483
  astNavigation: true,
2345
2484
  privacyScrubbing: true,
2346
2485
  },
2486
+ // The fixed cost of being connected, re-sent on every request of the session.
2487
+ perTurnOverhead: {
2488
+ ...getToolSurfaceSummary(),
2489
+ schemaTokens: Math.round(getToolSchemaChars() / 4),
2490
+ note: "Hidden tools stay callable via lemma_toolbox. Set mcp.toolProfile='full' in lemma.config.json to advertise all of them.",
2491
+ },
2347
2492
  recommendations: toolCallCount === 0
2348
2493
  ? ["No se han usado tools de Lemma todavía. Asegúrate de que el agente esté usando el prompt lemma-turbomode."]
2349
2494
  : ["Sigue usando turbosqueeze antes de cada code block grande.", "Usa smarter_cache antes de razonar."],
@@ -2957,7 +3102,8 @@ async function handleSemanticDedupGuard(args) {
2957
3102
  throw new Error("query and response are required");
2958
3103
  try {
2959
3104
  const brain = getBrain();
2960
- const results = brain.search(query, 3);
3105
+ // A dedup check is not a question the user asked — it must not register as a cache hit.
3106
+ const results = brain.search(query, 3, 0, { countStats: false, projectId: deriveProjectId() });
2961
3107
  const topHit = results[0];
2962
3108
  if (!topHit) {
2963
3109
  return {
@@ -4063,7 +4209,7 @@ async function handleLocalSemanticAutofix(args) {
4063
4209
  throw new Error("errorMessage is required");
4064
4210
  try {
4065
4211
  const brain = getBrain();
4066
- const results = brain.search(errorMessage, 3, 0.70);
4212
+ const results = brain.search(errorMessage, 3, 0.70, { projectId: deriveProjectId() });
4067
4213
  const topHit = results[0];
4068
4214
  if (topHit) {
4069
4215
  const responseText = topHit.response;