@nxuss/lemma 1.2.0 → 1.2.2

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 (2) hide show
  1. package/bin/init.js +135 -84
  2. package/package.json +1 -1
package/bin/init.js CHANGED
@@ -71,6 +71,65 @@ function configureCursor() {
71
71
  }
72
72
  }
73
73
 
74
+ function configureCursorMcp() {
75
+ const globalCursorDir = path.join(os.homedir(), '.cursor');
76
+ const globalMcpFile = path.join(globalCursorDir, 'mcp.json');
77
+ const projectCursorDir = path.join(process.cwd(), '.cursor');
78
+ const projectMcpFile = path.join(projectCursorDir, 'mcp.json');
79
+ const lemmaServerConfig = { command: 'npx', args: ['-y', '@nxuss/lemma', 'mcp'] };
80
+
81
+ try {
82
+ ensureDir(globalCursorDir);
83
+ let globalConfig = {};
84
+ if (fs.existsSync(globalMcpFile)) {
85
+ globalConfig = JSON.parse(fs.readFileSync(globalMcpFile, 'utf8'));
86
+ }
87
+ if (!globalConfig.mcpServers) globalConfig.mcpServers = {};
88
+ globalConfig.mcpServers.lemma = lemmaServerConfig;
89
+ fs.writeFileSync(globalMcpFile, JSON.stringify(globalConfig, null, 2), 'utf8');
90
+
91
+ ensureDir(projectCursorDir);
92
+ let projectConfig = {};
93
+ if (fs.existsSync(projectMcpFile)) {
94
+ projectConfig = JSON.parse(fs.readFileSync(projectMcpFile, 'utf8'));
95
+ }
96
+ if (!projectConfig.mcpServers) projectConfig.mcpServers = {};
97
+ projectConfig.mcpServers.lemma = lemmaServerConfig;
98
+ fs.writeFileSync(projectMcpFile, JSON.stringify(projectConfig, null, 2), 'utf8');
99
+ return true;
100
+ } catch {
101
+ return false;
102
+ }
103
+ }
104
+
105
+ function configureCodexCliMcp() {
106
+ const codexDir = path.join(os.homedir(), '.codex');
107
+ const codexConfigFile = path.join(codexDir, 'config.toml');
108
+ const lemmaEntry = [
109
+ '',
110
+ '[mcp_servers.lemma]',
111
+ 'command = "npx"',
112
+ 'args = ["-y", "@nxuss/lemma", "mcp"]',
113
+ 'enabled = true',
114
+ ''
115
+ ].join('\n');
116
+
117
+ try {
118
+ ensureDir(codexDir);
119
+ let config = '';
120
+ if (fs.existsSync(codexConfigFile)) {
121
+ config = fs.readFileSync(codexConfigFile, 'utf8');
122
+ }
123
+ if (config.includes('[mcp_servers.lemma]')) {
124
+ return true;
125
+ }
126
+ fs.writeFileSync(codexConfigFile, `${config.replace(/\s*$/, '')}${lemmaEntry}`, 'utf8');
127
+ return true;
128
+ } catch {
129
+ return false;
130
+ }
131
+ }
132
+
74
133
  function configureOpenCodeMcp() {
75
134
  const configDir = path.join(os.homedir(), '.opencode');
76
135
  const configFile = path.join(configDir, 'mcp.json');
@@ -117,64 +176,66 @@ function configureClaudeDesktop() {
117
176
  }
118
177
 
119
178
  // ─── Lemma MCP tool names (server prefix: "lemma") ────────────────────────────
120
- const LEMMA_MCP_TOOLS = [
121
- // Workspace I/O — replaces native Read/Write/LS/Grep
122
- 'mcp__lemma__read_workspace_file',
123
- 'mcp__lemma__write_workspace_file',
124
- 'mcp__lemma__create_workspace_file',
125
- 'mcp__lemma__list_workspace_dir',
126
- 'mcp__lemma__search_workspace',
127
- 'mcp__lemma__apply_workspace_patch',
128
- 'mcp__lemma__run_workspace_command',
129
- // Memory & cache
130
- 'mcp__lemma__search_memory',
131
- 'mcp__lemma__store_memory',
132
- 'mcp__lemma__smarter_cache',
133
- 'mcp__lemma__get_cached_solution',
134
- 'mcp__lemma__store_cached_solution',
135
- // Context & onboarding
136
- 'mcp__lemma__get_project_onboarding',
137
- 'mcp__lemma__get_telepathic_hints',
138
- 'mcp__lemma__get_ast_hologram',
139
- 'mcp__lemma__get_symbol_surgical_context',
140
- 'mcp__lemma__compress_context',
141
- 'mcp__lemma__collapse_context',
142
- // Token optimization
143
- 'mcp__lemma__squeeze_prompt',
144
- 'mcp__lemma__turbosqueeze',
145
- 'mcp__lemma__wormhole_squeeze',
146
- 'mcp__lemma__token_budget',
147
- 'mcp__lemma__prune_conversation_history',
148
- 'mcp__lemma__summarize_long_text',
149
- // Code quality & patching
150
- 'mcp__lemma__validate_patch_sandbox',
151
- 'mcp__lemma__surgical_ast_insert',
152
- 'mcp__lemma__diff_only',
153
- 'mcp__lemma__local_semantic_autofix',
154
- 'mcp__lemma__auto_heal',
155
- // Analysis
156
- 'mcp__lemma__get_routing_advice',
157
- 'mcp__lemma__query_hybrid_consensus',
158
- 'mcp__lemma__batch_tool_calls',
159
- 'mcp__lemma__scrub_privacy',
160
- 'mcp__lemma__entropy_score',
161
- 'mcp__lemma__coupling_radar',
162
- 'mcp__lemma__pattern_fossil',
163
- 'mcp__lemma__git_heatmap_risk',
164
- 'mcp__lemma__precrime_static',
165
- 'mcp__lemma__semantic_dedup_guard',
166
- 'mcp__lemma__dead_export_necromancer',
167
- 'mcp__lemma__depgraph',
168
- 'mcp__lemma__refactor',
169
- // PR Review
170
- 'mcp__lemma__review_diff',
171
- 'mcp__lemma__review_pr',
172
- 'mcp__lemma__pr_status',
173
- 'mcp__lemma__generate_pr_workflow',
174
- // Reporting
175
- 'mcp__lemma__generate_executive_roi_report',
179
+ const FALLBACK_LEMMA_TOOL_NAMES = [
180
+ 'read_workspace_file', 'write_workspace_file', 'create_workspace_file', 'list_workspace_dir',
181
+ 'search_workspace', 'apply_workspace_patch', 'run_workspace_command',
182
+ 'search_memory', 'store_memory', 'smarter_cache',
183
+ 'get_project_onboarding', 'get_telepathic_hints', 'get_ast_hologram',
184
+ 'get_symbol_surgical_context', 'compress_context',
185
+ 'squeeze_prompt', 'turbosqueeze', 'wormhole_squeeze',
186
+ 'token_budget', 'prune_conversation_history', 'summarize_long_text',
187
+ 'validate_patch_sandbox', 'surgical_ast_insert', 'diff_only',
188
+ 'local_semantic_autofix', 'auto_heal', 'get_routing_advice',
189
+ 'query_hybrid_consensus', 'batch_tool_calls', 'scrub_privacy',
190
+ 'entropy_score', 'coupling_radar', 'pattern_fossil',
191
+ 'git_heatmap_risk', 'precrime_static', 'semantic_dedup_guard',
192
+ 'dead_export_necromancer', 'depgraph', 'refactor',
193
+ 'review_diff', 'review_pr', 'pr_status',
194
+ 'generate_pr_workflow', 'generate_executive_roi_report',
176
195
  ];
177
196
 
197
+ function extractToolNamesFromSource(source, containerName) {
198
+ const blockRegex = new RegExp(`const ${containerName}(?:\\s*:\\s*[^=]+)?\\s*=\\s*\\{([\\s\\S]*?)\\n\\};`);
199
+ const match = source.match(blockRegex);
200
+ if (!match) return [];
201
+ const names = [];
202
+ const seen = new Set();
203
+ const keyRegex = /\n\s*([a-zA-Z0-9_]+):\s*(?:handle[A-Za-z0-9_]+|async\s*\()/g;
204
+ for (const hit of match[1].matchAll(keyRegex)) {
205
+ const name = hit[1];
206
+ if (seen.has(name)) continue;
207
+ seen.add(name);
208
+ names.push(name);
209
+ }
210
+ return names;
211
+ }
212
+
213
+ function loadLemmaToolNames() {
214
+ const seen = new Set();
215
+ const collected = [];
216
+ const candidates = [
217
+ ['toolHandlers', path.join(__dirname, '..', 'src', 'mcp', 'tools.ts')],
218
+ ['toolHandlers', path.join(__dirname, '..', 'dist', 'cjs', 'mcp', 'tools.js')],
219
+ ['handlers', path.join(__dirname, '..', 'src', 'infra', 'mcp-tools.ts')],
220
+ ['handlers', path.join(__dirname, '..', 'dist', 'cjs', 'infra', 'mcp-tools.js')],
221
+ ];
222
+ for (const [containerName, filePath] of candidates) {
223
+ if (!fs.existsSync(filePath)) continue;
224
+ try {
225
+ const source = fs.readFileSync(filePath, 'utf8');
226
+ for (const name of extractToolNamesFromSource(source, containerName)) {
227
+ if (seen.has(name)) continue;
228
+ seen.add(name);
229
+ collected.push(name);
230
+ }
231
+ } catch {}
232
+ }
233
+ return collected.length > 0 ? collected : FALLBACK_LEMMA_TOOL_NAMES;
234
+ }
235
+
236
+ const LEMMA_TOOL_NAMES = loadLemmaToolNames();
237
+ const LEMMA_MCP_TOOLS = LEMMA_TOOL_NAMES.map((name) => `mcp__lemma__${name}`);
238
+
178
239
  const CLAUDE_MD_CONTENT = `# Lemma MCP — Reglas de Comportamiento Obligatorio
179
240
 
180
241
  > ⚠️ ESTAS REGLAS SON ABSOLUTAS. Síguilas sin excepción.
@@ -255,10 +316,11 @@ function configureClaudeCode() {
255
316
  );
256
317
  settings.enableAllProjectMcpServers = true;
257
318
 
258
- // Merge allowedTools without duplicates
259
- const existing = new Set(settings.allowedTools || []);
260
- for (const t of LEMMA_MCP_TOOLS) existing.add(t);
261
- settings.allowedTools = Array.from(existing);
319
+ // Rebuild Lemma tool permissions from the current catalog while preserving non-Lemma tools.
320
+ const preservedTools = (settings.allowedTools || []).filter(
321
+ (tool) => !tool.startsWith('mcp__lemma__')
322
+ );
323
+ settings.allowedTools = [...preservedTools, ...LEMMA_MCP_TOOLS];
262
324
 
263
325
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
264
326
 
@@ -276,25 +338,6 @@ function configureClaudeCode() {
276
338
  return true;
277
339
  }
278
340
 
279
- // ─── Short tool names (for non-Claude tools that don't use mcp__ prefix) ───────
280
- const LEMMA_TOOL_NAMES = [
281
- 'read_workspace_file', 'write_workspace_file', 'create_workspace_file', 'list_workspace_dir',
282
- 'search_workspace', 'apply_workspace_patch', 'run_workspace_command',
283
- 'search_memory', 'store_memory', 'smarter_cache',
284
- 'get_project_onboarding', 'get_telepathic_hints', 'get_ast_hologram',
285
- 'get_symbol_surgical_context', 'compress_context',
286
- 'squeeze_prompt', 'turbosqueeze', 'wormhole_squeeze',
287
- 'token_budget', 'prune_conversation_history', 'summarize_long_text',
288
- 'validate_patch_sandbox', 'surgical_ast_insert', 'diff_only',
289
- 'local_semantic_autofix', 'auto_heal', 'get_routing_advice',
290
- 'query_hybrid_consensus', 'batch_tool_calls', 'scrub_privacy',
291
- 'entropy_score', 'coupling_radar', 'pattern_fossil',
292
- 'git_heatmap_risk', 'precrime_static', 'semantic_dedup_guard',
293
- 'dead_export_necromancer', 'depgraph', 'refactor',
294
- 'review_diff', 'review_pr', 'pr_status',
295
- 'generate_pr_workflow', 'generate_executive_roi_report',
296
- ];
297
-
298
341
  const LEMMA_RULES_CONTENT = `# Lemma MCP — Autonomous Behaviors
299
342
 
300
343
  > ⚠️ MANDATORY RULES — Follow without exception.
@@ -612,15 +655,23 @@ async function main() {
612
655
  const hasCursor = detectTool('Cursor', () => fs.existsSync(path.join(os.homedir(), '.cursor')));
613
656
  if (hasCursor) {
614
657
  const configured = configureCursor();
615
- if (configured) ok('Cursor configured');
616
- else warn('Cursor detected but config could not be auto-updated');
617
- } else skip('Cursor not detected');
658
+ const mcpConfigured = configureCursorMcp();
659
+ if (configured && mcpConfigured) ok('Cursor configured (API base + MCP registration)');
660
+ else warn('Cursor detected but config could not be fully auto-updated');
661
+ } else {
662
+ const projectCursorConfigured = configureCursorMcp();
663
+ if (projectCursorConfigured) ok('Project Cursor MCP configured');
664
+ else skip('Cursor not detected');
665
+ }
618
666
 
619
667
  const hasCodex = detectTool('Codex CLI', () => {
620
668
  try { execSync('which codex', { stdio: 'ignore' }); return true; } catch { return false; }
621
669
  });
622
- if (hasCodex) ok('Codex CLI detected — set OPENAI_BASE_URL env (auto-configured below)');
623
- else skip('Codex CLI not detected');
670
+ if (hasCodex) {
671
+ const codexConfigured = configureCodexCliMcp();
672
+ if (codexConfigured) ok('Codex CLI configured (MCP + OPENAI_BASE_URL env)');
673
+ else warn('Codex CLI detected but MCP config could not be auto-updated');
674
+ } else skip('Codex CLI not detected');
624
675
 
625
676
  const hasWindsurf = detectTool('Windsurf', () => fs.existsSync(path.join(os.homedir(), '.windsurfrules')));
626
677
  if (hasWindsurf) ok('Windsurf detected — env auto-configured');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxuss/lemma",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Intelligent AI Gateway for IDEs & Agents — Semantic cache, Privacy Firewall, Infrastructure Command Center, and Autonomous Cost-Optimization.",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",