@nxuss/lemma 1.40.0 → 1.41.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/README.md +45 -14
- package/bin/brain-ingest.js +41 -0
- package/bin/init.js +92 -92
- package/bin/postinstall.js +58 -4
- package/dashboard/dist/assets/index-Ca05LJvs.js +453 -0
- package/dashboard/dist/assets/{index-DNTQXLER.css → index-DdEVyDRj.css} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/cjs/cli/hostHooks.d.ts +14 -1
- package/dist/cjs/cli/hostHooks.d.ts.map +1 -1
- package/dist/cjs/cli/hostHooks.js +53 -6
- package/dist/cjs/cli/hostHooks.js.map +1 -1
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +29 -10
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/contracts/evidence.d.ts +8 -1
- package/dist/cjs/contracts/evidence.d.ts.map +1 -1
- package/dist/cjs/mcp/index.js +19 -0
- 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 +6 -16
- package/dist/cjs/mcp/prompts.js.map +1 -1
- package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/cjs/mcp/tool-profiles.js +8 -4
- package/dist/cjs/mcp/tool-profiles.js.map +1 -1
- package/dist/cjs/mcp/tools/context-tools.d.ts.map +1 -1
- package/dist/cjs/mcp/tools/context-tools.js +21 -17
- package/dist/cjs/mcp/tools/context-tools.js.map +1 -1
- package/dist/cjs/mcp/tools/memory.d.ts.map +1 -1
- package/dist/cjs/mcp/tools/memory.js +25 -4
- package/dist/cjs/mcp/tools/memory.js.map +1 -1
- package/dist/cjs/mcp/tools/workspace.d.ts.map +1 -1
- package/dist/cjs/mcp/tools/workspace.js +97 -54
- package/dist/cjs/mcp/tools/workspace.js.map +1 -1
- package/dist/cjs/mcp/tools.d.ts.map +1 -1
- package/dist/cjs/mcp/tools.js +95 -34
- package/dist/cjs/mcp/tools.js.map +1 -1
- package/dist/cjs/mcp/utils.d.ts.map +1 -1
- package/dist/cjs/mcp/utils.js +6 -1
- package/dist/cjs/mcp/utils.js.map +1 -1
- package/dist/cjs/mcp/workspaceShared.d.ts.map +1 -1
- package/dist/cjs/mcp/workspaceShared.js +14 -9
- package/dist/cjs/mcp/workspaceShared.js.map +1 -1
- package/dist/cjs/proxy/KeyManager.d.ts +10 -0
- package/dist/cjs/proxy/KeyManager.d.ts.map +1 -1
- package/dist/cjs/proxy/KeyManager.js +45 -8
- package/dist/cjs/proxy/KeyManager.js.map +1 -1
- package/dist/cjs/security/SemanticScrubber.d.ts.map +1 -1
- package/dist/cjs/security/SemanticScrubber.js +19 -1
- package/dist/cjs/security/SemanticScrubber.js.map +1 -1
- package/dist/cjs/subconscious/HostRecall.d.ts +7 -5
- package/dist/cjs/subconscious/HostRecall.d.ts.map +1 -1
- package/dist/cjs/subconscious/HostRecall.js +115 -13
- package/dist/cjs/subconscious/HostRecall.js.map +1 -1
- package/dist/cjs/subconscious/TheBrainV2.d.ts +6 -1
- package/dist/cjs/subconscious/TheBrainV2.d.ts.map +1 -1
- package/dist/cjs/subconscious/TheBrainV2.js +21 -9
- package/dist/cjs/subconscious/TheBrainV2.js.map +1 -1
- package/dist/cjs/subconscious/freshness/LocalFsResolver.d.ts +3 -0
- package/dist/cjs/subconscious/freshness/LocalFsResolver.d.ts.map +1 -1
- package/dist/cjs/subconscious/freshness/LocalFsResolver.js +32 -14
- package/dist/cjs/subconscious/freshness/LocalFsResolver.js.map +1 -1
- package/dist/cjs/utils/CommandOutputDistiller.d.ts +10 -0
- package/dist/cjs/utils/CommandOutputDistiller.d.ts.map +1 -1
- package/dist/cjs/utils/CommandOutputDistiller.js +40 -2
- package/dist/cjs/utils/CommandOutputDistiller.js.map +1 -1
- package/dist/cjs/utils/ContextSqueezer.d.ts +13 -1
- package/dist/cjs/utils/ContextSqueezer.d.ts.map +1 -1
- package/dist/cjs/utils/ContextSqueezer.js +176 -3
- package/dist/cjs/utils/ContextSqueezer.js.map +1 -1
- package/dist/cjs/utils/FixCapture.d.ts.map +1 -1
- package/dist/cjs/utils/FixCapture.js +56 -0
- package/dist/cjs/utils/FixCapture.js.map +1 -1
- package/dist/cjs/utils/LineNumberMap.d.ts.map +1 -1
- package/dist/cjs/utils/LineNumberMap.js +27 -0
- package/dist/cjs/utils/LineNumberMap.js.map +1 -1
- package/dist/cjs/utils/PatchMatcher.d.ts.map +1 -1
- package/dist/cjs/utils/PatchMatcher.js +26 -0
- package/dist/cjs/utils/PatchMatcher.js.map +1 -1
- package/dist/cjs/utils/ProLicense.d.ts +2 -0
- package/dist/cjs/utils/ProLicense.d.ts.map +1 -0
- package/dist/cjs/utils/ProLicense.js +31 -0
- package/dist/cjs/utils/ProLicense.js.map +1 -0
- package/dist/cjs/utils/RevivalThrottle.d.ts +9 -0
- package/dist/cjs/utils/RevivalThrottle.d.ts.map +1 -0
- package/dist/cjs/utils/RevivalThrottle.js +25 -0
- package/dist/cjs/utils/RevivalThrottle.js.map +1 -0
- package/dist/cjs/utils/SavingsLedger.d.ts +58 -18
- package/dist/cjs/utils/SavingsLedger.d.ts.map +1 -1
- package/dist/cjs/utils/SavingsLedger.js +332 -179
- package/dist/cjs/utils/SavingsLedger.js.map +1 -1
- package/dist/cjs/utils/SearchCache.d.ts +2 -2
- package/dist/cjs/utils/SearchCache.d.ts.map +1 -1
- package/dist/cjs/utils/SearchCache.js +8 -6
- package/dist/cjs/utils/SearchCache.js.map +1 -1
- package/dist/cjs/utils/SessionCapture.d.ts +65 -0
- package/dist/cjs/utils/SessionCapture.d.ts.map +1 -0
- package/dist/cjs/utils/SessionCapture.js +339 -0
- package/dist/cjs/utils/SessionCapture.js.map +1 -0
- package/dist/cjs/utils/StaleDelta.d.ts +2 -0
- package/dist/cjs/utils/StaleDelta.d.ts.map +1 -0
- package/dist/cjs/utils/StaleDelta.js +60 -0
- package/dist/cjs/utils/StaleDelta.js.map +1 -0
- package/dist/cjs/utils/TokenBudgetedReader.d.ts.map +1 -1
- package/dist/cjs/utils/TokenBudgetedReader.js +56 -15
- package/dist/cjs/utils/TokenBudgetedReader.js.map +1 -1
- package/dist/cjs/utils/WorkspaceSearch.d.ts +8 -1
- package/dist/cjs/utils/WorkspaceSearch.d.ts.map +1 -1
- package/dist/cjs/utils/WorkspaceSearch.js +75 -5
- package/dist/cjs/utils/WorkspaceSearch.js.map +1 -1
- package/dist/cjs/utils/reportSavings.d.ts +3 -0
- package/dist/cjs/utils/reportSavings.d.ts.map +1 -1
- package/dist/cjs/utils/reportSavings.js +44 -5
- package/dist/cjs/utils/reportSavings.js.map +1 -1
- package/dist/esm/cli/hostHooks.d.ts +14 -1
- package/dist/esm/cli/hostHooks.d.ts.map +1 -1
- package/dist/esm/cli/hostHooks.js +52 -5
- package/dist/esm/cli/hostHooks.js.map +1 -1
- package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +30 -11
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/contracts/evidence.d.ts +8 -1
- package/dist/esm/contracts/evidence.d.ts.map +1 -1
- package/dist/esm/mcp/index.js +20 -1
- 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 +6 -16
- package/dist/esm/mcp/prompts.js.map +1 -1
- package/dist/esm/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/esm/mcp/tool-profiles.js +8 -4
- package/dist/esm/mcp/tool-profiles.js.map +1 -1
- package/dist/esm/mcp/tools/context-tools.d.ts.map +1 -1
- package/dist/esm/mcp/tools/context-tools.js +21 -17
- package/dist/esm/mcp/tools/context-tools.js.map +1 -1
- package/dist/esm/mcp/tools/memory.d.ts.map +1 -1
- package/dist/esm/mcp/tools/memory.js +26 -5
- package/dist/esm/mcp/tools/memory.js.map +1 -1
- package/dist/esm/mcp/tools/workspace.d.ts.map +1 -1
- package/dist/esm/mcp/tools/workspace.js +98 -55
- package/dist/esm/mcp/tools/workspace.js.map +1 -1
- package/dist/esm/mcp/tools.d.ts.map +1 -1
- package/dist/esm/mcp/tools.js +94 -33
- package/dist/esm/mcp/tools.js.map +1 -1
- package/dist/esm/mcp/utils.d.ts.map +1 -1
- package/dist/esm/mcp/utils.js +6 -1
- package/dist/esm/mcp/utils.js.map +1 -1
- package/dist/esm/mcp/workspaceShared.d.ts.map +1 -1
- package/dist/esm/mcp/workspaceShared.js +14 -9
- package/dist/esm/mcp/workspaceShared.js.map +1 -1
- package/dist/esm/proxy/KeyManager.d.ts +10 -0
- package/dist/esm/proxy/KeyManager.d.ts.map +1 -1
- package/dist/esm/proxy/KeyManager.js +43 -6
- package/dist/esm/proxy/KeyManager.js.map +1 -1
- package/dist/esm/security/SemanticScrubber.d.ts.map +1 -1
- package/dist/esm/security/SemanticScrubber.js +19 -1
- package/dist/esm/security/SemanticScrubber.js.map +1 -1
- package/dist/esm/subconscious/HostRecall.d.ts +7 -5
- package/dist/esm/subconscious/HostRecall.d.ts.map +1 -1
- package/dist/esm/subconscious/HostRecall.js +115 -14
- package/dist/esm/subconscious/HostRecall.js.map +1 -1
- package/dist/esm/subconscious/TheBrainV2.d.ts +6 -1
- package/dist/esm/subconscious/TheBrainV2.d.ts.map +1 -1
- package/dist/esm/subconscious/TheBrainV2.js +21 -9
- package/dist/esm/subconscious/TheBrainV2.js.map +1 -1
- package/dist/esm/subconscious/freshness/LocalFsResolver.d.ts +3 -0
- package/dist/esm/subconscious/freshness/LocalFsResolver.d.ts.map +1 -1
- package/dist/esm/subconscious/freshness/LocalFsResolver.js +32 -14
- package/dist/esm/subconscious/freshness/LocalFsResolver.js.map +1 -1
- package/dist/esm/utils/CommandOutputDistiller.d.ts +10 -0
- package/dist/esm/utils/CommandOutputDistiller.d.ts.map +1 -1
- package/dist/esm/utils/CommandOutputDistiller.js +39 -2
- package/dist/esm/utils/CommandOutputDistiller.js.map +1 -1
- package/dist/esm/utils/ContextSqueezer.d.ts +13 -1
- package/dist/esm/utils/ContextSqueezer.d.ts.map +1 -1
- package/dist/esm/utils/ContextSqueezer.js +175 -3
- package/dist/esm/utils/ContextSqueezer.js.map +1 -1
- package/dist/esm/utils/FixCapture.d.ts.map +1 -1
- package/dist/esm/utils/FixCapture.js +56 -0
- package/dist/esm/utils/FixCapture.js.map +1 -1
- package/dist/esm/utils/LineNumberMap.d.ts.map +1 -1
- package/dist/esm/utils/LineNumberMap.js +27 -0
- package/dist/esm/utils/LineNumberMap.js.map +1 -1
- package/dist/esm/utils/PatchMatcher.d.ts.map +1 -1
- package/dist/esm/utils/PatchMatcher.js +26 -0
- package/dist/esm/utils/PatchMatcher.js.map +1 -1
- package/dist/esm/utils/ProLicense.d.ts +2 -0
- package/dist/esm/utils/ProLicense.d.ts.map +1 -0
- package/dist/esm/utils/ProLicense.js +25 -0
- package/dist/esm/utils/ProLicense.js.map +1 -0
- package/dist/esm/utils/RevivalThrottle.d.ts +9 -0
- package/dist/esm/utils/RevivalThrottle.d.ts.map +1 -0
- package/dist/esm/utils/RevivalThrottle.js +22 -0
- package/dist/esm/utils/RevivalThrottle.js.map +1 -0
- package/dist/esm/utils/SavingsLedger.d.ts +58 -18
- package/dist/esm/utils/SavingsLedger.d.ts.map +1 -1
- package/dist/esm/utils/SavingsLedger.js +332 -179
- package/dist/esm/utils/SavingsLedger.js.map +1 -1
- package/dist/esm/utils/SearchCache.d.ts +2 -2
- package/dist/esm/utils/SearchCache.d.ts.map +1 -1
- package/dist/esm/utils/SearchCache.js +8 -6
- package/dist/esm/utils/SearchCache.js.map +1 -1
- package/dist/esm/utils/SessionCapture.d.ts +65 -0
- package/dist/esm/utils/SessionCapture.d.ts.map +1 -0
- package/dist/esm/utils/SessionCapture.js +328 -0
- package/dist/esm/utils/SessionCapture.js.map +1 -0
- package/dist/esm/utils/StaleDelta.d.ts +2 -0
- package/dist/esm/utils/StaleDelta.d.ts.map +1 -0
- package/dist/esm/utils/StaleDelta.js +54 -0
- package/dist/esm/utils/StaleDelta.js.map +1 -0
- package/dist/esm/utils/TokenBudgetedReader.d.ts.map +1 -1
- package/dist/esm/utils/TokenBudgetedReader.js +56 -15
- package/dist/esm/utils/TokenBudgetedReader.js.map +1 -1
- package/dist/esm/utils/WorkspaceSearch.d.ts +8 -1
- package/dist/esm/utils/WorkspaceSearch.d.ts.map +1 -1
- package/dist/esm/utils/WorkspaceSearch.js +74 -5
- package/dist/esm/utils/WorkspaceSearch.js.map +1 -1
- package/dist/esm/utils/reportSavings.d.ts +3 -0
- package/dist/esm/utils/reportSavings.d.ts.map +1 -1
- package/dist/esm/utils/reportSavings.js +42 -5
- package/dist/esm/utils/reportSavings.js.map +1 -1
- package/package.json +4 -4
- package/dashboard/dist/assets/index-DzL_Zu3m.js +0 -452
- package/dist/cjs/examples/basic-usage.d.ts +0 -12
- package/dist/cjs/examples/basic-usage.d.ts.map +0 -1
- package/dist/cjs/examples/basic-usage.js +0 -128
- package/dist/cjs/examples/basic-usage.js.map +0 -1
- package/dist/esm/examples/basic-usage.d.ts +0 -12
- package/dist/esm/examples/basic-usage.d.ts.map +0 -1
- package/dist/esm/examples/basic-usage.js +0 -126
- package/dist/esm/examples/basic-usage.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# 🧠 Lemma
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@nxuss/lemma)
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
7
|
|
|
8
8
|
**A memory your AI agent can verify, and a context window that only holds what matters.**
|
|
9
9
|
|
|
@@ -19,6 +19,12 @@ lemma init # wires Claude Code, Cursor, Codex, Windsurf, Kiro, OpenCode, Mu
|
|
|
19
19
|
|
|
20
20
|
That's it: your agent now has Lemma's tools. The dashboard opens at `http://localhost:8081/dashboard/`.
|
|
21
21
|
|
|
22
|
+
> **Run `lemma init` in every project, and again after each upgrade.** `npm install -g` updates Lemma's
|
|
23
|
+
> code, but part of what Lemma does lives in files `init` writes into *your project*: the rules your agent
|
|
24
|
+
> reads (`CLAUDE.md` / `AGENTS.md`), the pre-approved tool permissions, and the hooks for the other clients.
|
|
25
|
+
> Without it you keep the old rules and keep seeing permission prompts. Then reconnect the MCP server
|
|
26
|
+
> (`/mcp` in Claude Code) so the running session loads the new build. [What an upgrade does on its own](#after-an-upgrade).
|
|
27
|
+
|
|
22
28
|
---
|
|
23
29
|
|
|
24
30
|
## Why Lemma
|
|
@@ -28,6 +34,15 @@ in a local index (BM25, no embeddings service, no cloud). Every memory is tied t
|
|
|
28
34
|
symbols it's about, so when the code changes it comes back **stale** instead of confidently
|
|
29
35
|
wrong — and a memory with no evidence is labelled as such, never sold as a verified answer.
|
|
30
36
|
|
|
37
|
+
**↪ Work that survives the session (Pro).** A session that edited code leaves a hand-off built from the
|
|
38
|
+
transcript and git, with no model tokens: the task exactly as it was given, the files changed, the last test
|
|
39
|
+
run. The next session's briefing points at it, so "continue where we left off" has something to continue from.
|
|
40
|
+
An agent's own note tends to keep function names and drop their parameter lists; the hand-off keeps the plan
|
|
41
|
+
verbatim. In our benchmark (a plan said only in the first session, graded by hidden tests) agents with Lemma
|
|
42
|
+
finished it in 3 of 3 runs, Claude Code's built-in memory in 1 of 3 and another agent-memory tool in 0 of 3 (3-run
|
|
43
|
+
cells, one model, our own benchmark: a lead, not a proof). It costs more per task, not less, because the agent
|
|
44
|
+
does the work the others skip.
|
|
45
|
+
|
|
31
46
|
**✍️ It fills itself.** A test, build or lint command that goes red → green is saved as
|
|
32
47
|
`fix: <the error>` with the diff that fixed it. Commits become memories. Next time the same
|
|
33
48
|
error shows up, the agent is pointed at the fix — through hooks in Claude Code, Codex, Cursor
|
|
@@ -60,14 +75,15 @@ pretending (`capability_matrix`).
|
|
|
60
75
|
|
|
61
76
|
## Free vs Pro
|
|
62
77
|
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
Free covers what your agent can already do natively (reading, searching, running commands, patching) plus the
|
|
79
|
+
memory you write and read yourself, upkeep and export. Pro is what keeps memories true over time and carries work
|
|
80
|
+
across sessions: `verify_memory`, `refresh_memory`, `brain_team`, and the automatic end-of-session capture and hand-off.
|
|
65
81
|
|
|
66
82
|
| | Free | Pro |
|
|
67
83
|
| :--- | :--- | :--- |
|
|
68
|
-
| Memory | `search_memory`, `store_memory`, `get_memory`, `update_memory`, `
|
|
69
|
-
| Context | `read_workspace_file`, `scrub_privacy`, `token_budget`, `token_receipt`, `auto_context_bundle` | `
|
|
70
|
-
| Workspace |
|
|
84
|
+
| Memory | `search_memory`, `store_memory`, `get_memory`, `update_memory`, `forget_memory`, `brain_upkeep`, `brain_export`… | `verify_memory`, `refresh_memory`, `brain_team`, automatic session capture and hand-off, `get_telepathic_hints`, `semantic_dedup_guard`, `local_semantic_autofix` |
|
|
85
|
+
| Context | `read_workspace_file`, `get_ast_hologram`, `output_region`, `scrub_privacy`, `token_budget`, `token_receipt`, `auto_context_bundle` | `smart_file_slice`, `get_symbol_surgical_context` |
|
|
86
|
+
| Workspace | `search_workspace`, `run_workspace_command`, `apply_workspace_patch` | `write_workspace_file`, `validate_patch_sandbox`, `surgical_ast_insert` |
|
|
71
87
|
| Analysis & review | `type_coverage_report`, `git_blame_heat` | `depgraph`, `entropy_score`, `refactor`, `review_diff`, `review_pr`, `test_oracle` |
|
|
72
88
|
| Monolith analysis | `infra_analyze_monolith`, `infra_suggest_split` (not advertised; via `lemma_toolbox`) | — |
|
|
73
89
|
|
|
@@ -89,9 +105,25 @@ Without a license, Pro tools aren't advertised to the model at all. Full list:
|
|
|
89
105
|
`post-checkout`, and post-tool hooks for Claude Code, Codex, Cursor and OpenCode). In Claude Code
|
|
90
106
|
it adds a `SessionStart` briefing and a prompt-time recall too, and pre-approves the read and
|
|
91
107
|
memory tools so using them never costs a permission prompt. Existing hooks are kept; `--no-hooks`
|
|
92
|
-
skips them.
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
skips them. Health check: `lemma doctor`.
|
|
109
|
+
|
|
110
|
+
### After an upgrade
|
|
111
|
+
|
|
112
|
+
After `npm install -g @nxuss/lemma` and reconnecting the MCP server, a project that already ran
|
|
113
|
+
`lemma init` gets some things by itself and needs `lemma init` again for the rest:
|
|
114
|
+
|
|
115
|
+
| | Comes with the upgrade | Needs `lemma init` again |
|
|
116
|
+
| :--- | :---: | :---: |
|
|
117
|
+
| New and fixed tool behaviour (freshness checks, the diff shown on a STALE memory, the hand-off logic) | ✅ | |
|
|
118
|
+
| Missing Claude Code hooks (`SessionStart`, prompt recall, the note before an edit, `SessionEnd`), added at the next MCP start | ✅ | |
|
|
119
|
+
| Agent rules in `CLAUDE.md` / `AGENTS.md` (when to search, store and leave a hand-off) | | ✅ |
|
|
120
|
+
| Pre-approved read and memory tool permissions (otherwise the agent is asked each time) | | ✅ |
|
|
121
|
+
| Hooks and config for Codex, Cursor, OpenCode, Windsurf and Kiro | | ✅ |
|
|
122
|
+
| A project that never ran `lemma init` | | ✅ |
|
|
123
|
+
|
|
124
|
+
The startup repair adds hooks but never changes what your agent may do without asking, which is why
|
|
125
|
+
permissions and rules wait for `lemma init`. It keeps what you wrote in those files and only replaces
|
|
126
|
+
Lemma's own block.
|
|
95
127
|
|
|
96
128
|
### CLI
|
|
97
129
|
|
|
@@ -119,8 +151,7 @@ MCP server starts; re-run `lemma init` after upgrading to refresh the agent rule
|
|
|
119
151
|
}
|
|
120
152
|
```
|
|
121
153
|
|
|
122
|
-
`LEMMA_BRAIN_ENCRYPT=1` stores the Brain AES-256-GCM encrypted at rest.
|
|
123
|
-
environment variables are documented in [`.env.security.example`](.env.security.example).
|
|
154
|
+
`LEMMA_BRAIN_ENCRYPT=1` stores the Brain AES-256-GCM encrypted at rest.
|
|
124
155
|
|
|
125
156
|
### Proxy mode (optional)
|
|
126
157
|
|
|
@@ -130,4 +161,4 @@ project, never served across projects.
|
|
|
130
161
|
|
|
131
162
|
---
|
|
132
163
|
|
|
133
|
-
|
|
164
|
+
MIT © Nxus Studio · [lemma.nxus.studio](https://lemma.nxus.studio) · [Lemma Pro](https://lemma.nxus.studio/upgrade)
|
package/bin/brain-ingest.js
CHANGED
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* Usage: lemma-brain-ingest [projectRoot] [--commits [--max N]] [--changelog] [--team-pull]
|
|
17
17
|
* lemma-brain-ingest --hook-result [--host=claude|codex|cursor|opencode] < payload.json
|
|
18
18
|
* lemma-brain-ingest --prompt-submit < payload.json
|
|
19
|
+
* lemma-brain-ingest --pre-tool < payload.json
|
|
20
|
+
* lemma-brain-ingest --session-end < payload.json
|
|
19
21
|
*
|
|
20
22
|
* --team-pull Import the repo's shared memories from .lemma-brain/ (post-merge/post-checkout).
|
|
21
23
|
* --hook-result Read a host PostToolUse hook payload on stdin (Claude Code shape) and:
|
|
@@ -123,6 +125,29 @@ async function promptSubmit() {
|
|
|
123
125
|
await reportOffered(dist, payload && payload.cwd, [recall]);
|
|
124
126
|
}
|
|
125
127
|
|
|
128
|
+
// PreToolUse hook (Claude Code), edits only: the memories about the file, before it is written.
|
|
129
|
+
// Prints context and nothing else — never a permission decision, so it cannot stop or prompt a tool.
|
|
130
|
+
async function preTool() {
|
|
131
|
+
const dist = path.join(__dirname, '..', 'dist', 'cjs');
|
|
132
|
+
let preEditRecall, formatHostOutput;
|
|
133
|
+
try {
|
|
134
|
+
({ preEditRecall } = require(path.join(dist, 'subconscious', 'HostRecall.js')));
|
|
135
|
+
({ formatHostOutput } = require(path.join(dist, 'subconscious', 'HostAdapters.js')));
|
|
136
|
+
} catch {
|
|
137
|
+
return; // no build — a hook must stay silent and harmless
|
|
138
|
+
}
|
|
139
|
+
let payload;
|
|
140
|
+
try {
|
|
141
|
+
payload = JSON.parse(readStdin());
|
|
142
|
+
} catch {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const recall = preEditRecall(payload, payload && payload.cwd);
|
|
146
|
+
const out = formatHostOutput('claude', recall ? recall.text : '', 'PreToolUse');
|
|
147
|
+
if (out) process.stdout.write(out);
|
|
148
|
+
await reportOffered(dist, payload && payload.cwd, [recall]);
|
|
149
|
+
}
|
|
150
|
+
|
|
126
151
|
// SessionStart hook (Claude Code): the Brain's briefing and the Lemma playbook, in context
|
|
127
152
|
// before the first prompt — no tool call for the agent to remember or skip.
|
|
128
153
|
async function sessionStart() {
|
|
@@ -155,10 +180,26 @@ async function sessionStart() {
|
|
|
155
180
|
await reportOffered(dist, payload.cwd, [recall]);
|
|
156
181
|
}
|
|
157
182
|
|
|
183
|
+
// SessionEnd hook (Claude Code): a session that did real work and stored nothing leaves its closing
|
|
184
|
+
// answer in the Brain (see SessionCapture). Silent: the host shows nothing at session end.
|
|
185
|
+
async function sessionEnd() {
|
|
186
|
+
const dist = path.join(__dirname, '..', 'dist', 'cjs');
|
|
187
|
+
let payload = {};
|
|
188
|
+
try {
|
|
189
|
+
payload = JSON.parse(readStdin()) || {};
|
|
190
|
+
} catch {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const { captureSession } = require(path.join(dist, 'utils', 'SessionCapture.js'));
|
|
194
|
+
captureSession(payload, payload.cwd);
|
|
195
|
+
}
|
|
196
|
+
|
|
158
197
|
async function main() {
|
|
159
198
|
const args = process.argv.slice(2);
|
|
199
|
+
if (args.includes('--session-end')) return sessionEnd();
|
|
160
200
|
if (args.includes('--session-start')) return sessionStart();
|
|
161
201
|
if (args.includes('--prompt-submit')) return promptSubmit();
|
|
202
|
+
if (args.includes('--pre-tool')) return preTool();
|
|
162
203
|
if (args.includes('--hook-result')) return hookResult(args);
|
|
163
204
|
const maxIndex = args.indexOf('--max');
|
|
164
205
|
const maxCommits = maxIndex >= 0 ? parseInt(args[maxIndex + 1], 10) : undefined;
|
package/bin/init.js
CHANGED
|
@@ -261,94 +261,39 @@ function loadLemmaToolNames() {
|
|
|
261
261
|
|
|
262
262
|
const LEMMA_TOOL_NAMES = loadLemmaToolNames();
|
|
263
263
|
// Pre-approved in Claude Code: reading, searching and the Brain's lifecycle — local, and nothing
|
|
264
|
-
// a native Read/Grep couldn't already do. Tools that write code
|
|
265
|
-
//
|
|
266
|
-
//
|
|
264
|
+
// a native Read/Grep couldn't already do — plus run_workspace_command. Tools that write code
|
|
265
|
+
// (apply_workspace_patch, surgical_ast_insert), delete (forget_memory) or dispatch arbitrary tools
|
|
266
|
+
// (lemma_toolbox) still ask.
|
|
267
|
+
//
|
|
268
|
+
// run_workspace_command is pre-approved on purpose, and it is a real trade-off: a tool that asks on
|
|
269
|
+
// every call is one agents stop using, and it is the one that turns a 70k-character test run into
|
|
270
|
+
// its failures (measured). What it lets through without a prompt is whatever Lemma's own allowlist
|
|
271
|
+
// permits (npm, git, node, python, make, docker… — deny-by-default, no redirection or substitution),
|
|
272
|
+
// which is more than a native Bash rule like `Bash(npx jest *)` would. To tighten it: set
|
|
273
|
+
// mcp.confirmActions or mcp.sandbox (docker) in lemma.config.json, or remove the entry from
|
|
274
|
+
// permissions.allow.
|
|
267
275
|
const LEMMA_PREAPPROVED_TOOLS = [
|
|
268
276
|
'search_memory', 'get_memory', 'verify_memory', 'store_memory', 'update_memory', 'refresh_memory',
|
|
269
277
|
'downvote_memory', 'read_workspace_file', 'list_workspace_dir', 'search_workspace', 'smart_file_slice',
|
|
270
278
|
'get_ast_hologram', 'get_symbol_surgical_context', 'output_region', 'get_project_onboarding',
|
|
271
|
-
'state_hash_cache', 'smarter_cache', 'token_receipt', 'get_routing_advice',
|
|
279
|
+
'state_hash_cache', 'smarter_cache', 'token_receipt', 'get_routing_advice', 'run_workspace_command',
|
|
272
280
|
].map((name) => `mcp__lemma__${name}`);
|
|
273
281
|
|
|
274
282
|
const CLAUDE_MD_CONTENT = `# Lemma MCP — Cómo usar las herramientas de este proyecto
|
|
275
283
|
|
|
276
|
-
|
|
277
|
-
es **recordar entre sesiones**: el Brain guarda fixes y decisiones verificadas, y avisa cuando el
|
|
278
|
-
código del que dependen cambió. Ese valor solo existe si se consulta y se alimenta, así que estos
|
|
279
|
-
momentos no son opcionales:
|
|
280
|
-
|
|
281
|
-
## Disparadores — haz esto siempre
|
|
282
|
-
|
|
283
|
-
| Cuando… | Llama | Por qué no basta lo nativo |
|
|
284
|
-
|---|---|---|
|
|
285
|
-
| Vas a investigar un bug, un error o un "¿por qué X?" | \`mcp__lemma__search_memory\` con el texto del error o la pregunta | Puede que ya esté resuelto; \`grep\` no encuentra lo que se aprendió en otra sesión |
|
|
286
|
-
| Un aviso 🧠 de Lemma nombra memorias | \`mcp__lemma__get_memory\` de la primera | El aviso trae solo el titular; el cuerpo tiene los archivos y el porqué |
|
|
287
|
-
| Un fix quedó verificado (tests pasan, build ok, el usuario confirmó) | \`mcp__lemma__store_memory\` con \`filePaths\`: la causa raíz, la trampa o lo que no funcionó — no un resumen de qué cambió | Si no se guarda, la próxima sesión lo vuelve a investigar desde cero; un resumen de cambios ya está en git log y nadie lo vuelve a buscar |
|
|
288
|
-
| Una memoria resultó desactualizada o incorrecta | \`mcp__lemma__update_memory\` / \`forget_memory\` | Guardar otra al lado deja dos versiones y la búsqueda mezcla ambas |
|
|
289
|
-
| Vas a leer código que aún no ubicaste, o que puede tener secretos | \`mcp__lemma__search_workspace\` / \`read_workspace_file\` | Enmascaran API keys antes de que entren a tu contexto |
|
|
290
|
-
|
|
291
|
-
Si las tools de Lemma te llegan diferidas (solo el nombre), cárgalas en **una sola** llamada a
|
|
292
|
-
ToolSearch: \`select:mcp__lemma__search_memory,mcp__lemma__get_memory,mcp__lemma__verify_memory,mcp__lemma__store_memory,mcp__lemma__update_memory,mcp__lemma__read_workspace_file,mcp__lemma__search_workspace\`.
|
|
293
|
-
|
|
294
|
-
Fuera de esos momentos usa tu criterio: para un archivo chico que ya ubicaste, \`Read\` nativo
|
|
295
|
-
cuesta menos que el ida y vuelta, y para procesos, puertos o git Lemma no tiene nada que aportar.
|
|
296
|
-
|
|
297
|
-
## Referencia: qué tool para qué
|
|
298
|
-
|
|
299
|
-
| Tarea | Tool de Lemma | Por qué |
|
|
300
|
-
|---|---|---|
|
|
301
|
-
| Leer/listar/buscar en el workspace | \`mcp__lemma__read_workspace_file\`, \`list_workspace_dir\`, \`search_workspace\` | Auto-scrubbing de secretos y compresión de contexto sin perder el archivo real |
|
|
302
|
-
| Pregunta puntual, de una sola sesión, atada a archivos concretos | \`mcp__lemma__state_hash_cache\` (action="lookup" antes de razonar, "store" después) | Hit solo si el hash exacto de esos archivos no cambió — invalidación exacta, no una adivinanza por similitud. Match de texto exacto en la query: no sirve para "¿algo así ya se resolvió?", solo para "esta misma pregunta, ¿estos archivos siguen igual?" |
|
|
303
|
-
| Conocimiento que debe sobrevivir sesiones, con seguimiento por símbolo (no todo el archivo) | \`mcp__lemma__search_memory\` / \`store_memory\` | Motor con más garantías: invalida por símbolo específico (no por archivo completo), distingue cambio de código real vs. cambio de branch, y sigue cadenas de memorias derivadas — no solo "sin dependencia de archivos", también rastrea archivos con más precisión que state_hash_cache |
|
|
304
|
-
| Pregunta general, sin dependencia de archivos ni necesidad de sobrevivir sesiones | \`mcp__lemma__smarter_cache\` | Cache semántico difuso; si hay hit, dilo explícitamente ("esto viene de una respuesta cacheada") en vez de presentarlo como recién razonado |
|
|
305
|
-
| Tarea mecánica/trivial y quieres saber si conviene un modelo más barato | \`mcp__lemma__get_routing_advice\` | Evita gastar presupuesto de modelo grande en algo mecánico |
|
|
306
|
-
| 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 |
|
|
307
|
-
| Antes de aplicar un patch generado | \`mcp__lemma__validate_patch_sandbox\` | Valídalo antes de escribirlo, no después |
|
|
308
|
-
| "¿Cuánto ahorramos de verdad?" | \`mcp__lemma__token_receipt\` | Bitácora auditable real, no una cifra inventada |
|
|
309
|
-
| 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 |
|
|
310
|
-
| 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 |
|
|
311
|
-
| 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 |
|
|
312
|
-
| 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) |
|
|
313
|
-
| 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 |
|
|
314
|
-
|
|
315
|
-
El ciclo de vida completo (\`search\` → \`get\` → \`update\`/\`refresh\`/\`forget\`) está anunciado:
|
|
316
|
-
ante una memoria desactualizada, corrígela; no guardes otra al lado.
|
|
317
|
-
|
|
318
|
-
## No todas las tools de Lemma están en tu lista
|
|
319
|
-
|
|
320
|
-
Cada schema que Lemma anuncia se re-envía en **cada** request de la sesión, así que solo se
|
|
321
|
-
anuncian las que este proyecto puede usar de verdad. El resto sigue registrado y disponible:
|
|
322
|
-
|
|
323
|
-
- \`mcp__lemma__lemma_toolbox\` con \`action="list"\` (y un \`query\`) para ver qué existe.
|
|
324
|
-
- \`action="schema"\` para leer los inputs de una tool antes de llamarla.
|
|
325
|
-
- \`action="call"\` para ejecutarla.
|
|
326
|
-
|
|
327
|
-
Antes de decirle al usuario que Lemma no puede hacer algo, revisa el toolbox. Si el proyecto
|
|
328
|
-
necesita todas las tools anunciadas de forma permanente, pon \`mcp.toolProfile: "full"\` en
|
|
329
|
-
\`lemma.config.json\`.
|
|
330
|
-
|
|
331
|
-
## Antes de pedirle código al usuario
|
|
332
|
-
|
|
333
|
-
No le pidas que pegue archivos, logs o documentación que puedes obtener tú: agota primero
|
|
334
|
-
\`search_workspace\`, \`get_ast_hologram\` (estructura: da nombres y líneas, no firmas) y
|
|
335
|
-
\`read_workspace_file\`. Cada ida y vuelta con el usuario cuesta más que la lectura.
|
|
336
|
-
|
|
337
|
-
## Al iniciar en este proyecto
|
|
338
|
-
|
|
339
|
-
El hook \`SessionStart\` de Lemma ya te dio un resumen del Brain al arrancar (si no lo ves,
|
|
340
|
-
\`lemma doctor\` dice qué falta). Para más contexto: \`mcp__lemma__get_project_onboarding\`.
|
|
341
|
-
Lo que venga del Brain no es un hecho hasta verificarlo contra el estado actual del código.
|
|
342
|
-
|
|
343
|
-
## Al investigar un bug, además
|
|
284
|
+
El servidor MCP \`lemma\` recuerda entre sesiones y avisa cuando el código del que depende una memoria cambió. Úsalo en estos momentos:
|
|
344
285
|
|
|
345
|
-
|
|
286
|
+
## Disparadores
|
|
346
287
|
|
|
347
|
-
|
|
288
|
+
- Vas a investigar un bug, un error o un "¿por qué X?" → \`mcp__lemma__search_memory\` con el error o la pregunta.
|
|
289
|
+
- Un aviso 🧠 nombra memorias → \`mcp__lemma__get_memory\` de la primera.
|
|
290
|
+
- Un fix quedó verificado (tests pasan) → \`mcp__lemma__store_memory\` con \`filePaths\`: la causa raíz o la trampa, no un resumen del cambio.
|
|
291
|
+
- Terminas una sesión con trabajo a medias o por partes → \`store_memory\` (o \`update_memory\` si ya hay una) con lo HECHO, lo PENDIENTE y los nombres/firmas exactos de lo especificado; no resumas la especificación.
|
|
292
|
+
- Una memoria quedó desactualizada → \`update_memory\` (o \`forget_memory\`); no guardes otra al lado.
|
|
293
|
+
- Tests, build o lint con salida larga → \`mcp__lemma__run_workspace_command\`: devuelve los fallos y el resumen; el resto se abre con \`output_region\`.
|
|
294
|
+
- Código que aún no ubicaste o que puede tener secretos → \`search_workspace\` / \`read_workspace_file\`.
|
|
348
295
|
|
|
349
|
-
|
|
350
|
-
para tu propio contexto debe entregarse con la información real y sin ocultar que vino de ahí.
|
|
351
|
-
Ahorrar tokens nunca debe costar que el usuario reciba algo incorrecto sin saberlo.
|
|
296
|
+
Si las tools de Lemma llegan diferidas (solo el nombre), cárgalas en una sola llamada a ToolSearch. Las demás están en \`mcp__lemma__lemma_toolbox\` (\`list\`, \`schema\`, \`call\`). No le pidas al usuario código que puedas leer tú. Si una respuesta viene de cache o de un archivo comprimido, dilo.
|
|
352
297
|
|
|
353
298
|
---
|
|
354
299
|
*Auto-generado por \`lemma init\` — @nxuss/lemma*
|
|
@@ -488,6 +433,7 @@ OpenCode).
|
|
|
488
433
|
| Investigating a bug, an error, or a "why does X" | \`search_memory\` with the error text or question | grep can't find what was learned in another session |
|
|
489
434
|
| A 🧠 Lemma note names memories | \`get_memory\` on the top one | The note carries the headline; the body has the files and the reason |
|
|
490
435
|
| A fix is verified (tests pass, build ok, user confirmed) | \`store_memory\` with \`filePaths\`: the root cause, the trap or what didn't work — not a summary of what changed | Unsaved, the next session investigates it from scratch; a change summary is already in git log and nobody searches for it |
|
|
436
|
+
| You end a session with work half done or split in parts | \`store_memory\` (or \`update_memory\` if one exists): what is DONE, what is PENDING, and the exact names/signatures that were specified — don't summarise the spec | The next session starts with no memory of the plan; a vague note loses the details |
|
|
491
437
|
| A memory turned out outdated or wrong | \`update_memory\` / \`forget_memory\` | A near-duplicate leaves two versions and search mixes them |
|
|
492
438
|
| Reading code you haven't located yet, or that may hold secrets | \`search_workspace\` / \`read_workspace_file\` | They mask API keys before they reach your context |
|
|
493
439
|
| Applying a generated patch | \`validate_patch_sandbox\` first | Validate before writing, not after |
|
|
@@ -990,7 +936,7 @@ function applyEnvToCurrentProcess() {
|
|
|
990
936
|
// Upstream API keys live here (mode 0600) instead of inside the launchd/systemd
|
|
991
937
|
// unit: the proxy daemon loads this file at startup (lemma-proxy.cjs loadEnvFile),
|
|
992
938
|
// so a background service gets credentials without baking secrets into a unit file.
|
|
993
|
-
function createEnvFile(envFile = ENV_FILE) {
|
|
939
|
+
function createEnvFile(envFile = ENV_FILE, { routing = true } = {}) {
|
|
994
940
|
const proxyUrl = `http://localhost:${PORT}`;
|
|
995
941
|
const keyLines = [
|
|
996
942
|
process.env.ANTHROPIC_API_KEY ? `export ANTHROPIC_API_KEY=${process.env.ANTHROPIC_API_KEY}` : '',
|
|
@@ -1001,14 +947,14 @@ function createEnvFile(envFile = ENV_FILE) {
|
|
|
1001
947
|
# source ${envFile}
|
|
1002
948
|
export LEMMA_PROXY_PORT=${PORT}
|
|
1003
949
|
export LEMMA_CACHE_DIR=${LEMMA_CACHE}
|
|
1004
|
-
export OPENAI_BASE_URL=${proxyUrl}
|
|
950
|
+
${routing ? `export OPENAI_BASE_URL=${proxyUrl}
|
|
1005
951
|
export OPENAI_API_BASE=${proxyUrl}
|
|
1006
952
|
export CODEX_BASE_URL=${proxyUrl}
|
|
1007
953
|
export LITELLM_PROXY_BASE_URL=${proxyUrl}
|
|
1008
|
-
${keyLines ? keyLines + '\n' : ''}`;
|
|
954
|
+
` : ''}${keyLines ? keyLines + '\n' : ''}`;
|
|
1009
955
|
fs.writeFileSync(envFile, envContent, 'utf8');
|
|
1010
956
|
try { fs.chmodSync(envFile, 0o600); } catch {}
|
|
1011
|
-
applyEnvToCurrentProcess();
|
|
957
|
+
if (routing) applyEnvToCurrentProcess();
|
|
1012
958
|
return envFile;
|
|
1013
959
|
}
|
|
1014
960
|
|
|
@@ -1048,6 +994,44 @@ function setupShellIntegration() {
|
|
|
1048
994
|
return configured;
|
|
1049
995
|
}
|
|
1050
996
|
|
|
997
|
+
/**
|
|
998
|
+
* Undo what an earlier `lemma init` appended to the shell: the two lines that source the env file
|
|
999
|
+
* (only that exact block — anything the user wrote, or commented out, is left alone) and the
|
|
1000
|
+
* launchctl variables, only when they still hold the value we set.
|
|
1001
|
+
*
|
|
1002
|
+
* Routing OPENAI_BASE_URL & co. through localhost:8081 is only safe while the proxy is guaranteed
|
|
1003
|
+
* to be up, which is what --daemon installs. Without it, the first time the proxy was not running
|
|
1004
|
+
* every OpenAI-compatible tool in every new terminal (and GUI apps, via launchctl) failed with
|
|
1005
|
+
* 'connection refused'.
|
|
1006
|
+
*/
|
|
1007
|
+
function removeShellIntegration(home = os.homedir()) {
|
|
1008
|
+
const block = /\n?# Lemma Cache Proxy — auto-configured by `lemma init`\n\[ -f "[^"\n]*lemma-cache\/env" \] && \. "[^"\n]*lemma-cache\/env"\n?/g;
|
|
1009
|
+
let removed = false;
|
|
1010
|
+
for (const file of [path.join(home, '.zshrc'), path.join(home, '.bashrc'), path.join(home, '.config', 'fish', 'config.fish')]) {
|
|
1011
|
+
try {
|
|
1012
|
+
if (!fs.existsSync(file)) continue;
|
|
1013
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
1014
|
+
const next = content.replace(block, '\n');
|
|
1015
|
+
if (next !== content) {
|
|
1016
|
+
fs.writeFileSync(file, next, 'utf8');
|
|
1017
|
+
removed = true;
|
|
1018
|
+
}
|
|
1019
|
+
} catch {}
|
|
1020
|
+
}
|
|
1021
|
+
if (process.platform === 'darwin') {
|
|
1022
|
+
for (const name of ['OPENAI_BASE_URL', 'CODEX_BASE_URL']) {
|
|
1023
|
+
try {
|
|
1024
|
+
const current = String(execSync(`launchctl getenv ${name}`, { stdio: ['ignore', 'pipe', 'ignore'] }) || '').trim();
|
|
1025
|
+
if (current === `http://localhost:${PORT}`) {
|
|
1026
|
+
execSync(`launchctl unsetenv ${name}`, { stdio: 'ignore' });
|
|
1027
|
+
removed = true;
|
|
1028
|
+
}
|
|
1029
|
+
} catch {}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
return removed;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1051
1035
|
async function main(argv = process.argv) {
|
|
1052
1036
|
const opts = parseInitArgs(argv);
|
|
1053
1037
|
if (opts.showHelp) {
|
|
@@ -1150,7 +1134,7 @@ async function main(argv = process.argv) {
|
|
|
1150
1134
|
});
|
|
1151
1135
|
if (hasCodex) {
|
|
1152
1136
|
const codexConfigured = configureCodexCliMcp();
|
|
1153
|
-
if (codexConfigured) ok('Codex CLI configured (MCP
|
|
1137
|
+
if (codexConfigured) ok('Codex CLI configured (MCP)');
|
|
1154
1138
|
else warn('Codex CLI detected but MCP config could not be auto-updated');
|
|
1155
1139
|
} else skip('Codex CLI not detected');
|
|
1156
1140
|
|
|
@@ -1176,12 +1160,20 @@ async function main(argv = process.argv) {
|
|
|
1176
1160
|
skip('Brain hooks not installed (--no-hooks)');
|
|
1177
1161
|
}
|
|
1178
1162
|
|
|
1179
|
-
|
|
1163
|
+
// Routing model traffic through the local proxy is tied to the daemon: only then is the proxy
|
|
1164
|
+
// guaranteed to be listening. Without it nothing in the user's shell or GUI apps is pointed at it.
|
|
1165
|
+
const envFile = createEnvFile(ENV_FILE, { routing: opts.wantDaemon });
|
|
1180
1166
|
ok(`Env file created: ${envFile}`);
|
|
1181
1167
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1168
|
+
if (opts.wantDaemon) {
|
|
1169
|
+
const shellOk = setupShellIntegration();
|
|
1170
|
+
if (shellOk) ok('Shell rc files updated (source ~/.lemma-cache/env)');
|
|
1171
|
+
else warn('Could not update shell rc — source manually: source ~/.lemma-cache/env');
|
|
1172
|
+
} else if (removeShellIntegration()) {
|
|
1173
|
+
ok('Removed the proxy routing an earlier init added to your shell (OPENAI_BASE_URL & co. pointed at a proxy that is not installed)');
|
|
1174
|
+
} else {
|
|
1175
|
+
skip('Shell and app environment untouched — no model traffic is routed (opt-in via `lemma init --daemon`)');
|
|
1176
|
+
}
|
|
1185
1177
|
|
|
1186
1178
|
process.stderr.write(`\n ${BOLD}${GREEN}✓ Lemma init complete${RESET}\n`);
|
|
1187
1179
|
process.stderr.write(` ${DIM}──────────────────────────────────────${RESET}\n`);
|
|
@@ -1192,14 +1184,22 @@ async function main(argv = process.argv) {
|
|
|
1192
1184
|
}
|
|
1193
1185
|
process.stderr.write(` ${GREEN}●${RESET} MCP server ready ${DIM}(lemma mcp)${RESET}\n`);
|
|
1194
1186
|
process.stderr.write(` ${GREEN}●${RESET} Dashboard active ${DIM}(lemma stats)${RESET}\n`);
|
|
1195
|
-
|
|
1187
|
+
if (opts.wantDaemon) {
|
|
1188
|
+
process.stderr.write(` ${GREEN}●${RESET} New terminals: env vars auto-loaded\n`);
|
|
1189
|
+
}
|
|
1196
1190
|
process.stderr.write(` ${DIM}Upkeep: re-run 'lemma init' after upgrading @nxuss/lemma (refreshes skills + rules)${RESET}\n`);
|
|
1197
|
-
|
|
1191
|
+
if (opts.wantDaemon) {
|
|
1192
|
+
process.stderr.write(` ${GREEN}●${RESET} Todos los IDE/CLI nuevos ya apuntan al proxy\n`);
|
|
1193
|
+
}
|
|
1198
1194
|
process.stderr.write(` ${gatewayResult.activated ? GREEN + '●' : YELLOW + '○'}${RESET} Claude Code gateway: ${gatewayResult.activated ? 'ACTIVE (model traffic cached)' : 'inactive (MCP tools only)'}\n`);
|
|
1199
1195
|
process.stderr.write(` ${DIM}──────────────────────────────────────${RESET}\n`);
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1196
|
+
if (opts.wantDaemon) {
|
|
1197
|
+
process.stderr.write(` ${YELLOW}Para activar en ESTA terminal:${RESET}\n`);
|
|
1198
|
+
process.stderr.write(` ${BOLD}source ${ENV_FILE}${RESET}\n`);
|
|
1199
|
+
process.stderr.write(` ${DIM}Logs: ${LEMMA_CACHE}/proxy-daemon.log${RESET}\n\n`);
|
|
1200
|
+
} else {
|
|
1201
|
+
process.stderr.write(` ${DIM}Open your agent (Claude Code, Cursor, Codex…) in this project and the MCP tools are there.${RESET}\n\n`);
|
|
1202
|
+
}
|
|
1203
1203
|
}
|
|
1204
1204
|
|
|
1205
1205
|
if (require.main === module) {
|
|
@@ -1209,4 +1209,4 @@ if (require.main === module) {
|
|
|
1209
1209
|
});
|
|
1210
1210
|
}
|
|
1211
1211
|
|
|
1212
|
-
module.exports = { main, configureMuse, configureMuseUserSkill, updateAgentsMd, configureClaudeCode, MUSE_SKILL_CONTENT, LEMMA_RULES_CONTENT, CLAUDE_MD_CONTENT, installProxyDaemon, removeProxyDaemon, parseInitArgs, createEnvFile, installBrainFeeds };
|
|
1212
|
+
module.exports = { removeShellIntegration, setupShellIntegration, main, configureMuse, configureMuseUserSkill, updateAgentsMd, configureClaudeCode, MUSE_SKILL_CONTENT, LEMMA_RULES_CONTENT, CLAUDE_MD_CONTENT, installProxyDaemon, removeProxyDaemon, parseInitArgs, createEnvFile, installBrainFeeds };
|
package/bin/postinstall.js
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
* catalog so it auto-loads in every `muse` session, with zero extra steps
|
|
7
7
|
* after `npm i [-g] @nxuss/lemma`.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* It also tells the person to run `lemma init` in their projects, once, on a GLOBAL install
|
|
10
|
+
* to a terminal (see notify). Total function: never fails the install, finishes in well under a
|
|
11
|
+
* second when there is nothing to do. No build output needed — skill install only registers a
|
|
12
|
+
* markdown file, it never invokes any tool.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
const { execSync, execFileSync } = require('child_process');
|
|
@@ -28,7 +29,58 @@ function museOnPath() {
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
const NOTICE = [
|
|
33
|
+
'',
|
|
34
|
+
' Lemma is installed. One more step, in each project you want it in:',
|
|
35
|
+
'',
|
|
36
|
+
' lemma init',
|
|
37
|
+
'',
|
|
38
|
+
' Run it again after every upgrade: it updates the agent rules and the pre-approved',
|
|
39
|
+
' permissions, which the package alone cannot change. Then reconnect the MCP server',
|
|
40
|
+
' (/mcp in Claude Code). What an upgrade does by itself: README, "After an upgrade".',
|
|
41
|
+
'',
|
|
42
|
+
].join('\n');
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Only a global install is the moment someone is setting Lemma up. `npx -y @nxuss/lemma mcp`
|
|
46
|
+
* (how some clients launch the server) and installs as a dependency run this script too, and
|
|
47
|
+
* a message written to the terminal there would land on top of the client's own screen.
|
|
48
|
+
*/
|
|
49
|
+
function shouldNotify(env) {
|
|
50
|
+
if (env.npm_config_global !== 'true') return false;
|
|
51
|
+
if (env.CI || env.LEMMA_QUIET) return false;
|
|
52
|
+
if (/^(silent|error)$/.test(env.npm_config_loglevel || '')) return false;
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* npm 7+ hides a postinstall script's output on a normal install, so printing to stdout would
|
|
58
|
+
* reach nobody. The controlling terminal is written directly instead; with no terminal (a pipe,
|
|
59
|
+
* a container) that fails quietly and the notice goes to stderr, which `--foreground-scripts` shows.
|
|
60
|
+
*/
|
|
61
|
+
function notify() {
|
|
62
|
+
try {
|
|
63
|
+
const fd = fs.openSync(process.platform === 'win32' ? '\\\\.\\CON' : '/dev/tty', 'w');
|
|
64
|
+
try {
|
|
65
|
+
fs.writeSync(fd, `${NOTICE}\n`);
|
|
66
|
+
} finally {
|
|
67
|
+
fs.closeSync(fd);
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
try {
|
|
71
|
+
process.stderr.write(`${NOTICE}\n`);
|
|
72
|
+
} catch {
|
|
73
|
+
// a notice is never worth failing an install
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
31
78
|
function main() {
|
|
79
|
+
try {
|
|
80
|
+
if (shouldNotify(process.env)) notify();
|
|
81
|
+
} catch {
|
|
82
|
+
// best-effort, same contract as below
|
|
83
|
+
}
|
|
32
84
|
try {
|
|
33
85
|
const skillDir = path.join(__dirname, '..', '.agents', 'skills', 'lemma');
|
|
34
86
|
if (!fs.existsSync(path.join(skillDir, 'SKILL.md'))) return;
|
|
@@ -40,4 +92,6 @@ function main() {
|
|
|
40
92
|
}
|
|
41
93
|
}
|
|
42
94
|
|
|
43
|
-
main();
|
|
95
|
+
if (require.main === module) main();
|
|
96
|
+
|
|
97
|
+
module.exports = { shouldNotify, NOTICE };
|