@nxuss/lemma 1.19.0 → 1.21.0
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/.agents/skills/lemma/SKILL.md +124 -0
- package/README.md +5 -1
- package/bin/init.js +109 -29
- package/bin/lemma-call.js +269 -0
- package/dist/cjs/mcp/index.js +4 -2
- package/dist/cjs/mcp/index.js.map +1 -1
- package/dist/cjs/mcp/prompts.d.ts +1 -1
- package/dist/cjs/mcp/prompts.d.ts.map +1 -1
- package/dist/cjs/mcp/prompts.js +1 -1
- package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/cjs/mcp/tool-profiles.js +9 -0
- package/dist/cjs/mcp/tool-profiles.js.map +1 -1
- package/dist/cjs/mcp/tools/analysis.d.ts.map +1 -1
- package/dist/cjs/mcp/tools/analysis.js +51 -20
- package/dist/cjs/mcp/tools/analysis.js.map +1 -1
- package/dist/cjs/mcp/tools/memory.d.ts +4 -4
- package/dist/cjs/mcp/tools/memory.d.ts.map +1 -1
- package/dist/cjs/mcp/tools/memory.js +217 -11
- package/dist/cjs/mcp/tools/memory.js.map +1 -1
- package/dist/cjs/mcp/tools.d.ts.map +1 -1
- package/dist/cjs/mcp/tools.js +4 -1
- package/dist/cjs/mcp/tools.js.map +1 -1
- package/dist/cjs/subconscious/TheBrainV2.d.ts +102 -1
- package/dist/cjs/subconscious/TheBrainV2.d.ts.map +1 -1
- package/dist/cjs/subconscious/TheBrainV2.js +255 -10
- package/dist/cjs/subconscious/TheBrainV2.js.map +1 -1
- package/dist/esm/mcp/index.js +4 -2
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/mcp/prompts.d.ts +1 -1
- package/dist/esm/mcp/prompts.d.ts.map +1 -1
- package/dist/esm/mcp/prompts.js +1 -1
- package/dist/esm/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/esm/mcp/tool-profiles.js +9 -0
- package/dist/esm/mcp/tool-profiles.js.map +1 -1
- package/dist/esm/mcp/tools/analysis.d.ts.map +1 -1
- package/dist/esm/mcp/tools/analysis.js +52 -21
- package/dist/esm/mcp/tools/analysis.js.map +1 -1
- package/dist/esm/mcp/tools/memory.d.ts +4 -4
- package/dist/esm/mcp/tools/memory.d.ts.map +1 -1
- package/dist/esm/mcp/tools/memory.js +217 -11
- package/dist/esm/mcp/tools/memory.js.map +1 -1
- package/dist/esm/mcp/tools.d.ts.map +1 -1
- package/dist/esm/mcp/tools.js +4 -1
- package/dist/esm/mcp/tools.js.map +1 -1
- package/dist/esm/subconscious/TheBrainV2.d.ts +102 -1
- package/dist/esm/subconscious/TheBrainV2.d.ts.map +1 -1
- package/dist/esm/subconscious/TheBrainV2.js +253 -10
- package/dist/esm/subconscious/TheBrainV2.js.map +1 -1
- package/lemma-proxy.cjs +12 -0
- package/package.json +11 -2
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lemma
|
|
3
|
+
description: Lemma — cache semántico, firewall de privacidad y memoria cross-sesión para Muse. Usa `lemma call <tool>` vía bash para acceder a 70+ tools sin MCP nativo.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Lemma para Muse
|
|
9
|
+
|
|
10
|
+
Eres Muse Code (muse-spark). Tienes **Lemma MCP** disponible — gateway de caché semántica, firewall de privacidad y memoria persistente. Muse no expone MCP nativamente, así que llamas a Lemma vía **CLI `lemma call`** (wrapper sobre `mcp-server.js` por stdio) + **proxy local**.
|
|
11
|
+
|
|
12
|
+
## Qué es Lemma
|
|
13
|
+
|
|
14
|
+
- **Proxy local** en `http://localhost:8081` — cachea respuestas LLM (BM25 + embeddings), ahorra tokens/dinero.
|
|
15
|
+
- **Brain persistente** en `~/.lemma-cache/lemma_brain.json` + memoria por proyecto.
|
|
16
|
+
- **MCP server** con 70+ tools — las mismas que usan Claude Code / Cursor / Codex, aquí vía `lemma call`.
|
|
17
|
+
|
|
18
|
+
> **Nota de compatibilidad:** Este skill NO afecta a Claude Code, Cursor, Codex ni otros CLIs con MCP nativo. Ellos siguen usando `mcp__lemma__*` directo. Las reglas globales de `AGENTS.md` (MANDATORY RULES) aplican a clientes con MCP nativo; si eres Muse, **este skill tiene prioridad** sobre `AGENTS.md` para decidir cuándo usar Lemma.
|
|
19
|
+
|
|
20
|
+
## Cuándo usar Lemma (Muse)
|
|
21
|
+
|
|
22
|
+
| Tarea | Qué hacer con Lemma |
|
|
23
|
+
|---|---|
|
|
24
|
+
| Pregunta que puede estar ya resuelta | `search_memory` antes de razonar |
|
|
25
|
+
| Pregunta atada a archivos concretos (mismo prompt + mismos archivos) | `state_hash_cache` con `action=lookup` antes, `store` después |
|
|
26
|
+
| Conocimiento que debe sobrevivir sesiones | `search_memory` / `store_memory` (con `derivedFrom`) |
|
|
27
|
+
| Leer/listar/buscar workspace | Usa tus tools nativos (`read_file`, `bash ls/grep`) — Lemma añade scrubbing si lo llamas, pero no es obligatorio |
|
|
28
|
+
| Tarea mecánica → decidir modelo barato | `get_routing_advice` |
|
|
29
|
+
| Aplicar patch | `validate_patch_sandbox` primero |
|
|
30
|
+
| "¿Cuánto ahorramos?" | `token_receipt` |
|
|
31
|
+
|
|
32
|
+
Si Lemma no encaja, usa tu tool nativo. No fuerces Lemma donde no aporta.
|
|
33
|
+
|
|
34
|
+
## Cómo llamar a Lemma desde Muse (preferido)
|
|
35
|
+
|
|
36
|
+
Usa el wrapper `lemma call` — evita craft de JSON-RPC manual y escapa args por ti. Está disponible como `node ./lemma-proxy.cjs call` o `node ./bin/lemma-call.js` o `npx @nxuss/lemma call` si instalaste global.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 1. Lookup en memoria antes de razonar (preferido — wrapper)
|
|
40
|
+
node ./lemma-proxy.cjs call search_memory --query "tu pregunta"
|
|
41
|
+
# o JSON explícito:
|
|
42
|
+
node ./lemma-proxy.cjs call search_memory '{"query":"tu pregunta"}'
|
|
43
|
+
|
|
44
|
+
# 2. Busca en workspace (con scrubbing)
|
|
45
|
+
node ./lemma-proxy.cjs call search_workspace --query "texto" --maxResults 10
|
|
46
|
+
|
|
47
|
+
# 3. Guarda solución verificada
|
|
48
|
+
node ./lemma-proxy.cjs call store_memory '{"content":"solución verificada","tags":["bugfix"]}'
|
|
49
|
+
|
|
50
|
+
# 4. Lista tools disponibles
|
|
51
|
+
node ./lemma-proxy.cjs call --list
|
|
52
|
+
|
|
53
|
+
# Pipe también funciona:
|
|
54
|
+
echo '{"query":"hello"}' | node ./bin/lemma-call.js search_memory
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Alternativa raw (solo si el wrapper no está disponible):**
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_memory","arguments":{"query":"tu pregunta"}}}' | node ./mcp-server.js
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Para tráfico LLM real (cache del proxy), asegúrate que el daemon corre:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
node ./lemma-proxy.cjs status
|
|
67
|
+
node ./lemma-proxy.cjs start
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Cero config en este repo
|
|
71
|
+
|
|
72
|
+
Si clonaste `lemma` y lo abriste con Muse, **no necesitas correr ningún comando**. Este repo ya trae:
|
|
73
|
+
- `.agents/skills/lemma/SKILL.md` (este archivo) — Muse lo detecta al abrir el workspace
|
|
74
|
+
- `AGENTS.md` con guard clause para Muse
|
|
75
|
+
- `bin/lemma-call.js` + `lemma-proxy.cjs call` (wrapper sin JSON manual)
|
|
76
|
+
- `mcp-server.js` como fallback sin build
|
|
77
|
+
|
|
78
|
+
Muse te pedirá confiar el workspace la primera vez (prompt interactivo). Una vez que aceptas, el skill queda activo automáticamente — sin `lemma init`, sin `npm run build` manual. Para uso total (70+ tools) el wrapper resuelve `dist/cjs/mcp/index.js` → `mcp-server.js`; si `dist` no existe se auto-construye o usa el fallback con las tools core.
|
|
79
|
+
|
|
80
|
+
## Sandbox y permisos
|
|
81
|
+
|
|
82
|
+
Muse corre con sandbox por workspace. El proxy (`localhost:8081`) y el brain (`~/.lemma-cache/`) están **fuera** del workspace, así que:
|
|
83
|
+
|
|
84
|
+
- La primera vez Muse pregunta si confías el workspace — di que sí. Después queda recordado, no necesitas `--trust-workspace` manual cada vez (solo si lo abres con flag explícito).
|
|
85
|
+
- Si ves `permission denied`, acepta el trust prompt y re-intenta; no necesitas `--disable-sandbox`.
|
|
86
|
+
- El wrapper prioriza `dist/cjs/mcp/index.js` y cae a `mcp-server.js` si aún no hay build — cero pasos extra.
|
|
87
|
+
|
|
88
|
+
## Flujo obligatorio al iniciar sesión
|
|
89
|
+
|
|
90
|
+
1. Llama `search_memory` vía `lemma call` con el tema de la tarea.
|
|
91
|
+
2. Si hay hit, verifícalo contra el código actual antes de presentarlo.
|
|
92
|
+
3. Al terminar con solución verificada (tests pasaron), llama `store_memory` vía `lemma call`.
|
|
93
|
+
|
|
94
|
+
## Flujo para bugs
|
|
95
|
+
|
|
96
|
+
1. `get_telepathic_hints` vía `lemma call` si necesitas contexto de runtime.
|
|
97
|
+
2. `search_memory` con el mensaje de error.
|
|
98
|
+
|
|
99
|
+
## Regla dura
|
|
100
|
+
|
|
101
|
+
Si tu respuesta viene de un cache hit, dilo explícitamente: "esto viene de memoria cacheada". Nunca presentes un hit como razonamiento fresco. Y nunca guardes una solución no verificada.
|
|
102
|
+
|
|
103
|
+
## Verificación (opcional)
|
|
104
|
+
|
|
105
|
+
En este repo no necesitas instalar nada — ya está listo. Solo si quieres verificar:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Wrapper responde (sin JSON manual)
|
|
109
|
+
node ./lemma-proxy.cjs call --list | head -c 500
|
|
110
|
+
node ./lemma-proxy.cjs call get_project_onboarding '{}' | head -c 800
|
|
111
|
+
|
|
112
|
+
# Proxy (solo si usas cache de tráfico LLM)
|
|
113
|
+
node ./lemma-proxy.cjs status
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Para **otros proyectos** (no este repo), instala Lemma una vez:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx @nxuss/lemma init # genera .agents/skills/lemma/SKILL.md + AGENTS.md en ese proyecto
|
|
120
|
+
# o
|
|
121
|
+
npm i -D @nxuss/lemma && npx lemma init
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
> Generado por `lemma init` — @nxuss/lemma para Muse.
|
package/README.md
CHANGED
|
@@ -27,10 +27,12 @@ to an auditable ledger, never an estimate.
|
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
npm install -g @nxuss/lemma
|
|
30
|
-
lemma init # auto-configures MCP for your IDE
|
|
30
|
+
lemma init # auto-configures MCP for your IDE (Claude Code, Cursor, Codex, Windsurf, Kiro, OpenCode)
|
|
31
31
|
lemma start # gateway + dashboard on http://localhost:8081
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
> **Muse** — no setup needed. This repo is already Muse-compatible zero-config: clone and open with `muse` — the skill at `.agents/skills/lemma/SKILL.md` auto-loads and `lemma call` works via `bash`. See [MCP integration](#-mcp-integration).
|
|
35
|
+
|
|
34
36
|
---
|
|
35
37
|
|
|
36
38
|
## ✨ What Lemma actually does
|
|
@@ -154,9 +156,11 @@ Customize via `lemma.config.json`:
|
|
|
154
156
|
|
|
155
157
|
| Client | Setup |
|
|
156
158
|
| :--- | :--- |
|
|
159
|
+
| **Muse** (`muse` CLI · Muse Spark) | **Zero-config** — clone this repo and open with `muse`; skill at `.agents/skills/lemma/SKILL.md` auto-loads, `lemma call` wrapper via `bash` (no MCP native needed). No `lemma init` required. |
|
|
157
160
|
| OpenCode | `lemma init` — registers itself in `~/.opencode.json`, zero config. |
|
|
158
161
|
| Cursor / Windsurf / Kiro / Antigravity | Settings → MCP → add server, command `lemma mcp`. |
|
|
159
162
|
| Claude Desktop | Add to `mcpServers`: `{ "lemma": { "command": "npx", "args": ["-y", "@nxuss/lemma", "mcp"] } }` |
|
|
163
|
+
| Claude Code / Codex CLI | `lemma init` — auto-configures MCP + sets gateway (`ANTHROPIC_BASE_URL`/`OPENAI_BASE_URL` → `http://localhost:8081`). |
|
|
160
164
|
|
|
161
165
|
Once connected, Lemma auto-injects its TurboMode system prompt on handshake — states the goal
|
|
162
166
|
(reduce what enters context without ever costing a correct answer) and enforces it: a tool call
|
package/bin/init.js
CHANGED
|
@@ -205,7 +205,7 @@ function configureClaudeDesktop() {
|
|
|
205
205
|
const FALLBACK_LEMMA_TOOL_NAMES = [
|
|
206
206
|
'read_workspace_file', 'write_workspace_file', 'create_workspace_file', 'list_workspace_dir',
|
|
207
207
|
'search_workspace', 'apply_workspace_patch', 'run_workspace_command', 'output_region',
|
|
208
|
-
'search_memory', 'store_memory', 'smarter_cache',
|
|
208
|
+
'search_memory', 'get_memory', 'store_memory', 'update_memory', 'smarter_cache',
|
|
209
209
|
'get_project_onboarding', 'get_telepathic_hints', 'get_ast_hologram',
|
|
210
210
|
'get_symbol_surgical_context', 'compress_context',
|
|
211
211
|
'squeeze_prompt', 'turbosqueeze', 'wormhole_squeeze',
|
|
@@ -282,11 +282,14 @@ tokens sale más cara que los tokens que ahorra.
|
|
|
282
282
|
| Agregar un método/función a un archivo existente | \`mcp__lemma__surgical_ast_insert\` | Patch quirúrgico en vez de reescribir el archivo completo |
|
|
283
283
|
| Antes de aplicar un patch generado | \`mcp__lemma__validate_patch_sandbox\` | Valídalo antes de escribirlo, no después |
|
|
284
284
|
| "¿Cuánto ahorramos de verdad?" | \`mcp__lemma__token_receipt\` | Bitácora auditable real, no una cifra inventada |
|
|
285
|
+
| Leer una memoria por su id (antes de editarla, o para un id que traes de otra sesión) | \`mcp__lemma__get_memory\` | Lectura determinista: \`search_memory\` es un ranking y puede no devolver la entrada que buscas. Editar un documento vivo sin leerlo antes es editarlo a ciegas |
|
|
286
|
+
| Corregir o extender una memoria que ya existe | \`mcp__lemma__update_memory\` | Reescribe en su sitio conservando id, hits y las aristas \`derivedFrom\`. \`store_memory\` siempre crea un id nuevo: re-guardar una versión corregida deja las dos y la búsqueda devuelve una mezcla arbitraria de ambas |
|
|
285
287
|
| Guardar una solución para el futuro | \`mcp__lemma__store_memory\` | Solo después de verificarla (tests pasaron, el usuario confirmó) — una solución mala guardada se vuelve un cache hit falso más adelante |
|
|
286
288
|
| Una memoria salió STALE pero la verificaste y sigue siendo correcta | \`mcp__lemma__refresh_memory\` | Re-ancla la evidencia conservando id, hits y las aristas \`derivedFrom\`; guardar un casi-duplicado pierde las tres (y el dedup suele rechazarlo) |
|
|
287
289
|
| Una memoria está simplemente mal, no solo mal encajada | \`mcp__lemma__forget_memory\` | Borrado real. Para "no era lo que buscaba en esta consulta" usa \`downvote_memory\`: esa memoria puede ser la correcta para otra pregunta |
|
|
288
290
|
|
|
289
|
-
|
|
291
|
+
El ciclo de vida completo (\`search\` → \`get\` → \`update\`/\`refresh\`/\`forget\`) está anunciado:
|
|
292
|
+
ante una memoria desactualizada, corrígela; no guardes otra al lado.
|
|
290
293
|
|
|
291
294
|
## No todas las tools de Lemma están en tu lista
|
|
292
295
|
|
|
@@ -427,9 +430,18 @@ function configureClaudeCodeGateway(anthropicApiKey) {
|
|
|
427
430
|
|
|
428
431
|
const LEMMA_RULES_CONTENT = `# Lemma MCP — Autonomous Behaviors
|
|
429
432
|
|
|
430
|
-
> ⚠️ MANDATORY RULES — Follow without exception
|
|
433
|
+
> ⚠️ MANDATORY RULES — Follow without exception **for MCP-native clients**.
|
|
431
434
|
|
|
432
|
-
The MCP server \`lemma\` is connected.
|
|
435
|
+
The MCP server \`lemma\` is connected. For clients with native MCP support
|
|
436
|
+
(Claude Code, Cursor, Codex, Windsurf, Kiro, OpenCode), NEVER use native tools
|
|
437
|
+
when a Lemma equivalent exists.
|
|
438
|
+
|
|
439
|
+
> **Muse Code:** you DO NOT have native \`mcp__lemma__*\` tools. For you,
|
|
440
|
+
> \`AGENTS.md\` is advisory. Follow \`.agents/skills/lemma/SKILL.md\` instead —
|
|
441
|
+
> it has priority and uses \`lemma call\` via bash. Do not enforce the table
|
|
442
|
+
> below as a hard block; use it only when \`lemma call\` actually helps.
|
|
443
|
+
|
|
444
|
+
## Mandatory Substitutions (MCP-native clients only)
|
|
433
445
|
|
|
434
446
|
## Mandatory Substitutions
|
|
435
447
|
|
|
@@ -452,6 +464,8 @@ The MCP server \`lemma\` is connected. NEVER use native tools when a Lemma equiv
|
|
|
452
464
|
2. Call \`search_memory\` with the task topic — recall past solutions
|
|
453
465
|
3. When done: call \`store_memory\` with the solution
|
|
454
466
|
|
|
467
|
+
> Muse: use \`node ./lemma-proxy.cjs call <tool>\` (see \`.agents/skills/lemma/SKILL.md\`).
|
|
468
|
+
|
|
455
469
|
## Bug Reporting
|
|
456
470
|
|
|
457
471
|
1. Call \`get_telepathic_hints\` with the relevant file
|
|
@@ -474,6 +488,17 @@ function updateAgentsMd() {
|
|
|
474
488
|
const existing = fs.readFileSync(agentsMdPath, 'utf8');
|
|
475
489
|
if (!existing.includes('@nxuss/lemma')) {
|
|
476
490
|
fs.appendFileSync(agentsMdPath, '\n---\n\n' + LEMMA_RULES_CONTENT, 'utf8');
|
|
491
|
+
} else if (!existing.includes('Muse Code:') || !existing.includes('MCP-native clients')) {
|
|
492
|
+
// Migrate old Lemma AGENTS.md (pre-Muse guard) to new version with Muse compatibility
|
|
493
|
+
// Replace the old block in-place to preserve any user content above it
|
|
494
|
+
const lemmaBlockRegex = /# Lemma MCP[\s\S]*?\*Auto-generated by `lemma init` — @nxuss\/lemma\*/;
|
|
495
|
+
if (lemmaBlockRegex.test(existing)) {
|
|
496
|
+
const updated = existing.replace(lemmaBlockRegex, LEMMA_RULES_CONTENT.trim());
|
|
497
|
+
fs.writeFileSync(agentsMdPath, updated, 'utf8');
|
|
498
|
+
} else {
|
|
499
|
+
// Fallback: append new block
|
|
500
|
+
fs.appendFileSync(agentsMdPath, '\n---\n\n' + LEMMA_RULES_CONTENT, 'utf8');
|
|
501
|
+
}
|
|
477
502
|
}
|
|
478
503
|
} else {
|
|
479
504
|
fs.writeFileSync(agentsMdPath, LEMMA_RULES_CONTENT, 'utf8');
|
|
@@ -546,21 +571,25 @@ function configureKiro() {
|
|
|
546
571
|
return true;
|
|
547
572
|
}
|
|
548
573
|
|
|
549
|
-
// OpenCode: register MCP server in opencode.json
|
|
574
|
+
// OpenCode: register MCP server in opencode.json + Muse skill
|
|
550
575
|
const MUSE_SKILL_CONTENT = `---
|
|
551
576
|
name: lemma
|
|
552
|
-
description: Lemma — cache semántico, firewall de privacidad y memoria cross-sesión para Muse.
|
|
577
|
+
description: Lemma — cache semántico, firewall de privacidad y memoria cross-sesión para Muse. Usa lemma call <tool> vía bash para acceder a 70+ tools sin MCP nativo.
|
|
578
|
+
user-invocable: true
|
|
579
|
+
allowed-tools: bash
|
|
553
580
|
---
|
|
554
581
|
|
|
555
582
|
# Lemma para Muse
|
|
556
583
|
|
|
557
|
-
Eres Muse Code (muse-spark). Tienes
|
|
584
|
+
Eres Muse Code (muse-spark). Tienes **Lemma MCP** disponible — gateway de caché semántica, firewall de privacidad y memoria persistente. Muse no expone MCP nativamente, así que llamas a Lemma vía **CLI \`lemma call\`** (wrapper sobre \`mcp-server.js\` por stdio) + **proxy local**.
|
|
558
585
|
|
|
559
586
|
## Qué es Lemma
|
|
560
587
|
|
|
561
588
|
- **Proxy local** en \`http://localhost:8081\` — cachea respuestas LLM (BM25 + embeddings), ahorra tokens/dinero.
|
|
562
589
|
- **Brain persistente** en \`~/.lemma-cache/lemma_brain.json\` + memoria por proyecto.
|
|
563
|
-
- **MCP server** con 70+ tools — las mismas que usan Claude Code / Cursor / Codex,
|
|
590
|
+
- **MCP server** con 70+ tools — las mismas que usan Claude Code / Cursor / Codex, aquí vía \`lemma call\`.
|
|
591
|
+
|
|
592
|
+
> **Nota de compatibilidad:** Este skill NO afecta a Claude Code, Cursor, Codex ni otros CLIs con MCP nativo. Ellos siguen usando \`mcp__lemma__*\` directo. Las reglas globales de \`AGENTS.md\` (MANDATORY RULES) aplican a clientes con MCP nativo; si eres Muse, **este skill tiene prioridad** sobre \`AGENTS.md\` para decidir cuándo usar Lemma.
|
|
564
593
|
|
|
565
594
|
## Cuándo usar Lemma (Muse)
|
|
566
595
|
|
|
@@ -576,61 +605,94 @@ Eres Muse Code (muse-spark). Tienes disponible **Lemma MCP** — gateway de cach
|
|
|
576
605
|
|
|
577
606
|
Si Lemma no encaja, usa tu tool nativo. No fuerces Lemma donde no aporta.
|
|
578
607
|
|
|
579
|
-
## Cómo llamar a Lemma desde Muse
|
|
608
|
+
## Cómo llamar a Lemma desde Muse (preferido)
|
|
580
609
|
|
|
581
|
-
|
|
610
|
+
Usa el wrapper \`lemma call\` — evita craft de JSON-RPC manual y escapa args por ti. Está disponible como \`node ./lemma-proxy.cjs call\` o \`node ./bin/lemma-call.js\` o \`npx @nxuss/lemma call\` si instalaste global.
|
|
582
611
|
|
|
583
612
|
\`\`\`bash
|
|
584
|
-
# 1. Lookup en memoria antes de razonar
|
|
585
|
-
|
|
613
|
+
# 1. Lookup en memoria antes de razonar (preferido — wrapper)
|
|
614
|
+
node ./lemma-proxy.cjs call search_memory --query "tu pregunta"
|
|
615
|
+
# o JSON explícito:
|
|
616
|
+
node ./lemma-proxy.cjs call search_memory '{"query":"tu pregunta"}'
|
|
586
617
|
|
|
587
|
-
# 2.
|
|
588
|
-
|
|
618
|
+
# 2. Busca en workspace (con scrubbing)
|
|
619
|
+
node ./lemma-proxy.cjs call search_workspace --query "texto" --maxResults 10
|
|
589
620
|
|
|
590
621
|
# 3. Guarda solución verificada
|
|
591
|
-
|
|
622
|
+
node ./lemma-proxy.cjs call store_memory '{"content":"solución verificada","tags":["bugfix"]}'
|
|
623
|
+
|
|
624
|
+
# 4. Lista tools disponibles
|
|
625
|
+
node ./lemma-proxy.cjs call --list
|
|
626
|
+
|
|
627
|
+
# Pipe también funciona:
|
|
628
|
+
echo '{"query":"hello"}' | node ./bin/lemma-call.js search_memory
|
|
592
629
|
\`\`\`
|
|
593
630
|
|
|
594
|
-
|
|
631
|
+
**Alternativa raw (solo si el wrapper no está disponible):**
|
|
595
632
|
|
|
596
633
|
\`\`\`bash
|
|
597
|
-
|
|
598
|
-
lemma search "query" # CLI directa si instalaste lemma global
|
|
634
|
+
echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_memory","arguments":{"query":"tu pregunta"}}}' | node ./mcp-server.js
|
|
599
635
|
\`\`\`
|
|
600
636
|
|
|
601
|
-
Para
|
|
637
|
+
Para tráfico LLM real (cache del proxy), asegúrate que el daemon corre:
|
|
602
638
|
|
|
603
639
|
\`\`\`bash
|
|
604
640
|
node ./lemma-proxy.cjs status
|
|
605
641
|
node ./lemma-proxy.cjs start
|
|
606
642
|
\`\`\`
|
|
607
643
|
|
|
644
|
+
## Cero config en este repo
|
|
645
|
+
|
|
646
|
+
Si clonaste \`lemma\` y lo abriste con Muse, **no necesitas correr ningún comando**. Este repo ya trae:
|
|
647
|
+
- \`.agents/skills/lemma/SKILL.md\` (este archivo) — Muse lo detecta al abrir el workspace
|
|
648
|
+
- \`AGENTS.md\` con guard clause para Muse
|
|
649
|
+
- \`bin/lemma-call.js\` + \`lemma-proxy.cjs call\` (wrapper sin JSON manual)
|
|
650
|
+
- \`mcp-server.js\` como fallback sin build
|
|
651
|
+
|
|
652
|
+
Muse te pedirá confiar el workspace la primera vez (prompt interactivo). Una vez que aceptas, el skill queda activo automáticamente — sin \`lemma init\`, sin \`npm run build\` manual. Para uso total (70+ tools) el wrapper resuelve \`dist/cjs/mcp/index.js\` → \`mcp-server.js\`; si \`dist\` no existe se auto-construye o usa el fallback con las tools core.
|
|
653
|
+
|
|
654
|
+
## Sandbox y permisos
|
|
655
|
+
|
|
656
|
+
Muse corre con sandbox por workspace. El proxy (\`localhost:8081\`) y el brain (\`~/.lemma-cache/\`) están **fuera** del workspace, así que:
|
|
657
|
+
|
|
658
|
+
- La primera vez Muse pregunta si confías el workspace — di que sí. Después queda recordado, no necesitas \`--trust-workspace\` manual cada vez (solo si lo abres con flag explícito).
|
|
659
|
+
- Si ves \`permission denied\`, acepta el trust prompt y re-intenta; no necesitas \`--disable-sandbox\`.
|
|
660
|
+
- El wrapper prioriza \`dist/cjs/mcp/index.js\` y cae a \`mcp-server.js\` si aún no hay build — cero pasos extra.
|
|
661
|
+
|
|
608
662
|
## Flujo obligatorio al iniciar sesión
|
|
609
663
|
|
|
610
|
-
1. Llama \`search_memory\` con el tema de la tarea.
|
|
664
|
+
1. Llama \`search_memory\` vía \`lemma call\` con el tema de la tarea.
|
|
611
665
|
2. Si hay hit, verifícalo contra el código actual antes de presentarlo.
|
|
612
|
-
3. Al terminar con solución verificada (tests pasaron), llama \`store_memory\`.
|
|
666
|
+
3. Al terminar con solución verificada (tests pasaron), llama \`store_memory\` vía \`lemma call\`.
|
|
613
667
|
|
|
614
668
|
## Flujo para bugs
|
|
615
669
|
|
|
616
|
-
1. \`get_telepathic_hints\` si necesitas contexto de runtime.
|
|
670
|
+
1. \`get_telepathic_hints\` vía \`lemma call\` si necesitas contexto de runtime.
|
|
617
671
|
2. \`search_memory\` con el mensaje de error.
|
|
618
672
|
|
|
619
673
|
## Regla dura
|
|
620
674
|
|
|
621
675
|
Si tu respuesta viene de un cache hit, dilo explícitamente: "esto viene de memoria cacheada". Nunca presentes un hit como razonamiento fresco. Y nunca guardes una solución no verificada.
|
|
622
676
|
|
|
623
|
-
##
|
|
677
|
+
## Verificación (opcional)
|
|
678
|
+
|
|
679
|
+
En este repo no necesitas instalar nada — ya está listo. Solo si quieres verificar:
|
|
624
680
|
|
|
625
681
|
\`\`\`bash
|
|
626
|
-
#
|
|
627
|
-
node ./
|
|
682
|
+
# Wrapper responde (sin JSON manual)
|
|
683
|
+
node ./lemma-proxy.cjs call --list | head -c 500
|
|
684
|
+
node ./lemma-proxy.cjs call get_project_onboarding '{}' | head -c 800
|
|
628
685
|
|
|
629
|
-
#
|
|
630
|
-
|
|
686
|
+
# Proxy (solo si usas cache de tráfico LLM)
|
|
687
|
+
node ./lemma-proxy.cjs status
|
|
688
|
+
\`\`\`
|
|
631
689
|
|
|
632
|
-
|
|
633
|
-
|
|
690
|
+
Para **otros proyectos** (no este repo), instala Lemma una vez:
|
|
691
|
+
|
|
692
|
+
\`\`\`bash
|
|
693
|
+
npx @nxuss/lemma init # genera .agents/skills/lemma/SKILL.md + AGENTS.md en ese proyecto
|
|
694
|
+
# o
|
|
695
|
+
npm i -D @nxuss/lemma && npx lemma init
|
|
634
696
|
\`\`\`
|
|
635
697
|
|
|
636
698
|
> Generado por \`lemma init\` — @nxuss/lemma para Muse.
|
|
@@ -647,6 +709,24 @@ function configureMuse() {
|
|
|
647
709
|
} catch { return false; }
|
|
648
710
|
}
|
|
649
711
|
|
|
712
|
+
function ensureKiroMigratesAgentsMd() {
|
|
713
|
+
// Kiro's configureKiro also touches AGENTS.md — ensure it migrates the same way
|
|
714
|
+
const cwd = process.cwd();
|
|
715
|
+
const agentsMdPath = path.join(cwd, 'AGENTS.md');
|
|
716
|
+
try {
|
|
717
|
+
if (fs.existsSync(agentsMdPath)) {
|
|
718
|
+
const existing = fs.readFileSync(agentsMdPath, 'utf8');
|
|
719
|
+
if (existing.includes('@nxuss/lemma') && (!existing.includes('Muse Code:') || !existing.includes('MCP-native clients'))) {
|
|
720
|
+
const lemmaBlockRegex = /# Lemma MCP[\s\S]*?\*Auto-generated by `lemma init` — @nxuss\/lemma\*/;
|
|
721
|
+
if (lemmaBlockRegex.test(existing)) {
|
|
722
|
+
const updated = existing.replace(lemmaBlockRegex, LEMMA_RULES_CONTENT.trim());
|
|
723
|
+
fs.writeFileSync(agentsMdPath, updated, 'utf8');
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
} catch {}
|
|
728
|
+
}
|
|
729
|
+
|
|
650
730
|
function configureOpenCode() {
|
|
651
731
|
const cwd = process.cwd();
|
|
652
732
|
const opencodePath = path.join(cwd, 'opencode.json');
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Lemma Call — one-shot MCP tool invocation for CLIs without native MCP (Muse, etc.)
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node bin/lemma-call.js <tool_name> [json_args]
|
|
9
|
+
* node bin/lemma-call.js search_memory '{"query":"hello"}'
|
|
10
|
+
* echo '{"query":"hello"}' | node bin/lemma-call.js search_memory
|
|
11
|
+
* node bin/lemma-call.js --list # lists available tools
|
|
12
|
+
*
|
|
13
|
+
* Also via npx:
|
|
14
|
+
* npx @nxuss/lemma call search_memory '{"query":"hello"}'
|
|
15
|
+
*
|
|
16
|
+
* Resolves MCP server path the same way as bin/init.js and mcp-server.js:
|
|
17
|
+
* 1. dist/cjs/mcp/index.js (built)
|
|
18
|
+
* 2. mcp-server.js (fallback/legacy)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const { spawn } = require('child_process');
|
|
24
|
+
|
|
25
|
+
function resolveMcpServer() {
|
|
26
|
+
const candidates = [
|
|
27
|
+
path.join(__dirname, '..', 'dist', 'cjs', 'mcp', 'index.js'),
|
|
28
|
+
path.join(__dirname, '..', 'mcp-server.js'),
|
|
29
|
+
path.join(__dirname, 'mcp-server.js'),
|
|
30
|
+
];
|
|
31
|
+
for (const p of candidates) {
|
|
32
|
+
if (fs.existsSync(p)) return p;
|
|
33
|
+
}
|
|
34
|
+
// Zero-config: try to auto-build dist if missing and tsc is available (fresh clone without build)
|
|
35
|
+
try {
|
|
36
|
+
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
37
|
+
if (fs.existsSync(pkgPath)) {
|
|
38
|
+
const { execSync } = require('child_process');
|
|
39
|
+
// Only attempt if typescript is installed (npm install was run)
|
|
40
|
+
execSync('npx tsc --project tsconfig.cjs.json --outDir ./dist/cjs 2>&1', { cwd: path.join(__dirname, '..'), stdio: 'ignore', timeout: 30000 });
|
|
41
|
+
const built = path.join(__dirname, '..', 'dist', 'cjs', 'mcp', 'index.js');
|
|
42
|
+
if (fs.existsSync(built)) return built;
|
|
43
|
+
}
|
|
44
|
+
} catch {}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseArgs(argv) {
|
|
49
|
+
const args = argv.slice(2);
|
|
50
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
51
|
+
return { help: true };
|
|
52
|
+
}
|
|
53
|
+
if (args.includes('--list') || args.includes('--tools')) {
|
|
54
|
+
return { list: true };
|
|
55
|
+
}
|
|
56
|
+
// tool name is first non-flag arg
|
|
57
|
+
let tool = null;
|
|
58
|
+
let jsonArg = null;
|
|
59
|
+
const flags = {};
|
|
60
|
+
for (let i = 0; i < args.length; i++) {
|
|
61
|
+
const a = args[i];
|
|
62
|
+
if (a.startsWith('--')) {
|
|
63
|
+
const key = a.slice(2);
|
|
64
|
+
const next = args[i + 1];
|
|
65
|
+
if (next && !next.startsWith('--')) {
|
|
66
|
+
flags[key] = next;
|
|
67
|
+
i++;
|
|
68
|
+
} else {
|
|
69
|
+
flags[key] = true;
|
|
70
|
+
}
|
|
71
|
+
} else if (!tool) {
|
|
72
|
+
tool = a;
|
|
73
|
+
} else if (!jsonArg) {
|
|
74
|
+
jsonArg = a;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Flags like --query "hello" should become jsonArg object
|
|
78
|
+
let jsonObj = null;
|
|
79
|
+
if (jsonArg) {
|
|
80
|
+
try { jsonObj = JSON.parse(jsonArg); } catch (e) {
|
|
81
|
+
console.error(`[lemma-call] Invalid JSON args: ${e.message}`);
|
|
82
|
+
process.exit(2);
|
|
83
|
+
}
|
|
84
|
+
} else if (Object.keys(flags).length > 0) {
|
|
85
|
+
jsonObj = flags;
|
|
86
|
+
// coerce common string flags
|
|
87
|
+
for (const k of Object.keys(jsonObj)) {
|
|
88
|
+
if (jsonObj[k] === 'true') jsonObj[k] = true;
|
|
89
|
+
if (jsonObj[k] === 'false') jsonObj[k] = false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { tool, jsonObj, flags };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function readStdin() {
|
|
96
|
+
return new Promise(resolve => {
|
|
97
|
+
if (process.stdin.isTTY) return resolve(null);
|
|
98
|
+
let data = '';
|
|
99
|
+
process.stdin.setEncoding('utf8');
|
|
100
|
+
process.stdin.on('data', chunk => data += chunk);
|
|
101
|
+
process.stdin.on('end', () => {
|
|
102
|
+
const t = data.trim();
|
|
103
|
+
if (!t) return resolve(null);
|
|
104
|
+
try { resolve(JSON.parse(t)); } catch { resolve(t); }
|
|
105
|
+
});
|
|
106
|
+
// If stdin never ends (no pipe), timeout
|
|
107
|
+
setTimeout(() => resolve(null), 100);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function main() {
|
|
112
|
+
const parsed = parseArgs(process.argv);
|
|
113
|
+
if (parsed.help) {
|
|
114
|
+
console.log(`lemma-call — one-shot Lemma MCP tool invocation
|
|
115
|
+
|
|
116
|
+
Usage:
|
|
117
|
+
node bin/lemma-call.js <tool> [json]
|
|
118
|
+
node bin/lemma-call.js search_memory '{"query":"hello"}'
|
|
119
|
+
node bin/lemma-call.js search_memory --query "hello"
|
|
120
|
+
echo '{"query":"hello"}' | node bin/lemma-call.js search_memory
|
|
121
|
+
node bin/lemma-call.js --list
|
|
122
|
+
npx @nxuss/lemma call <tool> [json] (via lemma-proxy.cjs)
|
|
123
|
+
`);
|
|
124
|
+
process.exit(0);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const mcpPath = resolveMcpServer();
|
|
128
|
+
if (!mcpPath) {
|
|
129
|
+
console.error('[lemma-call] MCP server not found. Run `npm run build` first.');
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// --list mode: proper MCP handshake (initialize → tools/list)
|
|
134
|
+
if (parsed.list) {
|
|
135
|
+
const initReq = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'lemma-call', version: '1.0.0' } } }) + '\n';
|
|
136
|
+
const initialized = JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized' }) + '\n';
|
|
137
|
+
const listReq = JSON.stringify({ jsonrpc: '2.0', id: 2, method: 'tools/list' }) + '\n';
|
|
138
|
+
const child = spawn(process.execPath, [mcpPath], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
139
|
+
let out = '';
|
|
140
|
+
child.stdout.on('data', d => out += d.toString());
|
|
141
|
+
child.stderr.on('data', () => {}); // ignore watcher noise
|
|
142
|
+
child.stdin.write(initReq);
|
|
143
|
+
child.stdin.write(initialized);
|
|
144
|
+
child.stdin.write(listReq);
|
|
145
|
+
child.stdin.end();
|
|
146
|
+
// Give server time to respond before close (MCP is stream-oriented)
|
|
147
|
+
await new Promise(r => setTimeout(r, 800));
|
|
148
|
+
try { child.kill(); } catch {}
|
|
149
|
+
await new Promise(r => child.on('close', r).on('error', r));
|
|
150
|
+
// Parse all JSON-RPC response lines, find the tools/list result (id:2)
|
|
151
|
+
const lines = out.trim().split('\n').filter(Boolean);
|
|
152
|
+
let tools = null;
|
|
153
|
+
for (const line of lines) {
|
|
154
|
+
try {
|
|
155
|
+
const j = JSON.parse(line);
|
|
156
|
+
if (j.id === 2 && j.result && Array.isArray(j.result.tools)) { tools = j.result.tools; break; }
|
|
157
|
+
if (j.result && Array.isArray(j.result.tools) && !tools) tools = j.result.tools;
|
|
158
|
+
} catch {}
|
|
159
|
+
}
|
|
160
|
+
if (tools) {
|
|
161
|
+
console.log(JSON.stringify(tools.map(t => t.name), null, 2));
|
|
162
|
+
} else {
|
|
163
|
+
// Fallback: try last line approach
|
|
164
|
+
try {
|
|
165
|
+
const res = JSON.parse(lines[lines.length - 1] || '{}');
|
|
166
|
+
const t = res.result?.tools || [];
|
|
167
|
+
if (t.length) console.log(JSON.stringify(t.map(x => x.name), null, 2));
|
|
168
|
+
else console.log(out.trim().split('\n').pop() || '[]');
|
|
169
|
+
} catch {
|
|
170
|
+
console.log(out.trim() || '[]');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!parsed.tool) {
|
|
177
|
+
console.error('[lemma-call] Missing tool name. Try --help');
|
|
178
|
+
process.exit(2);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let argsObj = parsed.jsonObj;
|
|
182
|
+
// If no args from CLI, try stdin
|
|
183
|
+
if (!argsObj) {
|
|
184
|
+
const stdinData = await readStdin();
|
|
185
|
+
if (stdinData && typeof stdinData === 'object') argsObj = stdinData;
|
|
186
|
+
else if (typeof stdinData === 'string' && stdinData) {
|
|
187
|
+
try { argsObj = JSON.parse(stdinData); } catch { argsObj = { query: stdinData }; }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (!argsObj) argsObj = {};
|
|
191
|
+
|
|
192
|
+
// MCP handshake: initialize → initialized → tools/call (required by SDK)
|
|
193
|
+
const initReq = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'lemma-call', version: '1.0.0' } } }) + '\n';
|
|
194
|
+
const initialized = JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized' }) + '\n';
|
|
195
|
+
const request = JSON.stringify({
|
|
196
|
+
jsonrpc: '2.0',
|
|
197
|
+
id: 2,
|
|
198
|
+
method: 'tools/call',
|
|
199
|
+
params: { name: parsed.tool, arguments: argsObj }
|
|
200
|
+
}) + '\n';
|
|
201
|
+
|
|
202
|
+
const child = spawn(process.execPath, [mcpPath], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
203
|
+
let stdout = '';
|
|
204
|
+
let stderr = '';
|
|
205
|
+
child.stdout.on('data', d => stdout += d.toString());
|
|
206
|
+
child.stderr.on('data', d => stderr += d.toString());
|
|
207
|
+
child.stdin.write(initReq);
|
|
208
|
+
child.stdin.write(initialized);
|
|
209
|
+
child.stdin.write(request);
|
|
210
|
+
// Keep stdin open briefly for server to process; MCP SDK buffers responses
|
|
211
|
+
await new Promise(r => setTimeout(r, 500));
|
|
212
|
+
child.stdin.end();
|
|
213
|
+
// Wait for response with timeout
|
|
214
|
+
const exitCode = await Promise.race([
|
|
215
|
+
new Promise(resolve => child.on('close', resolve)),
|
|
216
|
+
new Promise(resolve => setTimeout(() => { try { child.kill(); } catch {}; resolve(0); }, 5000))
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
// MCP server returns JSON-RPC with result.content[].text — find id:2 response
|
|
220
|
+
const lines = stdout.trim().split('\n').filter(Boolean);
|
|
221
|
+
let parsedRes = null;
|
|
222
|
+
for (const line of lines) {
|
|
223
|
+
try {
|
|
224
|
+
const j = JSON.parse(line);
|
|
225
|
+
if (j.id === 2) { parsedRes = j; break; }
|
|
226
|
+
} catch {}
|
|
227
|
+
}
|
|
228
|
+
if (!parsedRes) {
|
|
229
|
+
try { parsedRes = JSON.parse(lines[lines.length - 1] || ''); } catch {}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (parsedRes && parsedRes.result) {
|
|
233
|
+
// Pretty-print the tool result for Muse to read easily
|
|
234
|
+
const content = parsedRes.result.content;
|
|
235
|
+
if (Array.isArray(content)) {
|
|
236
|
+
for (const c of content) {
|
|
237
|
+
if (c.text) {
|
|
238
|
+
// Try to pretty-print if text is JSON
|
|
239
|
+
try {
|
|
240
|
+
const j = JSON.parse(c.text);
|
|
241
|
+
console.log(JSON.stringify(j, null, 2));
|
|
242
|
+
} catch {
|
|
243
|
+
console.log(c.text);
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
console.log(JSON.stringify(c, null, 2));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
console.log(JSON.stringify(parsedRes.result, null, 2));
|
|
251
|
+
}
|
|
252
|
+
// Propagate isError as exit code
|
|
253
|
+
if (parsedRes.result.isError) process.exit(1);
|
|
254
|
+
process.exit(0);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (parsedRes && parsedRes.error) {
|
|
258
|
+
console.error(`[lemma-call] MCP error: ${parsedRes.error.message || JSON.stringify(parsedRes.error)}`);
|
|
259
|
+
if (stderr.trim()) console.error(stderr.trim());
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Fallback: dump raw output
|
|
264
|
+
if (stdout.trim()) console.log(stdout.trim());
|
|
265
|
+
if (stderr.trim()) console.error(stderr.trim());
|
|
266
|
+
process.exit(exitCode || 0);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
main().catch(e => { console.error('[lemma-call]', e.message); process.exit(1); });
|