@nxuss/lemma 0.7.1 โ†’ 0.7.3

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 (80) hide show
  1. package/README.md +6 -5
  2. package/dashboard/dist/assets/{index-BJzMIGrT.js โ†’ index-BNYoIN8-.js} +114 -114
  3. package/dashboard/dist/assets/index-BNYoIN8-.js.map +1 -0
  4. package/dashboard/dist/assets/index-DtEKr0hI.css +1 -0
  5. package/dashboard/dist/index.html +2 -2
  6. package/dist/cjs/api/dashboardRoutes.d.ts.map +1 -1
  7. package/dist/cjs/api/dashboardRoutes.js +41 -1
  8. package/dist/cjs/api/dashboardRoutes.js.map +1 -1
  9. package/dist/cjs/cli/lemma-proxy.d.ts +0 -6
  10. package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
  11. package/dist/cjs/cli/lemma-proxy.js +233 -359
  12. package/dist/cjs/cli/lemma-proxy.js.map +1 -1
  13. package/dist/cjs/mcp/index.js +165 -1192
  14. package/dist/cjs/mcp/index.js.map +1 -1
  15. package/dist/cjs/mcp/prompts.d.ts +4 -0
  16. package/dist/cjs/mcp/prompts.d.ts.map +1 -0
  17. package/dist/cjs/mcp/prompts.js +84 -0
  18. package/dist/cjs/mcp/prompts.js.map +1 -0
  19. package/dist/cjs/mcp/resources.d.ts +3 -0
  20. package/dist/cjs/mcp/resources.d.ts.map +1 -0
  21. package/dist/cjs/mcp/resources.js +254 -0
  22. package/dist/cjs/mcp/resources.js.map +1 -0
  23. package/dist/cjs/mcp/tools.d.ts +11 -0
  24. package/dist/cjs/mcp/tools.d.ts.map +1 -0
  25. package/dist/cjs/mcp/tools.js +1192 -0
  26. package/dist/cjs/mcp/tools.js.map +1 -0
  27. package/dist/cjs/mcp/utils.d.ts +11 -0
  28. package/dist/cjs/mcp/utils.d.ts.map +1 -0
  29. package/dist/cjs/mcp/utils.js +85 -0
  30. package/dist/cjs/mcp/utils.js.map +1 -0
  31. package/dist/cjs/proxy/ContextInjector.d.ts +14 -0
  32. package/dist/cjs/proxy/ContextInjector.d.ts.map +1 -0
  33. package/dist/cjs/proxy/ContextInjector.js +148 -0
  34. package/dist/cjs/proxy/ContextInjector.js.map +1 -0
  35. package/dist/cjs/proxy/ProjectRegistry.d.ts +17 -0
  36. package/dist/cjs/proxy/ProjectRegistry.d.ts.map +1 -0
  37. package/dist/cjs/proxy/ProjectRegistry.js +60 -0
  38. package/dist/cjs/proxy/ProjectRegistry.js.map +1 -0
  39. package/dist/cjs/utils/reportSavings.d.ts +6 -0
  40. package/dist/cjs/utils/reportSavings.d.ts.map +1 -1
  41. package/dist/cjs/utils/reportSavings.js.map +1 -1
  42. package/dist/esm/api/dashboardRoutes.d.ts.map +1 -1
  43. package/dist/esm/api/dashboardRoutes.js +41 -1
  44. package/dist/esm/api/dashboardRoutes.js.map +1 -1
  45. package/dist/esm/cli/lemma-proxy.d.ts +0 -6
  46. package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
  47. package/dist/esm/cli/lemma-proxy.js +233 -358
  48. package/dist/esm/cli/lemma-proxy.js.map +1 -1
  49. package/dist/esm/mcp/index.js +164 -1191
  50. package/dist/esm/mcp/index.js.map +1 -1
  51. package/dist/esm/mcp/prompts.d.ts +4 -0
  52. package/dist/esm/mcp/prompts.d.ts.map +1 -0
  53. package/dist/esm/mcp/prompts.js +80 -0
  54. package/dist/esm/mcp/prompts.js.map +1 -0
  55. package/dist/esm/mcp/resources.d.ts +3 -0
  56. package/dist/esm/mcp/resources.d.ts.map +1 -0
  57. package/dist/esm/mcp/resources.js +248 -0
  58. package/dist/esm/mcp/resources.js.map +1 -0
  59. package/dist/esm/mcp/tools.d.ts +11 -0
  60. package/dist/esm/mcp/tools.d.ts.map +1 -0
  61. package/dist/esm/mcp/tools.js +1153 -0
  62. package/dist/esm/mcp/tools.js.map +1 -0
  63. package/dist/esm/mcp/utils.d.ts +11 -0
  64. package/dist/esm/mcp/utils.d.ts.map +1 -0
  65. package/dist/esm/mcp/utils.js +73 -0
  66. package/dist/esm/mcp/utils.js.map +1 -0
  67. package/dist/esm/proxy/ContextInjector.d.ts +14 -0
  68. package/dist/esm/proxy/ContextInjector.d.ts.map +1 -0
  69. package/dist/esm/proxy/ContextInjector.js +108 -0
  70. package/dist/esm/proxy/ContextInjector.js.map +1 -0
  71. package/dist/esm/proxy/ProjectRegistry.d.ts +17 -0
  72. package/dist/esm/proxy/ProjectRegistry.d.ts.map +1 -0
  73. package/dist/esm/proxy/ProjectRegistry.js +56 -0
  74. package/dist/esm/proxy/ProjectRegistry.js.map +1 -0
  75. package/dist/esm/utils/reportSavings.d.ts +6 -0
  76. package/dist/esm/utils/reportSavings.d.ts.map +1 -1
  77. package/dist/esm/utils/reportSavings.js.map +1 -1
  78. package/package.json +1 -1
  79. package/dashboard/dist/assets/index-BJzMIGrT.js.map +0 -1
  80. package/dashboard/dist/assets/index-DUOrThix.css +0 -1
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.autoConfigureAll = autoConfigureAll;
8
7
  exports.startBackgroundClipboardWatcher = startBackgroundClipboardWatcher;
9
8
  exports.performAutoHeal = performAutoHeal;
10
9
  const commander_1 = require("commander");
@@ -40,6 +39,8 @@ const SemanticScrubber_1 = __importDefault(require("../security/SemanticScrubber
40
39
  const CloudSyncClient_1 = __importDefault(require("../cloud/CloudSyncClient"));
41
40
  const ContextSqueezer_1 = require("../utils/ContextSqueezer");
42
41
  const AgentMultiplexer_1 = require("../proxy/AgentMultiplexer");
42
+ const ProjectRegistry_1 = require("../proxy/ProjectRegistry");
43
+ const ContextInjector_1 = require("../proxy/ContextInjector");
43
44
  const SavingsLedger_1 = require("../utils/SavingsLedger");
44
45
  const chroma = new chromadb_1.ChromaClient({ host: 'localhost', port: 8000 });
45
46
  const dummyEmbeddingFunction = { generate: (texts) => Promise.resolve([]) };
@@ -57,11 +58,23 @@ const USAGE_FILE = path_1.default.join(CACHE_DIR, 'usage.json');
57
58
  const LICENSE_FILE = path_1.default.join(CACHE_DIR, 'license.json');
58
59
  const EVENT_LOG = [];
59
60
  const MAX_EVENTS = 100;
61
+ // MCP Status tracking
62
+ const MCP_STATUS = {
63
+ lastActivity: Date.now(),
64
+ toolCallCount: 0,
65
+ status: 'idle'
66
+ };
60
67
  function logEvent(event) {
61
68
  const ev = { id: Math.random().toString(36).slice(2, 11), timestamp: Date.now(), ...event };
62
69
  EVENT_LOG.push(ev);
63
70
  if (EVENT_LOG.length > MAX_EVENTS)
64
71
  EVENT_LOG.shift();
72
+ // Track MCP activity
73
+ if (event.type === 'mcp_tool_call') {
74
+ MCP_STATUS.lastActivity = Date.now();
75
+ MCP_STATUS.toolCallCount++;
76
+ MCP_STATUS.status = 'active';
77
+ }
65
78
  // Print a terminal savings summary every 10 events
66
79
  if (EVENT_LOG.length > 0 && EVENT_LOG.length % 10 === 0) {
67
80
  const snap = SavingsLedger_1.savingsLedger.getSnapshot();
@@ -70,354 +83,49 @@ function logEvent(event) {
70
83
  }
71
84
  }
72
85
  }
73
- const FREE_LIMIT = 500;
74
- const WARN_PCT = 0.8;
75
- const VALIDATE_URL = 'https://lemma.nxus.studio/api/v1/validate';
76
- if (!fs_1.default.existsSync(CACHE_DIR))
77
- fs_1.default.mkdirSync(CACHE_DIR, { recursive: true });
78
- // โ”€โ”€ Helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
79
- async function readJson(file, fallback) {
80
- try {
81
- if (await fs_1.default.promises.access(file).then(() => true).catch(() => false)) {
82
- const content = await fs_1.default.promises.readFile(file, 'utf8');
83
- return JSON.parse(content);
84
- }
85
- }
86
- catch { }
87
- return fallback;
88
- }
89
- const writeQueues = new Map();
90
- async function writeJson(file, data) {
91
- const chain = writeQueues.get(file) || Promise.resolve();
92
- const writeOperation = async () => {
93
- try {
94
- const tempFile = `${file}.${crypto_1.default.randomUUID ? crypto_1.default.randomUUID() : Math.random().toString(36).substring(2, 15)}.tmp`;
95
- await fs_1.default.promises.writeFile(tempFile, JSON.stringify(data, null, 2));
96
- await fs_1.default.promises.rename(tempFile, file);
97
- }
98
- catch { }
99
- };
100
- const nextChain = chain.then(writeOperation).catch(() => { });
101
- writeQueues.set(file, nextChain);
102
- return nextChain;
103
- }
104
- function projectHash(name) {
105
- return crypto_1.default.createHash('sha1').update(name).digest('hex').slice(0, 12);
106
- }
107
- function getCollectionName(projectName) {
108
- let cleanName = projectName.replace(/[^a-zA-Z0-9._-]/g, '_');
109
- cleanName = cleanName.replace(/^[^a-zA-Z0-9]+/, '');
110
- cleanName = cleanName.replace(/[^a-zA-Z0-9]+$/, '');
111
- if (cleanName.length < 3) {
112
- cleanName = 'lemma_proj_' + cleanName;
113
- }
114
- return `lemma-cache-${cleanName}`;
115
- }
116
- function detectProject() {
117
- const pkgPath = path_1.default.join(process.cwd(), 'package.json');
118
- if (fs_1.default.existsSync(pkgPath)) {
119
- try {
120
- const p = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
121
- if (p.name)
122
- return p.name;
123
- }
124
- catch { }
125
- }
126
- return path_1.default.basename(process.cwd());
127
- }
128
- function ensureProjectDir(name) {
129
- const dir = path_1.default.join(CACHE_DIR, projectHash(name));
130
- if (!fs_1.default.existsSync(dir))
131
- fs_1.default.mkdirSync(dir, { recursive: true });
132
- const marker = path_1.default.join(dir, 'project-name.txt');
133
- if (!fs_1.default.existsSync(marker))
134
- fs_1.default.writeFileSync(marker, name);
135
- return dir;
136
- }
137
- function getPidByPort(port) {
138
- const { execSync } = require('child_process');
139
- try {
140
- if (process.platform === 'win32') {
141
- const out = execSync(`netstat -ano | findstr :${port}`).toString();
142
- const match = out.match(/LISTENING\s+(\d+)/);
143
- return match ? match[1] : null;
144
- }
145
- else {
146
- const out = execSync(`lsof -t -i :${port}`).toString().trim();
147
- return out.split('\n')[0]; // Take first if multiple
148
- }
149
- }
150
- catch {
151
- return null;
152
- }
153
- }
154
- async function checkDependency(url) {
155
- try {
156
- const res = await axios_1.default.get(url, { timeout: 1000, validateStatus: () => true });
157
- return res.status >= 200 && res.status < 500;
158
- }
159
- catch {
160
- return false;
161
- }
162
- }
163
- async function ensureChromaRunning() {
164
- const chromaPort = process.env.CHROMA_PORT || '8000';
165
- const chromaHost = process.env.CHROMA_HOST || 'http://localhost';
166
- const chromaUrl = `${chromaHost}:${chromaPort}`;
167
- const isChromaUp = await checkDependency(`${chromaUrl}/api/v1/heartbeat`);
168
- if (isChromaUp) {
169
- console.log(`โœ… \x1b[32m[ChromaDB]\x1b[0m ChromaDB is already running on port ${chromaPort}`);
170
- return true;
171
- }
172
- console.log(`\n๐Ÿ“ฆ \x1b[35m[ChromaDB]\x1b[0m ChromaDB is not running on port ${chromaPort}.`);
173
- console.log(`๐Ÿ”„ Starting ChromaDB in background...`);
174
- const chromaDataPath = path_1.default.join(CACHE_DIR, 'chroma_data');
175
- try {
176
- const { spawn } = require('child_process');
177
- if (!fs_1.default.existsSync(chromaDataPath)) {
178
- fs_1.default.mkdirSync(chromaDataPath, { recursive: true });
179
- }
180
- const chromaProcess = spawn('chroma', ['run', '--path', chromaDataPath, '--port', chromaPort], {
181
- stdio: 'ignore',
182
- detached: true
183
- });
184
- chromaProcess.unref();
185
- // Wait up to 5 seconds for ChromaDB to start
186
- for (let i = 0; i < 5; i++) {
187
- await new Promise(r => setTimeout(r, 1000));
188
- const up = await checkDependency(`${chromaUrl}/api/v1/heartbeat`);
189
- if (up) {
190
- console.log(`โœ… \x1b[32m[ChromaDB]\x1b[0m ChromaDB has been started and is ready!`);
191
- return true;
192
- }
193
- }
194
- console.log(`โš ๏ธ \x1b[33m[ChromaDB]\x1b[0m ChromaDB was started but is taking longer to respond. It will continue starting in the background.`);
195
- return true;
196
- }
197
- catch (err) {
198
- console.error(`โŒ Failed to automatically start ChromaDB: ${err.message}`);
199
- console.error(`๐Ÿ‘‰ Please start it manually: chroma run --path ${chromaDataPath} --port ${chromaPort}`);
200
- return false;
201
- }
202
- }
203
- function ensureGitIgnore() {
204
- const gi = path_1.default.join(process.cwd(), '.gitignore');
205
- let added = false;
206
- try {
207
- let content = '';
208
- if (fs_1.default.existsSync(gi)) {
209
- content = fs_1.default.readFileSync(gi, 'utf8');
210
- }
211
- const appendLines = [];
212
- if (!content.includes('.lemma/')) {
213
- appendLines.push('# Lemma Context Logs', '.lemma/');
214
- added = true;
215
- }
216
- if (!content.includes('chroma_data/')) {
217
- appendLines.push('# Lemma Local Vector Database', 'chroma_data/');
218
- added = true;
219
- }
220
- if (appendLines.length > 0) {
221
- const prefix = content.endsWith('\n') || content === '' ? '' : '\n';
222
- fs_1.default.appendFileSync(gi, prefix + appendLines.join('\n') + '\n');
223
- }
224
- }
225
- catch { }
226
- return added;
227
- }
228
- const CLIPBOARD_PID_FILE = path_1.default.join(CACHE_DIR, 'clipboard.pid');
229
- function autoConfigureAll(projectName, cliOpts) {
230
- const project = projectName || detectProject();
231
- const HOME = process.env.HOME || process.env.USERPROFILE || '~';
232
- // Load configuration if it exists
233
- let configDisabled = false;
234
- let configEditor = true;
235
- let configShell = true;
236
- let configClaude = true;
237
- try {
238
- const configPath = path_1.default.join(process.cwd(), 'lemma.config.json');
239
- if (fs_1.default.existsSync(configPath)) {
240
- const rawConfig = fs_1.default.readFileSync(configPath, 'utf8');
241
- const config = JSON.parse(rawConfig);
242
- const systemConfig = config.system || {};
243
- const autoConfig = systemConfig.autoConfigure || {};
244
- if (autoConfig.disabled === true)
245
- configDisabled = true;
246
- if (autoConfig.editor === false)
247
- configEditor = false;
248
- if (autoConfig.shell === false)
249
- configShell = false;
250
- if (autoConfig.claude === false)
251
- configClaude = false;
252
- }
253
- }
254
- catch { }
255
- // Merge CLI overrides and config
256
- const shouldConfigure = cliOpts?.configure !== false && !configDisabled;
257
- if (!shouldConfigure) {
258
- console.log(`โญ๏ธ [Auto-Configure] Skipped auto-configuration (disabled via options or config).`);
259
- return;
260
- }
261
- const runEditor = cliOpts?.editor !== false && configEditor;
262
- const runShell = cliOpts?.shell !== false && configShell;
263
- const runClaude = cliOpts?.claude !== false && configClaude;
264
- // 1. Claude Desktop Config
265
- if (runClaude) {
266
- try {
267
- const isWin = process.platform === 'win32';
268
- const claudePath = isWin
269
- ? path_1.default.join(process.env.APPDATA || '', 'Claude/claude_desktop_config.json')
270
- : path_1.default.join(HOME, 'Library/Application Support/Claude/claude_desktop_config.json');
271
- if (fs_1.default.existsSync(path_1.default.dirname(claudePath))) {
272
- let config = { mcpServers: {} };
273
- if (fs_1.default.existsSync(claudePath)) {
274
- try {
275
- config = JSON.parse(fs_1.default.readFileSync(claudePath, 'utf8'));
276
- }
277
- catch { }
278
- }
279
- if (!config.mcpServers)
280
- config.mcpServers = {};
281
- config.mcpServers.lemma = {
282
- command: 'npx',
283
- args: ['-y', '@nxuss/lemma', 'mcp'],
284
- env: { LEMMA_PROJECT: project }
285
- };
286
- fs_1.default.writeFileSync(claudePath, JSON.stringify(config, null, 2));
287
- console.log(`โœ… [Claude Desktop] Configured automatically in: ${claudePath} (Restart Claude to activate)`);
288
- }
289
- }
290
- catch (e) {
291
- console.log(`โš ๏ธ Could not auto-configure Claude Desktop: ${e.message}`);
292
- }
293
- }
294
- else {
295
- console.log(`โญ๏ธ [Claude Desktop] Skip auto-configuration.`);
296
- }
297
- // 2. Shell Profiles Environment Override
298
- if (runShell) {
299
- try {
300
- const profiles = [
301
- path_1.default.join(HOME, '.zshrc'),
302
- path_1.default.join(HOME, '.bashrc'),
303
- path_1.default.join(HOME, '.bash_profile'),
304
- path_1.default.join(HOME, '.profile')
305
- ];
306
- const lines = [
307
- '',
308
- '# Lemma AI Gateway Overrides',
309
- 'export OPENAI_BASE_URL="http://localhost:8081/v1"',
310
- 'export ANTHROPIC_BASE_URL="http://localhost:8081"',
311
- 'export LEMMA_PROJECT="' + project + '"',
312
- ''
313
- ].join('\n');
314
- for (const profile of profiles) {
315
- if (fs_1.default.existsSync(profile)) {
316
- try {
317
- const content = fs_1.default.readFileSync(profile, 'utf8');
318
- if (!content.includes('OPENAI_BASE_URL') && !content.includes('LEMMA_PROJECT')) {
319
- fs_1.default.appendFileSync(profile, lines);
320
- console.log(`โœ… [Shell Profile] Configured ${path_1.default.basename(profile)} with local redirect variables!`);
321
- }
322
- }
323
- catch { }
324
- }
325
- }
326
- }
327
- catch (e) {
328
- console.log(`โš ๏ธ Could not auto-configure shell profiles: ${e.message}`);
329
- }
330
- }
331
- else {
332
- console.log(`โญ๏ธ [Shell Profile] Skip auto-configuration.`);
333
- }
334
- // 3. Cursor, Windsurf & VS Code settings.json updates
335
- if (runEditor) {
86
+ const LEMMA_RULES_BLOCK = `
87
+ ๐Ÿง  Lemma MCP โ€” Autonomous Behaviors
88
+
89
+ I have Lemma MCP tools connected. I MUST use them proactively:
90
+
91
+ 1. When starting work on a project, call \`get_project_onboarding\` or read \`lemma://project/context\` to understand the architecture.
92
+ 2. Before answering technical questions, call \`search_memory\` for relevant past solutions.
93
+ 3. When examining a file, call \`get_telepathic_hints\` for relevant context.
94
+ 4. After solving a problem, call \`store_memory\` to persist the solution for future sessions.
95
+ 5. Use \`read_workspace_file\`, \`list_workspace_dir\`, \`search_workspace\` instead of asking the user to read or navigate files.
96
+ 6. Always call \`validate_patch_sandbox\` before writing patches.
97
+ 7. Read \`lemma://runtime/context\` first when the user reports a bug.
98
+ `;
99
+ function injectIdeRules(projectName) {
100
+ const content = `\n# ${projectName} โ€” ${LEMMA_RULES_BLOCK.trim()}\n`;
101
+ const rulesTargets = [
102
+ { name: 'Cursor', path: path_1.default.join(process.cwd(), '.cursorrules') },
103
+ { name: 'Cursor (v2)', path: path_1.default.join(process.cwd(), '.cursor/rules/lemma.mdc') },
104
+ { name: 'Windsurf', path: path_1.default.join(process.cwd(), '.windsurfrules') },
105
+ { name: 'Cline', path: path_1.default.join(process.cwd(), '.clinerules') },
106
+ ];
107
+ for (const target of rulesTargets) {
336
108
  try {
337
- const isWin = process.platform === 'win32';
338
- const isMac = process.platform === 'darwin';
339
- const candidates = [];
340
- if (isMac) {
341
- candidates.push({
342
- name: 'Cursor',
343
- path: path_1.default.join(HOME, 'Library/Application Support/Cursor/User/settings.json')
344
- });
345
- candidates.push({
346
- name: 'Windsurf',
347
- path: path_1.default.join(HOME, 'Library/Application Support/Windsurf/User/settings.json')
348
- });
349
- candidates.push({
350
- name: 'VS Code',
351
- path: path_1.default.join(HOME, 'Library/Application Support/Code/User/settings.json')
352
- });
353
- }
354
- else if (isWin) {
355
- const appData = process.env.APPDATA || '';
356
- candidates.push({
357
- name: 'Cursor',
358
- path: path_1.default.join(appData, 'Cursor/User/settings.json')
359
- });
360
- candidates.push({
361
- name: 'Windsurf',
362
- path: path_1.default.join(appData, 'Windsurf/User/settings.json')
363
- });
364
- candidates.push({
365
- name: 'VS Code',
366
- path: path_1.default.join(appData, 'Code/User/settings.json')
367
- });
109
+ const dir = path_1.default.dirname(target.path);
110
+ if (target.name === 'Cursor (v2)') {
111
+ if (!fs_1.default.existsSync(dir))
112
+ fs_1.default.mkdirSync(dir, { recursive: true });
368
113
  }
369
- else {
370
- candidates.push({
371
- name: 'Cursor',
372
- path: path_1.default.join(HOME, '.config/Cursor/User/settings.json')
373
- });
374
- candidates.push({
375
- name: 'Windsurf',
376
- path: path_1.default.join(HOME, '.config/Windsurf/User/settings.json')
377
- });
378
- candidates.push({
379
- name: 'VS Code',
380
- path: path_1.default.join(HOME, '.config/Code/User/settings.json')
381
- });
114
+ else if (!fs_1.default.existsSync(dir)) {
115
+ continue;
382
116
  }
383
- for (const cand of candidates) {
384
- const dir = path_1.default.dirname(cand.path);
385
- if (fs_1.default.existsSync(dir)) {
386
- let settings = {};
387
- if (fs_1.default.existsSync(cand.path)) {
388
- try {
389
- settings = JSON.parse(fs_1.default.readFileSync(cand.path, 'utf8'));
390
- }
391
- catch { }
392
- }
393
- let updated = false;
394
- // Settings configuration to route custom openai/anthropic models through the proxy
395
- const updates = {
396
- "openai.baseURL": "http://localhost:8081/v1",
397
- "openai.apiKey": "dummy-key-for-lemma",
398
- "anthropic.baseURL": "http://localhost:8081",
399
- "anthropic.apiKey": "dummy-key-for-lemma"
400
- };
401
- for (const [key, value] of Object.entries(updates)) {
402
- if (settings[key] !== value) {
403
- settings[key] = value;
404
- updated = true;
405
- }
406
- }
407
- if (updated) {
408
- fs_1.default.writeFileSync(cand.path, JSON.stringify(settings, null, 2));
409
- console.log(`โœ… [${cand.name}] Configured editor settings in ${cand.path} to route via local gateway!`);
410
- }
411
- }
117
+ if (fs_1.default.existsSync(target.path)) {
118
+ const existing = fs_1.default.readFileSync(target.path, 'utf8');
119
+ if (existing.includes('Lemma MCP'))
120
+ continue;
412
121
  }
122
+ fs_1.default.writeFileSync(target.path, content, { flag: 'a' });
123
+ console.log(`โœ… [${target.name}] Injected Lemma rules: ${target.path}`);
413
124
  }
414
- catch (e) {
415
- console.log(`โš ๏ธ Could not auto-configure editor settings: ${e.message}`);
125
+ catch {
126
+ console.log(`โš ๏ธ Could not inject rules for ${target.name}`);
416
127
  }
417
128
  }
418
- else {
419
- console.log(`โญ๏ธ [Editor Settings] Skip auto-configuration.`);
420
- }
421
129
  }
422
130
  function startBackgroundClipboardWatcher(cliOpts) {
423
131
  // Load configuration if it exists
@@ -946,6 +654,8 @@ class LemmaServer {
946
654
  this.sessions = new Map();
947
655
  this.timeline = [];
948
656
  this.lastFileContents = new Map();
657
+ this.registry = new ProjectRegistry_1.ProjectRegistry();
658
+ this.projectSessions = new Map();
949
659
  this.port = port;
950
660
  this.projectName = projectName || detectProject();
951
661
  this.projectDir = ensureProjectDir(this.projectName);
@@ -971,6 +681,38 @@ class LemmaServer {
971
681
  const pro = await isPro();
972
682
  res.json({ status: 'ok', project: this.projectName, mode: pro ? 'pro' : 'standard', tier: pro ? 'pro' : 'free', port: this.port });
973
683
  });
684
+ // MCP Status endpoint
685
+ this.app.get('/api/mcp-status', (req, res) => {
686
+ // Update status based on inactivity (60 seconds = idle, 300 seconds = offline)
687
+ const now = Date.now();
688
+ const secondsSinceActive = (now - MCP_STATUS.lastActivity) / 1000;
689
+ let status = 'offline';
690
+ if (secondsSinceActive < 60) {
691
+ status = 'active';
692
+ }
693
+ else if (secondsSinceActive < 300) {
694
+ status = 'idle';
695
+ }
696
+ res.json({
697
+ status,
698
+ lastActivity: new Date(MCP_STATUS.lastActivity).toISOString(),
699
+ toolCallCount: MCP_STATUS.toolCallCount,
700
+ secondsSinceActive: Math.floor(secondsSinceActive),
701
+ uptime: Math.floor(process.uptime())
702
+ });
703
+ });
704
+ // Project registration from MCP handshake
705
+ this.app.post('/api/handshake', (req, res) => {
706
+ const { projectPath, projectName, stack } = req.body;
707
+ if (projectPath && projectName) {
708
+ this.registry.register(projectPath, projectName, stack || '');
709
+ console.log(`๐Ÿ“‹ [Handshake] Project registered: "${projectName}" (${projectPath})`);
710
+ res.json({ ok: true });
711
+ }
712
+ else {
713
+ res.status(400).json({ error: 'projectPath and projectName required' });
714
+ }
715
+ });
974
716
  this.app.get('/v1/models', (req, res) => {
975
717
  res.json({ object: 'list', data: [
976
718
  { id: 'gpt-4o', object: 'model', owned_by: 'lemma-proxy' },
@@ -1064,18 +806,28 @@ class LemmaServer {
1064
806
  const limit = parseInt(req.query.limit) || 50;
1065
807
  const start = (page - 1) * limit;
1066
808
  // Get local proxy events (normalized schema matching what dashboard expects)
1067
- const localEvents = EVENT_LOG.map(e => ({
1068
- id: e.id,
1069
- timestamp: new Date(e.timestamp).toISOString(),
1070
- type: e.type, // e.g., 'cache:hit' or 'cache:miss' which client normalizes to 'cache_hit' or 'cache_miss'
1071
- latency: e.latency,
1072
- tokensSaved: e.tokens,
1073
- costSaved: e.tokens ? (e.tokens / 1000) * 0.002 : 0,
1074
- metadata: {
1075
- provider: e.provider,
1076
- project: e.project
809
+ const localEvents = EVENT_LOG.map(e => {
810
+ const eventData = {
811
+ id: e.id,
812
+ timestamp: new Date(e.timestamp).toISOString(),
813
+ type: e.type, // e.g., 'cache:hit' or 'cache:miss' which client normalizes to 'cache_hit' or 'cache_miss'
814
+ latency: e.latency,
815
+ tokensSaved: e.tokens || e.tokensImpact || 0,
816
+ costSaved: (e.tokens || e.tokensImpact || 0) ? ((e.tokens || e.tokensImpact || 0) / 1000) * 0.002 : 0,
817
+ metadata: {
818
+ provider: e.provider,
819
+ project: e.project
820
+ }
821
+ };
822
+ // Include MCP-specific fields if this is an MCP event
823
+ if (e.type === 'mcp_tool_call') {
824
+ eventData.tool = e.tool;
825
+ eventData.args = e.args;
826
+ eventData.result = e.result;
827
+ eventData.tokensImpact = e.tokensImpact;
1077
828
  }
1078
- }));
829
+ return eventData;
830
+ });
1079
831
  let mergedEvents = [...localEvents];
1080
832
  // Query the stack orchestration API if running to merge stack events
1081
833
  try {
@@ -1465,6 +1217,33 @@ class LemmaServer {
1465
1217
  }
1466
1218
  res.json({ ok: true, snapshot: SavingsLedger_1.savingsLedger.getSnapshot().total });
1467
1219
  });
1220
+ // MCP Tool Event Endpoint โ€” called by MCP server when tools execute
1221
+ this.app.post('/api/mcp-event', (req, res) => {
1222
+ try {
1223
+ const event = req.body;
1224
+ if (!event.type || !event.tool) {
1225
+ return res.status(400).json({ error: 'Missing type or tool' });
1226
+ }
1227
+ // Create normalized event
1228
+ const mcpEvent = {
1229
+ id: Math.random().toString(36).slice(2, 11),
1230
+ type: event.type || 'mcp_tool_call',
1231
+ tool: event.tool,
1232
+ args: event.args || {},
1233
+ result: event.result || 'UNKNOWN',
1234
+ latency: event.latency || 0,
1235
+ tokensImpact: event.tokensImpact || 0,
1236
+ error: event.error || null,
1237
+ timestamp: event.timestamp || new Date().toISOString(),
1238
+ };
1239
+ // Add to EVENT_LOG
1240
+ logEvent(mcpEvent);
1241
+ res.json({ success: true, eventId: mcpEvent.id });
1242
+ }
1243
+ catch (error) {
1244
+ res.status(500).json({ error: 'Internal server error' });
1245
+ }
1246
+ });
1468
1247
  this.app.get('/dashboard/*', (req, res) => res.sendFile(path_1.default.join(this.dashboardPath, 'index.html')));
1469
1248
  }
1470
1249
  }
@@ -1567,6 +1346,15 @@ class LemmaServer {
1567
1346
  }
1568
1347
  console.log(`๐ŸŒŒ \x1b[35m[Multiverse]\x1b[0m Captured micro-snapshot ${snapshot.id} (${changedFiles.length} file edits mapped).`);
1569
1348
  }
1349
+ detectActiveProject(req) {
1350
+ const mcpActive = this.registry.getActive();
1351
+ if (mcpActive)
1352
+ return mcpActive;
1353
+ const sniffed = (0, ContextInjector_1.sniffProjectFromMessages)(req.body.messages || [], this.registry);
1354
+ if (sniffed)
1355
+ return sniffed;
1356
+ return null;
1357
+ }
1570
1358
  async handleCompletion(req, res, provider) {
1571
1359
  const t0 = Date.now();
1572
1360
  const pro = await isPro();
@@ -1687,6 +1475,24 @@ Adjusted Answer:`;
1687
1475
  return res.json(unmaskedData);
1688
1476
  }
1689
1477
  }
1478
+ // โ”€โ”€ 3. Context Injection (for cache misses) โ”€โ”€
1479
+ const activeProject = this.detectActiveProject(req);
1480
+ if (activeProject) {
1481
+ const psKey = activeProject.path;
1482
+ if (!this.projectSessions.has(psKey)) {
1483
+ this.projectSessions.set(psKey, { alreadyInjected: false });
1484
+ }
1485
+ const session = this.projectSessions.get(psKey);
1486
+ const injectionLevel = (0, ContextInjector_1.shouldInject)(activeProject, req.body.messages, session);
1487
+ if (injectionLevel !== 'none') {
1488
+ const context = injectionLevel === 'deep'
1489
+ ? await (0, ContextInjector_1.buildDeepContext)(activeProject, this.port)
1490
+ : (0, ContextInjector_1.buildMinimalContext)(activeProject);
1491
+ req.body.messages.unshift({ role: 'system', content: context });
1492
+ session.alreadyInjected = true;
1493
+ console.log(`๐Ÿง  [Context] Injected ${injectionLevel} context for "${activeProject.name}"`);
1494
+ }
1495
+ }
1690
1496
  // โ”€โ”€ 2. Cache Miss Path โ”€โ”€
1691
1497
  const routingDecision = complexityRouter.evaluate(prompt, originalModel);
1692
1498
  const model = routingDecision.model;
@@ -1927,9 +1733,46 @@ Adjusted Answer:`;
1927
1733
  }
1928
1734
  }
1929
1735
  // โ”€โ”€ CLI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1930
- commander_1.program.name('lemma').description('Lemma Proxy CLI โ€” Intelligent AI Gateway').version(VERSION);
1736
+ function showWelcomeScreen() {
1737
+ console.log(`
1738
+ \x1b[1m\x1b[35m _ \x1b[0m
1739
+ \x1b[1m\x1b[35m | | ___ _ __ ___ __ _ \x1b[0m \x1b[1mLEMMA\x1b[0m \x1b[35mv${VERSION}\x1b[0m
1740
+ \x1b[1m\x1b[35m | | / _ \\ '_ \` _ \\ / _\` |\x1b[0m
1741
+ \x1b[1m\x1b[35m | |__| __/ | | | | | (_| |\x1b[0m Intelligent AI Gateway
1742
+ \x1b[1m\x1b[35m |_____\\___|_| |_| |_|\\__,_|\x1b[0m
1743
+
1744
+ \x1b[1mQuick Start\x1b[0m
1745
+ \x1b[33mlemma init\x1b[0m Initialize Lemma in this project
1746
+ \x1b[33mlemma start\x1b[0m Start the proxy server
1747
+ \x1b[33mlemma mcp\x1b[0m Start MCP Server for Cursor/Claude
1748
+
1749
+ \x1b[1mFree Features\x1b[0m (all included)
1750
+ ๐Ÿ”’ Privacy Firewall ๐Ÿ’พ Exact-match Cache
1751
+ ๐Ÿ”€ Complexity Router ๐Ÿ“ก Telepathic Clipboard
1752
+ ๐Ÿ“Š Basic Usage Stats ๐Ÿง  MCP Tools & Resources
1753
+
1754
+ \x1b[1mPro Features\x1b[0m (\x1b[35mlemma activate <key>\x1b[0m or \x1b[36mhttps://lemma.nxus.studio/upgrade\x1b[0m)
1755
+ ๐ŸŽฏ Semantic Vector Cache ๐ŸŒ Hive Mind Cloud Sync
1756
+ ๐Ÿ›ธ Autopilot Healer ๐Ÿฉบ Auto-Diagnose & Heal
1757
+ ๐ŸŒŒ Codebase Multiverse ๐Ÿ—๏ธ Architecture Onboarding
1758
+
1759
+ \x1b[90mRun \`lemma --help\` for all commands.\x1b[0m
1760
+ `);
1761
+ }
1762
+ commander_1.program
1763
+ .name('lemma')
1764
+ .description('Intelligent AI Gateway โ€” cache, route, and optimize AI requests')
1765
+ .version(VERSION)
1766
+ .addHelpText('after', `
1767
+ \x1b[1mPlan Comparison\x1b[0m
1768
+ \x1b[32mFree\x1b[0m 500 req/mo ยท Exact cache ยท Privacy firewall ยท MCP tools
1769
+ \x1b[35mPro\x1b[0m Unlimited ยท Semantic cache ยท Cloud sync ยท Autopilot ยท Heal
1770
+ \x1b[36mhttps://lemma.nxus.studio/upgrade\x1b[0m
1771
+
1772
+ \x1b[90mActivate Pro: lemma activate <key>\x1b[0m
1773
+ `);
1931
1774
  commander_1.program.command('mcp')
1932
- .description('Start the Lemma MCP Server (Model Context Protocol)')
1775
+ .description('Start the MCP Server for Cursor / Claude Desktop integration')
1933
1776
  .action(() => {
1934
1777
  const { spawn } = require('child_process');
1935
1778
  // Robust search candidates for both development (TS source) and compiled (JS production) environments.
@@ -1984,6 +1827,7 @@ commander_1.program.command('start')
1984
1827
  // Keep configurations up to date even if server was already started
1985
1828
  const runningProject = resp.data.project || detectProject();
1986
1829
  autoConfigureAll(runningProject, { configure: opts.configure });
1830
+ injectIdeRules(runningProject);
1987
1831
  startBackgroundClipboardWatcher({ clipboard: opts.clipboard });
1988
1832
  process.exit(0);
1989
1833
  }
@@ -1991,6 +1835,7 @@ commander_1.program.command('start')
1991
1835
  catch { }
1992
1836
  const startProject = opts.project || detectProject();
1993
1837
  autoConfigureAll(startProject, { configure: opts.configure });
1838
+ injectIdeRules(startProject);
1994
1839
  startBackgroundClipboardWatcher({ clipboard: opts.clipboard });
1995
1840
  // Automatically enable autopilot if stack is enabled and user is Pro
1996
1841
  const runAutopilot = opts.autopilot || opts.stack;
@@ -2203,8 +2048,12 @@ commander_1.program.command('init')
2203
2048
  }
2204
2049
  console.log('\n๐Ÿง  \x1b[35mAuto-Setup MCP and redirection overrides for AI IDEs...\x1b[0m');
2205
2050
  autoConfigureAll(project, { configure: opts.configure });
2206
- console.log('\nโœจ Project initialized for the Agentic Era!');
2207
- console.log('๐Ÿš€ Run "lemma start" to begin.\n');
2051
+ injectIdeRules(project);
2052
+ console.log('\nโœจ Project initialized for the Agentic Era!\n');
2053
+ console.log('\x1b[1mNext steps:\x1b[0m');
2054
+ console.log(' \x1b[33mlemma start\x1b[0m Start the proxy & unlock AI savings');
2055
+ console.log(' \x1b[33mlemma upgrade\x1b[0m See Pro features & get a key');
2056
+ console.log(' \x1b[33mlemma --help\x1b[0m All available commands\n');
2208
2057
  });
2209
2058
  // โ”€โ”€ Auto-Healing Diagnostics (Zero-Cost Suite) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
2210
2059
  async function performAutoHeal(apply) {
@@ -2374,7 +2223,7 @@ Ensure you return strictly JSON. Do not return any other text outside the JSON b
2374
2223
  }
2375
2224
  }
2376
2225
  commander_1.program.command('heal')
2377
- .description('Diagnose and auto-heal the latest local server crash from .lemma/live-context.md')
2226
+ .description('[Pro] Diagnose and auto-heal the latest local server crash from .lemma/live-context.md')
2378
2227
  .option('--apply', 'Apply the auto-generated code fix automatically', false)
2379
2228
  .action(async (opts) => {
2380
2229
  try {
@@ -2403,7 +2252,7 @@ commander_1.program.command('heal')
2403
2252
  }
2404
2253
  });
2405
2254
  commander_1.program.command('autopilot')
2406
- .description('Start the autonomous background compiler watcher & active healer')
2255
+ .description('[Pro] Start the autonomous background compiler watcher & active healer')
2407
2256
  .option('--dir <directory>', 'Directory to monitor for changes', 'src')
2408
2257
  .action(async (opts) => {
2409
2258
  try {
@@ -2428,6 +2277,26 @@ commander_1.program.command('autopilot')
2428
2277
  process.exit(1);
2429
2278
  }
2430
2279
  });
2280
+ commander_1.program.command('upgrade')
2281
+ .description('Learn about Pro features and get an activation key')
2282
+ .action(() => {
2283
+ console.log(`
2284
+ \x1b[1m\x1b[35mLemma Pro\x1b[0m โ€” Unlimited AI Gateway power
2285
+
2286
+ \x1b[1mPro features:\x1b[0m
2287
+ ๐ŸŽฏ \x1b[1mSemantic Vector Cache\x1b[0m โ€” Smarter cache hits via vector similarity
2288
+ ๐ŸŒ \x1b[1mHive Mind Cloud Sync\x1b[0m โ€” Cross-project cache & bug telepathy
2289
+ ๐Ÿ›ธ \x1b[1mAutopilot Healer\x1b[0m โ€” Autonomous background compiler watcher
2290
+ ๐Ÿฉบ \x1b[1mAuto-Diagnose & Heal\x1b[0m โ€” AI-driven crash analysis & patch
2291
+ ๐ŸŒŒ \x1b[1mCodebase Multiverse\x1b[0m โ€” AST time-travel & snapshots
2292
+ ๐Ÿ—๏ธ \x1b[1mArchitecture Onboarding\x1b[0m โ€” Zero-shot project understanding
2293
+ ๐Ÿ”“ \x1b[1mUnlimited requests\x1b[0m โ€” No monthly caps
2294
+
2295
+ \x1b[36m https://lemma.nxus.studio/upgrade\x1b[0m
2296
+
2297
+ Already have a key? Run: \x1b[33mlemma activate <key>\x1b[0m
2298
+ `);
2299
+ });
2431
2300
  commander_1.program.command('clipboard')
2432
2301
  .description('Start the Lemma Local Clipboard Telepathic Optimizer (auto-squeezes copied code blocks)')
2433
2302
  .option('--interval <ms>', 'Polling interval in milliseconds', '1000')
@@ -2445,6 +2314,11 @@ commander_1.program.command('clipboard')
2445
2314
  }
2446
2315
  });
2447
2316
  if (process.argv[1] && (process.argv[1].includes('lemma') || process.argv[1].endsWith('.cjs') || process.argv[1].endsWith('.js'))) {
2317
+ // Bare `lemma` with no args โ†’ show welcome screen
2318
+ if (process.argv.length <= 2) {
2319
+ showWelcomeScreen();
2320
+ process.exit(0);
2321
+ }
2448
2322
  commander_1.program.parse(process.argv);
2449
2323
  }
2450
2324
  //# sourceMappingURL=lemma-proxy.js.map