@nxuss/lemma 0.7.2 โ 0.7.4
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 +251 -338
- 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 +251 -337
- 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 +2 -2
- 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([]) };
|
|
@@ -55,381 +56,184 @@ const PORT_FILE = path_1.default.join(CACHE_DIR, 'proxy.port');
|
|
|
55
56
|
const STATS_FILE = path_1.default.join(CACHE_DIR, 'stats.json');
|
|
56
57
|
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
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
};
|
|
66
|
-
function logEvent(event) {
|
|
67
|
-
const ev = { id: Math.random().toString(36).slice(2, 11), timestamp: Date.now(), ...event };
|
|
68
|
-
EVENT_LOG.push(ev);
|
|
69
|
-
if (EVENT_LOG.length > MAX_EVENTS)
|
|
70
|
-
EVENT_LOG.shift();
|
|
71
|
-
// Track MCP activity
|
|
72
|
-
if (event.type === 'mcp_tool_call') {
|
|
73
|
-
MCP_STATUS.lastActivity = Date.now();
|
|
74
|
-
MCP_STATUS.toolCallCount++;
|
|
75
|
-
MCP_STATUS.status = 'active';
|
|
59
|
+
const CLIPBOARD_PID_FILE = path_1.default.join(CACHE_DIR, 'clipboard.pid');
|
|
60
|
+
const FREE_LIMIT = 300;
|
|
61
|
+
const VALIDATE_URL = 'https://lemma.nxus.studio/api/validate-license';
|
|
62
|
+
async function checkDependency(url) {
|
|
63
|
+
try {
|
|
64
|
+
const resp = await fetch(url, { method: 'HEAD', signal: AbortSignal.timeout(2000) });
|
|
65
|
+
return resp.ok;
|
|
76
66
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const snap = SavingsLedger_1.savingsLedger.getSnapshot();
|
|
80
|
-
if (snap.total.tokensSaved > 0) {
|
|
81
|
-
console.log('\n' + SavingsLedger_1.savingsLedger.getTerminalSummary() + '\n');
|
|
82
|
-
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
83
69
|
}
|
|
84
70
|
}
|
|
85
|
-
|
|
86
|
-
const WARN_PCT = 0.8;
|
|
87
|
-
const VALIDATE_URL = 'https://lemma.nxus.studio/api/v1/validate';
|
|
88
|
-
if (!fs_1.default.existsSync(CACHE_DIR))
|
|
89
|
-
fs_1.default.mkdirSync(CACHE_DIR, { recursive: true });
|
|
90
|
-
// โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
91
|
-
async function readJson(file, fallback) {
|
|
71
|
+
function getPidByPort(port) {
|
|
92
72
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
73
|
+
const result = require('child_process').execSync(`lsof -ti :${port} 2>/dev/null`, { encoding: 'utf8' }).trim();
|
|
74
|
+
return result || null;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return null;
|
|
97
78
|
}
|
|
98
|
-
catch { }
|
|
99
|
-
return fallback;
|
|
100
79
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
const tempFile = `${file}.${crypto_1.default.randomUUID ? crypto_1.default.randomUUID() : Math.random().toString(36).substring(2, 15)}.tmp`;
|
|
107
|
-
await fs_1.default.promises.writeFile(tempFile, JSON.stringify(data, null, 2));
|
|
108
|
-
await fs_1.default.promises.rename(tempFile, file);
|
|
80
|
+
function readJson(filePath, defaultVal) {
|
|
81
|
+
try {
|
|
82
|
+
if (fs_1.default.existsSync(filePath)) {
|
|
83
|
+
return JSON.parse(fs_1.default.readFileSync(filePath, 'utf8'));
|
|
109
84
|
}
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
writeQueues.set(file, nextChain);
|
|
114
|
-
return nextChain;
|
|
85
|
+
}
|
|
86
|
+
catch { }
|
|
87
|
+
return defaultVal;
|
|
115
88
|
}
|
|
116
|
-
function
|
|
117
|
-
|
|
89
|
+
function writeJson(filePath, data) {
|
|
90
|
+
try {
|
|
91
|
+
fs_1.default.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
92
|
+
}
|
|
93
|
+
catch { }
|
|
118
94
|
}
|
|
119
95
|
function getCollectionName(projectName) {
|
|
120
|
-
|
|
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}`;
|
|
96
|
+
return `lemma-cache-${projectName}`;
|
|
127
97
|
}
|
|
128
98
|
function detectProject() {
|
|
129
|
-
const
|
|
99
|
+
const cwd = process.cwd();
|
|
100
|
+
const pkgPath = path_1.default.join(cwd, 'package.json');
|
|
130
101
|
if (fs_1.default.existsSync(pkgPath)) {
|
|
131
102
|
try {
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
134
|
-
return
|
|
103
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
|
|
104
|
+
if (pkg.name)
|
|
105
|
+
return pkg.name;
|
|
135
106
|
}
|
|
136
107
|
catch { }
|
|
137
108
|
}
|
|
138
|
-
return path_1.default.basename(
|
|
109
|
+
return path_1.default.basename(cwd);
|
|
139
110
|
}
|
|
140
|
-
function ensureProjectDir(
|
|
141
|
-
const
|
|
142
|
-
|
|
111
|
+
function ensureProjectDir(projectName) {
|
|
112
|
+
const hash = crypto_1.default.createHash('sha1').update(projectName).digest('hex').slice(0, 12);
|
|
113
|
+
const dir = path_1.default.join(CACHE_DIR, hash);
|
|
114
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
143
115
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
144
|
-
|
|
145
|
-
if (!fs_1.default.existsSync(marker))
|
|
146
|
-
fs_1.default.writeFileSync(marker, name);
|
|
116
|
+
}
|
|
147
117
|
return dir;
|
|
148
118
|
}
|
|
149
|
-
function
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return
|
|
156
|
-
|
|
157
|
-
else {
|
|
158
|
-
const out = execSync(`lsof -t -i :${port}`).toString().trim();
|
|
159
|
-
return out.split('\n')[0]; // Take first if multiple
|
|
160
|
-
}
|
|
119
|
+
function ensureGitIgnore() {
|
|
120
|
+
const gitignorePath = path_1.default.join(process.cwd(), '.gitignore');
|
|
121
|
+
const lemmaRules = '\n# Lemma AI Gateway\n.lemma/\n';
|
|
122
|
+
if (fs_1.default.existsSync(gitignorePath)) {
|
|
123
|
+
const content = fs_1.default.readFileSync(gitignorePath, 'utf8');
|
|
124
|
+
if (content.includes('.lemma/'))
|
|
125
|
+
return false;
|
|
126
|
+
fs_1.default.appendFileSync(gitignorePath, lemmaRules);
|
|
161
127
|
}
|
|
162
|
-
|
|
163
|
-
|
|
128
|
+
else {
|
|
129
|
+
fs_1.default.writeFileSync(gitignorePath, lemmaRules);
|
|
164
130
|
}
|
|
131
|
+
return true;
|
|
165
132
|
}
|
|
166
|
-
async function
|
|
133
|
+
async function ensureChromaRunning() {
|
|
167
134
|
try {
|
|
168
|
-
|
|
169
|
-
return res.status >= 200 && res.status < 500;
|
|
135
|
+
await chroma.heartbeat();
|
|
170
136
|
}
|
|
171
137
|
catch {
|
|
172
|
-
|
|
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 {
|
|
138
|
+
console.log('โ ๏ธ ChromaDB is not running. Starting it...');
|
|
188
139
|
const { spawn } = require('child_process');
|
|
189
|
-
|
|
190
|
-
fs_1.default.mkdirSync(chromaDataPath, { recursive: true });
|
|
191
|
-
}
|
|
192
|
-
const chromaProcess = spawn('chroma', ['run', '--path', chromaDataPath, '--port', chromaPort], {
|
|
140
|
+
const cp = spawn('chroma', ['run', '--path', path_1.default.join(CACHE_DIR, 'chroma_data')], {
|
|
193
141
|
stdio: 'ignore',
|
|
194
|
-
detached: true
|
|
142
|
+
detached: true,
|
|
195
143
|
});
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
}
|
|
144
|
+
cp.unref();
|
|
145
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
236
146
|
}
|
|
237
|
-
catch { }
|
|
238
|
-
return added;
|
|
239
147
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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).`);
|
|
148
|
+
function autoConfigureAll(project, opts) {
|
|
149
|
+
if (!opts.configure) {
|
|
150
|
+
console.log('โญ๏ธ Skipping auto-configuration (--no-configure)');
|
|
271
151
|
return;
|
|
272
152
|
}
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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}`);
|
|
153
|
+
const envFile = path_1.default.join(process.cwd(), '.env');
|
|
154
|
+
const lemmaConfig = `\n# Lemma AI Gateway Configuration\nOPENAI_BASE_URL=http://localhost:8081/v1\nLEMMA_PROJECT=${project}\n`;
|
|
155
|
+
if (fs_1.default.existsSync(envFile)) {
|
|
156
|
+
const content = fs_1.default.readFileSync(envFile, 'utf8');
|
|
157
|
+
if (!content.includes('OPENAI_BASE_URL')) {
|
|
158
|
+
fs_1.default.appendFileSync(envFile, lemmaConfig);
|
|
304
159
|
}
|
|
305
160
|
}
|
|
306
|
-
|
|
307
|
-
|
|
161
|
+
const mcpDir = path_1.default.join(process.cwd(), '.lemma', 'mcp');
|
|
162
|
+
if (!fs_1.default.existsSync(mcpDir)) {
|
|
163
|
+
fs_1.default.mkdirSync(mcpDir, { recursive: true });
|
|
308
164
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
-
}
|
|
165
|
+
console.log(`โ
Auto-configured Lemma for [${project}]`);
|
|
166
|
+
}
|
|
167
|
+
const EVENT_LOG = [];
|
|
168
|
+
const MAX_EVENTS = 100;
|
|
169
|
+
// MCP Status tracking
|
|
170
|
+
const MCP_STATUS = {
|
|
171
|
+
lastActivity: Date.now(),
|
|
172
|
+
toolCallCount: 0,
|
|
173
|
+
status: 'idle'
|
|
174
|
+
};
|
|
175
|
+
function logEvent(event) {
|
|
176
|
+
const ev = { id: Math.random().toString(36).slice(2, 11), timestamp: Date.now(), ...event };
|
|
177
|
+
EVENT_LOG.push(ev);
|
|
178
|
+
if (EVENT_LOG.length > MAX_EVENTS)
|
|
179
|
+
EVENT_LOG.shift();
|
|
180
|
+
// Track MCP activity
|
|
181
|
+
if (event.type === 'mcp_tool_call') {
|
|
182
|
+
MCP_STATUS.lastActivity = Date.now();
|
|
183
|
+
MCP_STATUS.toolCallCount++;
|
|
184
|
+
MCP_STATUS.status = 'active';
|
|
342
185
|
}
|
|
343
|
-
|
|
344
|
-
|
|
186
|
+
// Print a terminal savings summary every 10 events
|
|
187
|
+
if (EVENT_LOG.length > 0 && EVENT_LOG.length % 10 === 0) {
|
|
188
|
+
const snap = SavingsLedger_1.savingsLedger.getSnapshot();
|
|
189
|
+
if (snap.total.tokensSaved > 0) {
|
|
190
|
+
console.log('\n' + SavingsLedger_1.savingsLedger.getTerminalSummary() + '\n');
|
|
191
|
+
}
|
|
345
192
|
}
|
|
346
|
-
|
|
347
|
-
|
|
193
|
+
}
|
|
194
|
+
const LEMMA_RULES_BLOCK = `
|
|
195
|
+
๐ง Lemma MCP โ Autonomous Behaviors
|
|
196
|
+
|
|
197
|
+
I have Lemma MCP tools connected. I MUST use them proactively:
|
|
198
|
+
|
|
199
|
+
1. When starting work on a project, call \`get_project_onboarding\` or read \`lemma://project/context\` to understand the architecture.
|
|
200
|
+
2. Before answering technical questions, call \`search_memory\` for relevant past solutions.
|
|
201
|
+
3. When examining a file, call \`get_telepathic_hints\` for relevant context.
|
|
202
|
+
4. After solving a problem, call \`store_memory\` to persist the solution for future sessions.
|
|
203
|
+
5. Use \`read_workspace_file\`, \`list_workspace_dir\`, \`search_workspace\` instead of asking the user to read or navigate files.
|
|
204
|
+
6. Always call \`validate_patch_sandbox\` before writing patches.
|
|
205
|
+
7. Read \`lemma://runtime/context\` first when the user reports a bug.
|
|
206
|
+
`;
|
|
207
|
+
function injectIdeRules(projectName) {
|
|
208
|
+
const content = `\n# ${projectName} โ ${LEMMA_RULES_BLOCK.trim()}\n`;
|
|
209
|
+
const rulesTargets = [
|
|
210
|
+
{ name: 'Cursor', path: path_1.default.join(process.cwd(), '.cursorrules') },
|
|
211
|
+
{ name: 'Cursor (v2)', path: path_1.default.join(process.cwd(), '.cursor/rules/lemma.mdc') },
|
|
212
|
+
{ name: 'Windsurf', path: path_1.default.join(process.cwd(), '.windsurfrules') },
|
|
213
|
+
{ name: 'Cline', path: path_1.default.join(process.cwd(), '.clinerules') },
|
|
214
|
+
];
|
|
215
|
+
for (const target of rulesTargets) {
|
|
348
216
|
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
|
-
});
|
|
217
|
+
const dir = path_1.default.dirname(target.path);
|
|
218
|
+
if (target.name === 'Cursor (v2)') {
|
|
219
|
+
if (!fs_1.default.existsSync(dir))
|
|
220
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
380
221
|
}
|
|
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
|
-
});
|
|
222
|
+
else if (!fs_1.default.existsSync(dir)) {
|
|
223
|
+
continue;
|
|
394
224
|
}
|
|
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
|
-
}
|
|
225
|
+
if (fs_1.default.existsSync(target.path)) {
|
|
226
|
+
const existing = fs_1.default.readFileSync(target.path, 'utf8');
|
|
227
|
+
if (existing.includes('Lemma MCP'))
|
|
228
|
+
continue;
|
|
424
229
|
}
|
|
230
|
+
fs_1.default.writeFileSync(target.path, content, { flag: 'a' });
|
|
231
|
+
console.log(`โ
[${target.name}] Injected Lemma rules: ${target.path}`);
|
|
425
232
|
}
|
|
426
|
-
catch
|
|
427
|
-
console.log(`โ ๏ธ Could not
|
|
233
|
+
catch {
|
|
234
|
+
console.log(`โ ๏ธ Could not inject rules for ${target.name}`);
|
|
428
235
|
}
|
|
429
236
|
}
|
|
430
|
-
else {
|
|
431
|
-
console.log(`โญ๏ธ [Editor Settings] Skip auto-configuration.`);
|
|
432
|
-
}
|
|
433
237
|
}
|
|
434
238
|
function startBackgroundClipboardWatcher(cliOpts) {
|
|
435
239
|
// Load configuration if it exists
|
|
@@ -958,6 +762,8 @@ class LemmaServer {
|
|
|
958
762
|
this.sessions = new Map();
|
|
959
763
|
this.timeline = [];
|
|
960
764
|
this.lastFileContents = new Map();
|
|
765
|
+
this.registry = new ProjectRegistry_1.ProjectRegistry();
|
|
766
|
+
this.projectSessions = new Map();
|
|
961
767
|
this.port = port;
|
|
962
768
|
this.projectName = projectName || detectProject();
|
|
963
769
|
this.projectDir = ensureProjectDir(this.projectName);
|
|
@@ -1003,6 +809,18 @@ class LemmaServer {
|
|
|
1003
809
|
uptime: Math.floor(process.uptime())
|
|
1004
810
|
});
|
|
1005
811
|
});
|
|
812
|
+
// Project registration from MCP handshake
|
|
813
|
+
this.app.post('/api/handshake', (req, res) => {
|
|
814
|
+
const { projectPath, projectName, stack } = req.body;
|
|
815
|
+
if (projectPath && projectName) {
|
|
816
|
+
this.registry.register(projectPath, projectName, stack || '');
|
|
817
|
+
console.log(`๐ [Handshake] Project registered: "${projectName}" (${projectPath})`);
|
|
818
|
+
res.json({ ok: true });
|
|
819
|
+
}
|
|
820
|
+
else {
|
|
821
|
+
res.status(400).json({ error: 'projectPath and projectName required' });
|
|
822
|
+
}
|
|
823
|
+
});
|
|
1006
824
|
this.app.get('/v1/models', (req, res) => {
|
|
1007
825
|
res.json({ object: 'list', data: [
|
|
1008
826
|
{ id: 'gpt-4o', object: 'model', owned_by: 'lemma-proxy' },
|
|
@@ -1271,7 +1089,7 @@ class LemmaServer {
|
|
|
1271
1089
|
totalTokensSaved: 0,
|
|
1272
1090
|
providers: {}
|
|
1273
1091
|
};
|
|
1274
|
-
writeJson(STATS_FILE, this.stats)
|
|
1092
|
+
writeJson(STATS_FILE, this.stats);
|
|
1275
1093
|
}
|
|
1276
1094
|
// Clear Chroma DB collection
|
|
1277
1095
|
try {
|
|
@@ -1495,7 +1313,7 @@ class LemmaServer {
|
|
|
1495
1313
|
s.providers['mcp'] = { hits: 0, misses: 0 };
|
|
1496
1314
|
source === 'cache' ? s.providers['mcp'].hits++ : s.providers['mcp'].misses++;
|
|
1497
1315
|
// Write updated stats back to disk immediately
|
|
1498
|
-
writeJson(STATS_FILE, this.stats)
|
|
1316
|
+
writeJson(STATS_FILE, this.stats);
|
|
1499
1317
|
// Push to live telemetry logEvent array so the Dashboard event timeline updates instantly!
|
|
1500
1318
|
logEvent({
|
|
1501
1319
|
type: source === 'cache' ? 'cache:hit' : source,
|
|
@@ -1636,6 +1454,15 @@ class LemmaServer {
|
|
|
1636
1454
|
}
|
|
1637
1455
|
console.log(`๐ \x1b[35m[Multiverse]\x1b[0m Captured micro-snapshot ${snapshot.id} (${changedFiles.length} file edits mapped).`);
|
|
1638
1456
|
}
|
|
1457
|
+
detectActiveProject(req) {
|
|
1458
|
+
const mcpActive = this.registry.getActive();
|
|
1459
|
+
if (mcpActive)
|
|
1460
|
+
return mcpActive;
|
|
1461
|
+
const sniffed = (0, ContextInjector_1.sniffProjectFromMessages)(req.body.messages || [], this.registry);
|
|
1462
|
+
if (sniffed)
|
|
1463
|
+
return sniffed;
|
|
1464
|
+
return null;
|
|
1465
|
+
}
|
|
1639
1466
|
async handleCompletion(req, res, provider) {
|
|
1640
1467
|
const t0 = Date.now();
|
|
1641
1468
|
const pro = await isPro();
|
|
@@ -1756,6 +1583,24 @@ Adjusted Answer:`;
|
|
|
1756
1583
|
return res.json(unmaskedData);
|
|
1757
1584
|
}
|
|
1758
1585
|
}
|
|
1586
|
+
// โโ 3. Context Injection (for cache misses) โโ
|
|
1587
|
+
const activeProject = this.detectActiveProject(req);
|
|
1588
|
+
if (activeProject) {
|
|
1589
|
+
const psKey = activeProject.path;
|
|
1590
|
+
if (!this.projectSessions.has(psKey)) {
|
|
1591
|
+
this.projectSessions.set(psKey, { alreadyInjected: false });
|
|
1592
|
+
}
|
|
1593
|
+
const session = this.projectSessions.get(psKey);
|
|
1594
|
+
const injectionLevel = (0, ContextInjector_1.shouldInject)(activeProject, req.body.messages, session);
|
|
1595
|
+
if (injectionLevel !== 'none') {
|
|
1596
|
+
const context = injectionLevel === 'deep'
|
|
1597
|
+
? await (0, ContextInjector_1.buildDeepContext)(activeProject, this.port)
|
|
1598
|
+
: (0, ContextInjector_1.buildMinimalContext)(activeProject);
|
|
1599
|
+
req.body.messages.unshift({ role: 'system', content: context });
|
|
1600
|
+
session.alreadyInjected = true;
|
|
1601
|
+
console.log(`๐ง [Context] Injected ${injectionLevel} context for "${activeProject.name}"`);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1759
1604
|
// โโ 2. Cache Miss Path โโ
|
|
1760
1605
|
const routingDecision = complexityRouter.evaluate(prompt, originalModel);
|
|
1761
1606
|
const model = routingDecision.model;
|
|
@@ -1996,9 +1841,46 @@ Adjusted Answer:`;
|
|
|
1996
1841
|
}
|
|
1997
1842
|
}
|
|
1998
1843
|
// โโ CLI โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1999
|
-
|
|
1844
|
+
function showWelcomeScreen() {
|
|
1845
|
+
console.log(`
|
|
1846
|
+
\x1b[1m\x1b[35m _ \x1b[0m
|
|
1847
|
+
\x1b[1m\x1b[35m | | ___ _ __ ___ __ _ \x1b[0m \x1b[1mLEMMA\x1b[0m \x1b[35mv${VERSION}\x1b[0m
|
|
1848
|
+
\x1b[1m\x1b[35m | | / _ \\ '_ \` _ \\ / _\` |\x1b[0m
|
|
1849
|
+
\x1b[1m\x1b[35m | |__| __/ | | | | | (_| |\x1b[0m Intelligent AI Gateway
|
|
1850
|
+
\x1b[1m\x1b[35m |_____\\___|_| |_| |_|\\__,_|\x1b[0m
|
|
1851
|
+
|
|
1852
|
+
\x1b[1mQuick Start\x1b[0m
|
|
1853
|
+
\x1b[33mlemma init\x1b[0m Initialize Lemma in this project
|
|
1854
|
+
\x1b[33mlemma start\x1b[0m Start the proxy server
|
|
1855
|
+
\x1b[33mlemma mcp\x1b[0m Start MCP Server for Cursor/Claude
|
|
1856
|
+
|
|
1857
|
+
\x1b[1mFree Features\x1b[0m (all included)
|
|
1858
|
+
๐ Privacy Firewall ๐พ Exact-match Cache
|
|
1859
|
+
๐ Complexity Router ๐ก Telepathic Clipboard
|
|
1860
|
+
๐ Basic Usage Stats ๐ง MCP Tools & Resources
|
|
1861
|
+
|
|
1862
|
+
\x1b[1mPro Features\x1b[0m (\x1b[35mlemma activate <key>\x1b[0m or \x1b[36mhttps://lemma.nxus.studio/upgrade\x1b[0m)
|
|
1863
|
+
๐ฏ Semantic Vector Cache ๐ Hive Mind Cloud Sync
|
|
1864
|
+
๐ธ Autopilot Healer ๐ฉบ Auto-Diagnose & Heal
|
|
1865
|
+
๐ Codebase Multiverse ๐๏ธ Architecture Onboarding
|
|
1866
|
+
|
|
1867
|
+
\x1b[90mRun \`lemma --help\` for all commands.\x1b[0m
|
|
1868
|
+
`);
|
|
1869
|
+
}
|
|
1870
|
+
commander_1.program
|
|
1871
|
+
.name('lemma')
|
|
1872
|
+
.description('Intelligent AI Gateway โ cache, route, and optimize AI requests')
|
|
1873
|
+
.version(VERSION)
|
|
1874
|
+
.addHelpText('after', `
|
|
1875
|
+
\x1b[1mPlan Comparison\x1b[0m
|
|
1876
|
+
\x1b[32mFree\x1b[0m 500 req/mo ยท Exact cache ยท Privacy firewall ยท MCP tools
|
|
1877
|
+
\x1b[35mPro\x1b[0m Unlimited ยท Semantic cache ยท Cloud sync ยท Autopilot ยท Heal
|
|
1878
|
+
\x1b[36mhttps://lemma.nxus.studio/upgrade\x1b[0m
|
|
1879
|
+
|
|
1880
|
+
\x1b[90mActivate Pro: lemma activate <key>\x1b[0m
|
|
1881
|
+
`);
|
|
2000
1882
|
commander_1.program.command('mcp')
|
|
2001
|
-
.description('Start the
|
|
1883
|
+
.description('Start the MCP Server for Cursor / Claude Desktop integration')
|
|
2002
1884
|
.action(() => {
|
|
2003
1885
|
const { spawn } = require('child_process');
|
|
2004
1886
|
// Robust search candidates for both development (TS source) and compiled (JS production) environments.
|
|
@@ -2053,6 +1935,7 @@ commander_1.program.command('start')
|
|
|
2053
1935
|
// Keep configurations up to date even if server was already started
|
|
2054
1936
|
const runningProject = resp.data.project || detectProject();
|
|
2055
1937
|
autoConfigureAll(runningProject, { configure: opts.configure });
|
|
1938
|
+
injectIdeRules(runningProject);
|
|
2056
1939
|
startBackgroundClipboardWatcher({ clipboard: opts.clipboard });
|
|
2057
1940
|
process.exit(0);
|
|
2058
1941
|
}
|
|
@@ -2060,6 +1943,7 @@ commander_1.program.command('start')
|
|
|
2060
1943
|
catch { }
|
|
2061
1944
|
const startProject = opts.project || detectProject();
|
|
2062
1945
|
autoConfigureAll(startProject, { configure: opts.configure });
|
|
1946
|
+
injectIdeRules(startProject);
|
|
2063
1947
|
startBackgroundClipboardWatcher({ clipboard: opts.clipboard });
|
|
2064
1948
|
// Automatically enable autopilot if stack is enabled and user is Pro
|
|
2065
1949
|
const runAutopilot = opts.autopilot || opts.stack;
|
|
@@ -2272,8 +2156,12 @@ commander_1.program.command('init')
|
|
|
2272
2156
|
}
|
|
2273
2157
|
console.log('\n๐ง \x1b[35mAuto-Setup MCP and redirection overrides for AI IDEs...\x1b[0m');
|
|
2274
2158
|
autoConfigureAll(project, { configure: opts.configure });
|
|
2275
|
-
|
|
2276
|
-
console.log('
|
|
2159
|
+
injectIdeRules(project);
|
|
2160
|
+
console.log('\nโจ Project initialized for the Agentic Era!\n');
|
|
2161
|
+
console.log('\x1b[1mNext steps:\x1b[0m');
|
|
2162
|
+
console.log(' \x1b[33mlemma start\x1b[0m Start the proxy & unlock AI savings');
|
|
2163
|
+
console.log(' \x1b[33mlemma upgrade\x1b[0m See Pro features & get a key');
|
|
2164
|
+
console.log(' \x1b[33mlemma --help\x1b[0m All available commands\n');
|
|
2277
2165
|
});
|
|
2278
2166
|
// โโ Auto-Healing Diagnostics (Zero-Cost Suite) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
2279
2167
|
async function performAutoHeal(apply) {
|
|
@@ -2443,7 +2331,7 @@ Ensure you return strictly JSON. Do not return any other text outside the JSON b
|
|
|
2443
2331
|
}
|
|
2444
2332
|
}
|
|
2445
2333
|
commander_1.program.command('heal')
|
|
2446
|
-
.description('Diagnose and auto-heal the latest local server crash from .lemma/live-context.md')
|
|
2334
|
+
.description('[Pro] Diagnose and auto-heal the latest local server crash from .lemma/live-context.md')
|
|
2447
2335
|
.option('--apply', 'Apply the auto-generated code fix automatically', false)
|
|
2448
2336
|
.action(async (opts) => {
|
|
2449
2337
|
try {
|
|
@@ -2472,7 +2360,7 @@ commander_1.program.command('heal')
|
|
|
2472
2360
|
}
|
|
2473
2361
|
});
|
|
2474
2362
|
commander_1.program.command('autopilot')
|
|
2475
|
-
.description('Start the autonomous background compiler watcher & active healer')
|
|
2363
|
+
.description('[Pro] Start the autonomous background compiler watcher & active healer')
|
|
2476
2364
|
.option('--dir <directory>', 'Directory to monitor for changes', 'src')
|
|
2477
2365
|
.action(async (opts) => {
|
|
2478
2366
|
try {
|
|
@@ -2497,6 +2385,26 @@ commander_1.program.command('autopilot')
|
|
|
2497
2385
|
process.exit(1);
|
|
2498
2386
|
}
|
|
2499
2387
|
});
|
|
2388
|
+
commander_1.program.command('upgrade')
|
|
2389
|
+
.description('Learn about Pro features and get an activation key')
|
|
2390
|
+
.action(() => {
|
|
2391
|
+
console.log(`
|
|
2392
|
+
\x1b[1m\x1b[35mLemma Pro\x1b[0m โ Unlimited AI Gateway power
|
|
2393
|
+
|
|
2394
|
+
\x1b[1mPro features:\x1b[0m
|
|
2395
|
+
๐ฏ \x1b[1mSemantic Vector Cache\x1b[0m โ Smarter cache hits via vector similarity
|
|
2396
|
+
๐ \x1b[1mHive Mind Cloud Sync\x1b[0m โ Cross-project cache & bug telepathy
|
|
2397
|
+
๐ธ \x1b[1mAutopilot Healer\x1b[0m โ Autonomous background compiler watcher
|
|
2398
|
+
๐ฉบ \x1b[1mAuto-Diagnose & Heal\x1b[0m โ AI-driven crash analysis & patch
|
|
2399
|
+
๐ \x1b[1mCodebase Multiverse\x1b[0m โ AST time-travel & snapshots
|
|
2400
|
+
๐๏ธ \x1b[1mArchitecture Onboarding\x1b[0m โ Zero-shot project understanding
|
|
2401
|
+
๐ \x1b[1mUnlimited requests\x1b[0m โ No monthly caps
|
|
2402
|
+
|
|
2403
|
+
\x1b[36m https://lemma.nxus.studio/upgrade\x1b[0m
|
|
2404
|
+
|
|
2405
|
+
Already have a key? Run: \x1b[33mlemma activate <key>\x1b[0m
|
|
2406
|
+
`);
|
|
2407
|
+
});
|
|
2500
2408
|
commander_1.program.command('clipboard')
|
|
2501
2409
|
.description('Start the Lemma Local Clipboard Telepathic Optimizer (auto-squeezes copied code blocks)')
|
|
2502
2410
|
.option('--interval <ms>', 'Polling interval in milliseconds', '1000')
|
|
@@ -2514,6 +2422,11 @@ commander_1.program.command('clipboard')
|
|
|
2514
2422
|
}
|
|
2515
2423
|
});
|
|
2516
2424
|
if (process.argv[1] && (process.argv[1].includes('lemma') || process.argv[1].endsWith('.cjs') || process.argv[1].endsWith('.js'))) {
|
|
2425
|
+
// Bare `lemma` with no args โ show welcome screen
|
|
2426
|
+
if (process.argv.length <= 2) {
|
|
2427
|
+
showWelcomeScreen();
|
|
2428
|
+
process.exit(0);
|
|
2429
|
+
}
|
|
2517
2430
|
commander_1.program.parse(process.argv);
|
|
2518
2431
|
}
|
|
2519
2432
|
//# sourceMappingURL=lemma-proxy.js.map
|