@nxuss/lemma 0.7.2 → 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.
- package/README.md +6 -5
- package/dashboard/dist/assets/{index-C3X0fqmd.js → index-BNYoIN8-.js} +34 -34
- package/dashboard/dist/assets/index-BNYoIN8-.js.map +1 -0
- package/dashboard/dist/assets/index-DtEKr0hI.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/cjs/cli/lemma-proxy.d.ts +0 -6
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +153 -348
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/mcp/index.js +103 -1
- package/dist/cjs/mcp/index.js.map +1 -1
- package/dist/cjs/mcp/resources.d.ts.map +1 -1
- package/dist/cjs/mcp/resources.js +75 -0
- package/dist/cjs/mcp/resources.js.map +1 -1
- package/dist/cjs/mcp/tools.d.ts.map +1 -1
- package/dist/cjs/mcp/tools.js +174 -0
- package/dist/cjs/mcp/tools.js.map +1 -1
- package/dist/cjs/proxy/ContextInjector.d.ts +14 -0
- package/dist/cjs/proxy/ContextInjector.d.ts.map +1 -0
- package/dist/cjs/proxy/ContextInjector.js +148 -0
- package/dist/cjs/proxy/ContextInjector.js.map +1 -0
- package/dist/cjs/proxy/ProjectRegistry.d.ts +17 -0
- package/dist/cjs/proxy/ProjectRegistry.d.ts.map +1 -0
- package/dist/cjs/proxy/ProjectRegistry.js +60 -0
- package/dist/cjs/proxy/ProjectRegistry.js.map +1 -0
- package/dist/esm/cli/lemma-proxy.d.ts +0 -6
- package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +153 -347
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/mcp/index.js +103 -1
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/mcp/resources.d.ts.map +1 -1
- package/dist/esm/mcp/resources.js +75 -0
- package/dist/esm/mcp/resources.js.map +1 -1
- package/dist/esm/mcp/tools.d.ts.map +1 -1
- package/dist/esm/mcp/tools.js +175 -1
- package/dist/esm/mcp/tools.js.map +1 -1
- package/dist/esm/proxy/ContextInjector.d.ts +14 -0
- package/dist/esm/proxy/ContextInjector.d.ts.map +1 -0
- package/dist/esm/proxy/ContextInjector.js +108 -0
- package/dist/esm/proxy/ContextInjector.js.map +1 -0
- package/dist/esm/proxy/ProjectRegistry.d.ts +17 -0
- package/dist/esm/proxy/ProjectRegistry.d.ts.map +1 -0
- package/dist/esm/proxy/ProjectRegistry.js +56 -0
- package/dist/esm/proxy/ProjectRegistry.js.map +1 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/index-BoZujIjB.css +0 -1
- package/dashboard/dist/assets/index-C3X0fqmd.js.map +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([]) };
|
|
@@ -82,354 +83,49 @@ function logEvent(event) {
|
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
await fs_1.default.promises.writeFile(tempFile, JSON.stringify(data, null, 2));
|
|
108
|
-
await fs_1.default.promises.rename(tempFile, file);
|
|
109
|
-
}
|
|
110
|
-
catch { }
|
|
111
|
-
};
|
|
112
|
-
const nextChain = chain.then(writeOperation).catch(() => { });
|
|
113
|
-
writeQueues.set(file, nextChain);
|
|
114
|
-
return nextChain;
|
|
115
|
-
}
|
|
116
|
-
function projectHash(name) {
|
|
117
|
-
return crypto_1.default.createHash('sha1').update(name).digest('hex').slice(0, 12);
|
|
118
|
-
}
|
|
119
|
-
function getCollectionName(projectName) {
|
|
120
|
-
let cleanName = projectName.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
121
|
-
cleanName = cleanName.replace(/^[^a-zA-Z0-9]+/, '');
|
|
122
|
-
cleanName = cleanName.replace(/[^a-zA-Z0-9]+$/, '');
|
|
123
|
-
if (cleanName.length < 3) {
|
|
124
|
-
cleanName = 'lemma_proj_' + cleanName;
|
|
125
|
-
}
|
|
126
|
-
return `lemma-cache-${cleanName}`;
|
|
127
|
-
}
|
|
128
|
-
function detectProject() {
|
|
129
|
-
const pkgPath = path_1.default.join(process.cwd(), 'package.json');
|
|
130
|
-
if (fs_1.default.existsSync(pkgPath)) {
|
|
131
|
-
try {
|
|
132
|
-
const p = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
|
|
133
|
-
if (p.name)
|
|
134
|
-
return p.name;
|
|
135
|
-
}
|
|
136
|
-
catch { }
|
|
137
|
-
}
|
|
138
|
-
return path_1.default.basename(process.cwd());
|
|
139
|
-
}
|
|
140
|
-
function ensureProjectDir(name) {
|
|
141
|
-
const dir = path_1.default.join(CACHE_DIR, projectHash(name));
|
|
142
|
-
if (!fs_1.default.existsSync(dir))
|
|
143
|
-
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
144
|
-
const marker = path_1.default.join(dir, 'project-name.txt');
|
|
145
|
-
if (!fs_1.default.existsSync(marker))
|
|
146
|
-
fs_1.default.writeFileSync(marker, name);
|
|
147
|
-
return dir;
|
|
148
|
-
}
|
|
149
|
-
function getPidByPort(port) {
|
|
150
|
-
const { execSync } = require('child_process');
|
|
151
|
-
try {
|
|
152
|
-
if (process.platform === 'win32') {
|
|
153
|
-
const out = execSync(`netstat -ano | findstr :${port}`).toString();
|
|
154
|
-
const match = out.match(/LISTENING\s+(\d+)/);
|
|
155
|
-
return match ? match[1] : null;
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
const out = execSync(`lsof -t -i :${port}`).toString().trim();
|
|
159
|
-
return out.split('\n')[0]; // Take first if multiple
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
async function checkDependency(url) {
|
|
167
|
-
try {
|
|
168
|
-
const res = await axios_1.default.get(url, { timeout: 1000, validateStatus: () => true });
|
|
169
|
-
return res.status >= 200 && res.status < 500;
|
|
170
|
-
}
|
|
171
|
-
catch {
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
async function ensureChromaRunning() {
|
|
176
|
-
const chromaPort = process.env.CHROMA_PORT || '8000';
|
|
177
|
-
const chromaHost = process.env.CHROMA_HOST || 'http://localhost';
|
|
178
|
-
const chromaUrl = `${chromaHost}:${chromaPort}`;
|
|
179
|
-
const isChromaUp = await checkDependency(`${chromaUrl}/api/v1/heartbeat`);
|
|
180
|
-
if (isChromaUp) {
|
|
181
|
-
console.log(`✅ \x1b[32m[ChromaDB]\x1b[0m ChromaDB is already running on port ${chromaPort}`);
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
console.log(`\n📦 \x1b[35m[ChromaDB]\x1b[0m ChromaDB is not running on port ${chromaPort}.`);
|
|
185
|
-
console.log(`🔄 Starting ChromaDB in background...`);
|
|
186
|
-
const chromaDataPath = path_1.default.join(CACHE_DIR, 'chroma_data');
|
|
187
|
-
try {
|
|
188
|
-
const { spawn } = require('child_process');
|
|
189
|
-
if (!fs_1.default.existsSync(chromaDataPath)) {
|
|
190
|
-
fs_1.default.mkdirSync(chromaDataPath, { recursive: true });
|
|
191
|
-
}
|
|
192
|
-
const chromaProcess = spawn('chroma', ['run', '--path', chromaDataPath, '--port', chromaPort], {
|
|
193
|
-
stdio: 'ignore',
|
|
194
|
-
detached: true
|
|
195
|
-
});
|
|
196
|
-
chromaProcess.unref();
|
|
197
|
-
// Wait up to 5 seconds for ChromaDB to start
|
|
198
|
-
for (let i = 0; i < 5; i++) {
|
|
199
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
200
|
-
const up = await checkDependency(`${chromaUrl}/api/v1/heartbeat`);
|
|
201
|
-
if (up) {
|
|
202
|
-
console.log(`✅ \x1b[32m[ChromaDB]\x1b[0m ChromaDB has been started and is ready!`);
|
|
203
|
-
return true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
console.log(`⚠️ \x1b[33m[ChromaDB]\x1b[0m ChromaDB was started but is taking longer to respond. It will continue starting in the background.`);
|
|
207
|
-
return true;
|
|
208
|
-
}
|
|
209
|
-
catch (err) {
|
|
210
|
-
console.error(`❌ Failed to automatically start ChromaDB: ${err.message}`);
|
|
211
|
-
console.error(`👉 Please start it manually: chroma run --path ${chromaDataPath} --port ${chromaPort}`);
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function ensureGitIgnore() {
|
|
216
|
-
const gi = path_1.default.join(process.cwd(), '.gitignore');
|
|
217
|
-
let added = false;
|
|
218
|
-
try {
|
|
219
|
-
let content = '';
|
|
220
|
-
if (fs_1.default.existsSync(gi)) {
|
|
221
|
-
content = fs_1.default.readFileSync(gi, 'utf8');
|
|
222
|
-
}
|
|
223
|
-
const appendLines = [];
|
|
224
|
-
if (!content.includes('.lemma/')) {
|
|
225
|
-
appendLines.push('# Lemma Context Logs', '.lemma/');
|
|
226
|
-
added = true;
|
|
227
|
-
}
|
|
228
|
-
if (!content.includes('chroma_data/')) {
|
|
229
|
-
appendLines.push('# Lemma Local Vector Database', 'chroma_data/');
|
|
230
|
-
added = true;
|
|
231
|
-
}
|
|
232
|
-
if (appendLines.length > 0) {
|
|
233
|
-
const prefix = content.endsWith('\n') || content === '' ? '' : '\n';
|
|
234
|
-
fs_1.default.appendFileSync(gi, prefix + appendLines.join('\n') + '\n');
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
catch { }
|
|
238
|
-
return added;
|
|
239
|
-
}
|
|
240
|
-
const CLIPBOARD_PID_FILE = path_1.default.join(CACHE_DIR, 'clipboard.pid');
|
|
241
|
-
function autoConfigureAll(projectName, cliOpts) {
|
|
242
|
-
const project = projectName || detectProject();
|
|
243
|
-
const HOME = process.env.HOME || process.env.USERPROFILE || '~';
|
|
244
|
-
// Load configuration if it exists
|
|
245
|
-
let configDisabled = false;
|
|
246
|
-
let configEditor = true;
|
|
247
|
-
let configShell = true;
|
|
248
|
-
let configClaude = true;
|
|
249
|
-
try {
|
|
250
|
-
const configPath = path_1.default.join(process.cwd(), 'lemma.config.json');
|
|
251
|
-
if (fs_1.default.existsSync(configPath)) {
|
|
252
|
-
const rawConfig = fs_1.default.readFileSync(configPath, 'utf8');
|
|
253
|
-
const config = JSON.parse(rawConfig);
|
|
254
|
-
const systemConfig = config.system || {};
|
|
255
|
-
const autoConfig = systemConfig.autoConfigure || {};
|
|
256
|
-
if (autoConfig.disabled === true)
|
|
257
|
-
configDisabled = true;
|
|
258
|
-
if (autoConfig.editor === false)
|
|
259
|
-
configEditor = false;
|
|
260
|
-
if (autoConfig.shell === false)
|
|
261
|
-
configShell = false;
|
|
262
|
-
if (autoConfig.claude === false)
|
|
263
|
-
configClaude = false;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
catch { }
|
|
267
|
-
// Merge CLI overrides and config
|
|
268
|
-
const shouldConfigure = cliOpts?.configure !== false && !configDisabled;
|
|
269
|
-
if (!shouldConfigure) {
|
|
270
|
-
console.log(`⏭️ [Auto-Configure] Skipped auto-configuration (disabled via options or config).`);
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
const runEditor = cliOpts?.editor !== false && configEditor;
|
|
274
|
-
const runShell = cliOpts?.shell !== false && configShell;
|
|
275
|
-
const runClaude = cliOpts?.claude !== false && configClaude;
|
|
276
|
-
// 1. Claude Desktop Config
|
|
277
|
-
if (runClaude) {
|
|
278
|
-
try {
|
|
279
|
-
const isWin = process.platform === 'win32';
|
|
280
|
-
const claudePath = isWin
|
|
281
|
-
? path_1.default.join(process.env.APPDATA || '', 'Claude/claude_desktop_config.json')
|
|
282
|
-
: path_1.default.join(HOME, 'Library/Application Support/Claude/claude_desktop_config.json');
|
|
283
|
-
if (fs_1.default.existsSync(path_1.default.dirname(claudePath))) {
|
|
284
|
-
let config = { mcpServers: {} };
|
|
285
|
-
if (fs_1.default.existsSync(claudePath)) {
|
|
286
|
-
try {
|
|
287
|
-
config = JSON.parse(fs_1.default.readFileSync(claudePath, 'utf8'));
|
|
288
|
-
}
|
|
289
|
-
catch { }
|
|
290
|
-
}
|
|
291
|
-
if (!config.mcpServers)
|
|
292
|
-
config.mcpServers = {};
|
|
293
|
-
config.mcpServers.lemma = {
|
|
294
|
-
command: 'npx',
|
|
295
|
-
args: ['-y', '@nxuss/lemma', 'mcp'],
|
|
296
|
-
env: { LEMMA_PROJECT: project }
|
|
297
|
-
};
|
|
298
|
-
fs_1.default.writeFileSync(claudePath, JSON.stringify(config, null, 2));
|
|
299
|
-
console.log(`✅ [Claude Desktop] Configured automatically in: ${claudePath} (Restart Claude to activate)`);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
catch (e) {
|
|
303
|
-
console.log(`⚠️ Could not auto-configure Claude Desktop: ${e.message}`);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
console.log(`⏭️ [Claude Desktop] Skip auto-configuration.`);
|
|
308
|
-
}
|
|
309
|
-
// 2. Shell Profiles Environment Override
|
|
310
|
-
if (runShell) {
|
|
311
|
-
try {
|
|
312
|
-
const profiles = [
|
|
313
|
-
path_1.default.join(HOME, '.zshrc'),
|
|
314
|
-
path_1.default.join(HOME, '.bashrc'),
|
|
315
|
-
path_1.default.join(HOME, '.bash_profile'),
|
|
316
|
-
path_1.default.join(HOME, '.profile')
|
|
317
|
-
];
|
|
318
|
-
const lines = [
|
|
319
|
-
'',
|
|
320
|
-
'# Lemma AI Gateway Overrides',
|
|
321
|
-
'export OPENAI_BASE_URL="http://localhost:8081/v1"',
|
|
322
|
-
'export ANTHROPIC_BASE_URL="http://localhost:8081"',
|
|
323
|
-
'export LEMMA_PROJECT="' + project + '"',
|
|
324
|
-
''
|
|
325
|
-
].join('\n');
|
|
326
|
-
for (const profile of profiles) {
|
|
327
|
-
if (fs_1.default.existsSync(profile)) {
|
|
328
|
-
try {
|
|
329
|
-
const content = fs_1.default.readFileSync(profile, 'utf8');
|
|
330
|
-
if (!content.includes('OPENAI_BASE_URL') && !content.includes('LEMMA_PROJECT')) {
|
|
331
|
-
fs_1.default.appendFileSync(profile, lines);
|
|
332
|
-
console.log(`✅ [Shell Profile] Configured ${path_1.default.basename(profile)} with local redirect variables!`);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
catch { }
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
catch (e) {
|
|
340
|
-
console.log(`⚠️ Could not auto-configure shell profiles: ${e.message}`);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
else {
|
|
344
|
-
console.log(`⏭️ [Shell Profile] Skip auto-configuration.`);
|
|
345
|
-
}
|
|
346
|
-
// 3. Cursor, Windsurf & VS Code settings.json updates
|
|
347
|
-
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) {
|
|
348
108
|
try {
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
candidates.push({
|
|
354
|
-
name: 'Cursor',
|
|
355
|
-
path: path_1.default.join(HOME, 'Library/Application Support/Cursor/User/settings.json')
|
|
356
|
-
});
|
|
357
|
-
candidates.push({
|
|
358
|
-
name: 'Windsurf',
|
|
359
|
-
path: path_1.default.join(HOME, 'Library/Application Support/Windsurf/User/settings.json')
|
|
360
|
-
});
|
|
361
|
-
candidates.push({
|
|
362
|
-
name: 'VS Code',
|
|
363
|
-
path: path_1.default.join(HOME, 'Library/Application Support/Code/User/settings.json')
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
else if (isWin) {
|
|
367
|
-
const appData = process.env.APPDATA || '';
|
|
368
|
-
candidates.push({
|
|
369
|
-
name: 'Cursor',
|
|
370
|
-
path: path_1.default.join(appData, 'Cursor/User/settings.json')
|
|
371
|
-
});
|
|
372
|
-
candidates.push({
|
|
373
|
-
name: 'Windsurf',
|
|
374
|
-
path: path_1.default.join(appData, 'Windsurf/User/settings.json')
|
|
375
|
-
});
|
|
376
|
-
candidates.push({
|
|
377
|
-
name: 'VS Code',
|
|
378
|
-
path: path_1.default.join(appData, 'Code/User/settings.json')
|
|
379
|
-
});
|
|
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 });
|
|
380
113
|
}
|
|
381
|
-
else {
|
|
382
|
-
|
|
383
|
-
name: 'Cursor',
|
|
384
|
-
path: path_1.default.join(HOME, '.config/Cursor/User/settings.json')
|
|
385
|
-
});
|
|
386
|
-
candidates.push({
|
|
387
|
-
name: 'Windsurf',
|
|
388
|
-
path: path_1.default.join(HOME, '.config/Windsurf/User/settings.json')
|
|
389
|
-
});
|
|
390
|
-
candidates.push({
|
|
391
|
-
name: 'VS Code',
|
|
392
|
-
path: path_1.default.join(HOME, '.config/Code/User/settings.json')
|
|
393
|
-
});
|
|
114
|
+
else if (!fs_1.default.existsSync(dir)) {
|
|
115
|
+
continue;
|
|
394
116
|
}
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
if (
|
|
398
|
-
|
|
399
|
-
if (fs_1.default.existsSync(cand.path)) {
|
|
400
|
-
try {
|
|
401
|
-
settings = JSON.parse(fs_1.default.readFileSync(cand.path, 'utf8'));
|
|
402
|
-
}
|
|
403
|
-
catch { }
|
|
404
|
-
}
|
|
405
|
-
let updated = false;
|
|
406
|
-
// Settings configuration to route custom openai/anthropic models through the proxy
|
|
407
|
-
const updates = {
|
|
408
|
-
"openai.baseURL": "http://localhost:8081/v1",
|
|
409
|
-
"openai.apiKey": "dummy-key-for-lemma",
|
|
410
|
-
"anthropic.baseURL": "http://localhost:8081",
|
|
411
|
-
"anthropic.apiKey": "dummy-key-for-lemma"
|
|
412
|
-
};
|
|
413
|
-
for (const [key, value] of Object.entries(updates)) {
|
|
414
|
-
if (settings[key] !== value) {
|
|
415
|
-
settings[key] = value;
|
|
416
|
-
updated = true;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
if (updated) {
|
|
420
|
-
fs_1.default.writeFileSync(cand.path, JSON.stringify(settings, null, 2));
|
|
421
|
-
console.log(`✅ [${cand.name}] Configured editor settings in ${cand.path} to route via local gateway!`);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
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;
|
|
424
121
|
}
|
|
122
|
+
fs_1.default.writeFileSync(target.path, content, { flag: 'a' });
|
|
123
|
+
console.log(`✅ [${target.name}] Injected Lemma rules: ${target.path}`);
|
|
425
124
|
}
|
|
426
|
-
catch
|
|
427
|
-
console.log(`⚠️ Could not
|
|
125
|
+
catch {
|
|
126
|
+
console.log(`⚠️ Could not inject rules for ${target.name}`);
|
|
428
127
|
}
|
|
429
128
|
}
|
|
430
|
-
else {
|
|
431
|
-
console.log(`⏭️ [Editor Settings] Skip auto-configuration.`);
|
|
432
|
-
}
|
|
433
129
|
}
|
|
434
130
|
function startBackgroundClipboardWatcher(cliOpts) {
|
|
435
131
|
// Load configuration if it exists
|
|
@@ -958,6 +654,8 @@ class LemmaServer {
|
|
|
958
654
|
this.sessions = new Map();
|
|
959
655
|
this.timeline = [];
|
|
960
656
|
this.lastFileContents = new Map();
|
|
657
|
+
this.registry = new ProjectRegistry_1.ProjectRegistry();
|
|
658
|
+
this.projectSessions = new Map();
|
|
961
659
|
this.port = port;
|
|
962
660
|
this.projectName = projectName || detectProject();
|
|
963
661
|
this.projectDir = ensureProjectDir(this.projectName);
|
|
@@ -1003,6 +701,18 @@ class LemmaServer {
|
|
|
1003
701
|
uptime: Math.floor(process.uptime())
|
|
1004
702
|
});
|
|
1005
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
|
+
});
|
|
1006
716
|
this.app.get('/v1/models', (req, res) => {
|
|
1007
717
|
res.json({ object: 'list', data: [
|
|
1008
718
|
{ id: 'gpt-4o', object: 'model', owned_by: 'lemma-proxy' },
|
|
@@ -1636,6 +1346,15 @@ class LemmaServer {
|
|
|
1636
1346
|
}
|
|
1637
1347
|
console.log(`🌌 \x1b[35m[Multiverse]\x1b[0m Captured micro-snapshot ${snapshot.id} (${changedFiles.length} file edits mapped).`);
|
|
1638
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
|
+
}
|
|
1639
1358
|
async handleCompletion(req, res, provider) {
|
|
1640
1359
|
const t0 = Date.now();
|
|
1641
1360
|
const pro = await isPro();
|
|
@@ -1756,6 +1475,24 @@ Adjusted Answer:`;
|
|
|
1756
1475
|
return res.json(unmaskedData);
|
|
1757
1476
|
}
|
|
1758
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
|
+
}
|
|
1759
1496
|
// ── 2. Cache Miss Path ──
|
|
1760
1497
|
const routingDecision = complexityRouter.evaluate(prompt, originalModel);
|
|
1761
1498
|
const model = routingDecision.model;
|
|
@@ -1996,9 +1733,46 @@ Adjusted Answer:`;
|
|
|
1996
1733
|
}
|
|
1997
1734
|
}
|
|
1998
1735
|
// ── CLI ────────────────────────────────────────────────────────────────────────
|
|
1999
|
-
|
|
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
|
+
`);
|
|
2000
1774
|
commander_1.program.command('mcp')
|
|
2001
|
-
.description('Start the
|
|
1775
|
+
.description('Start the MCP Server for Cursor / Claude Desktop integration')
|
|
2002
1776
|
.action(() => {
|
|
2003
1777
|
const { spawn } = require('child_process');
|
|
2004
1778
|
// Robust search candidates for both development (TS source) and compiled (JS production) environments.
|
|
@@ -2053,6 +1827,7 @@ commander_1.program.command('start')
|
|
|
2053
1827
|
// Keep configurations up to date even if server was already started
|
|
2054
1828
|
const runningProject = resp.data.project || detectProject();
|
|
2055
1829
|
autoConfigureAll(runningProject, { configure: opts.configure });
|
|
1830
|
+
injectIdeRules(runningProject);
|
|
2056
1831
|
startBackgroundClipboardWatcher({ clipboard: opts.clipboard });
|
|
2057
1832
|
process.exit(0);
|
|
2058
1833
|
}
|
|
@@ -2060,6 +1835,7 @@ commander_1.program.command('start')
|
|
|
2060
1835
|
catch { }
|
|
2061
1836
|
const startProject = opts.project || detectProject();
|
|
2062
1837
|
autoConfigureAll(startProject, { configure: opts.configure });
|
|
1838
|
+
injectIdeRules(startProject);
|
|
2063
1839
|
startBackgroundClipboardWatcher({ clipboard: opts.clipboard });
|
|
2064
1840
|
// Automatically enable autopilot if stack is enabled and user is Pro
|
|
2065
1841
|
const runAutopilot = opts.autopilot || opts.stack;
|
|
@@ -2272,8 +2048,12 @@ commander_1.program.command('init')
|
|
|
2272
2048
|
}
|
|
2273
2049
|
console.log('\n🧠 \x1b[35mAuto-Setup MCP and redirection overrides for AI IDEs...\x1b[0m');
|
|
2274
2050
|
autoConfigureAll(project, { configure: opts.configure });
|
|
2275
|
-
|
|
2276
|
-
console.log('
|
|
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');
|
|
2277
2057
|
});
|
|
2278
2058
|
// ── Auto-Healing Diagnostics (Zero-Cost Suite) ───────────────────────────────
|
|
2279
2059
|
async function performAutoHeal(apply) {
|
|
@@ -2443,7 +2223,7 @@ Ensure you return strictly JSON. Do not return any other text outside the JSON b
|
|
|
2443
2223
|
}
|
|
2444
2224
|
}
|
|
2445
2225
|
commander_1.program.command('heal')
|
|
2446
|
-
.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')
|
|
2447
2227
|
.option('--apply', 'Apply the auto-generated code fix automatically', false)
|
|
2448
2228
|
.action(async (opts) => {
|
|
2449
2229
|
try {
|
|
@@ -2472,7 +2252,7 @@ commander_1.program.command('heal')
|
|
|
2472
2252
|
}
|
|
2473
2253
|
});
|
|
2474
2254
|
commander_1.program.command('autopilot')
|
|
2475
|
-
.description('Start the autonomous background compiler watcher & active healer')
|
|
2255
|
+
.description('[Pro] Start the autonomous background compiler watcher & active healer')
|
|
2476
2256
|
.option('--dir <directory>', 'Directory to monitor for changes', 'src')
|
|
2477
2257
|
.action(async (opts) => {
|
|
2478
2258
|
try {
|
|
@@ -2497,6 +2277,26 @@ commander_1.program.command('autopilot')
|
|
|
2497
2277
|
process.exit(1);
|
|
2498
2278
|
}
|
|
2499
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
|
+
});
|
|
2500
2300
|
commander_1.program.command('clipboard')
|
|
2501
2301
|
.description('Start the Lemma Local Clipboard Telepathic Optimizer (auto-squeezes copied code blocks)')
|
|
2502
2302
|
.option('--interval <ms>', 'Polling interval in milliseconds', '1000')
|
|
@@ -2514,6 +2314,11 @@ commander_1.program.command('clipboard')
|
|
|
2514
2314
|
}
|
|
2515
2315
|
});
|
|
2516
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
|
+
}
|
|
2517
2322
|
commander_1.program.parse(process.argv);
|
|
2518
2323
|
}
|
|
2519
2324
|
//# sourceMappingURL=lemma-proxy.js.map
|