@nxuss/lemma 1.27.1 → 1.28.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 +53 -14
- package/bin/brain-ingest.js +64 -2
- package/dist/cjs/cli/gitHooks.d.ts +28 -0
- package/dist/cjs/cli/gitHooks.d.ts.map +1 -0
- package/dist/cjs/cli/gitHooks.js +112 -0
- package/dist/cjs/cli/gitHooks.js.map +1 -0
- package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/cjs/cli/lemma-proxy.js +12 -0
- package/dist/cjs/cli/lemma-proxy.js.map +1 -1
- package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/cjs/mcp/tool-profiles.js +8 -0
- package/dist/cjs/mcp/tool-profiles.js.map +1 -1
- package/dist/cjs/mcp/tools/memory.d.ts.map +1 -1
- package/dist/cjs/mcp/tools/memory.js +56 -0
- package/dist/cjs/mcp/tools/memory.js.map +1 -1
- package/dist/cjs/mcp/tools/misc.d.ts.map +1 -1
- package/dist/cjs/mcp/tools/misc.js +16 -1
- package/dist/cjs/mcp/tools/misc.js.map +1 -1
- package/dist/cjs/mcp/tools/workspace.d.ts.map +1 -1
- package/dist/cjs/mcp/tools/workspace.js +17 -3
- 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 +2 -0
- package/dist/cjs/mcp/tools.js.map +1 -1
- package/dist/cjs/subconscious/GitIngest.d.ts.map +1 -1
- package/dist/cjs/subconscious/GitIngest.js +49 -1
- package/dist/cjs/subconscious/GitIngest.js.map +1 -1
- package/dist/cjs/subconscious/TeamBrain.d.ts +82 -0
- package/dist/cjs/subconscious/TeamBrain.d.ts.map +1 -0
- package/dist/cjs/subconscious/TeamBrain.js +543 -0
- package/dist/cjs/subconscious/TeamBrain.js.map +1 -0
- package/dist/cjs/subconscious/TheBrainV2.d.ts +4 -2
- package/dist/cjs/subconscious/TheBrainV2.d.ts.map +1 -1
- package/dist/cjs/subconscious/TheBrainV2.js +1 -0
- package/dist/cjs/subconscious/TheBrainV2.js.map +1 -1
- package/dist/cjs/utils/FixCapture.d.ts +66 -0
- package/dist/cjs/utils/FixCapture.d.ts.map +1 -0
- package/dist/cjs/utils/FixCapture.js +338 -0
- package/dist/cjs/utils/FixCapture.js.map +1 -0
- package/dist/esm/cli/gitHooks.d.ts +28 -0
- package/dist/esm/cli/gitHooks.d.ts.map +1 -0
- package/dist/esm/cli/gitHooks.js +106 -0
- package/dist/esm/cli/gitHooks.js.map +1 -0
- package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
- package/dist/esm/cli/lemma-proxy.js +12 -0
- package/dist/esm/cli/lemma-proxy.js.map +1 -1
- package/dist/esm/mcp/tool-profiles.d.ts.map +1 -1
- package/dist/esm/mcp/tool-profiles.js +8 -0
- package/dist/esm/mcp/tool-profiles.js.map +1 -1
- package/dist/esm/mcp/tools/memory.d.ts.map +1 -1
- package/dist/esm/mcp/tools/memory.js +56 -0
- package/dist/esm/mcp/tools/memory.js.map +1 -1
- package/dist/esm/mcp/tools/misc.d.ts.map +1 -1
- package/dist/esm/mcp/tools/misc.js +16 -1
- package/dist/esm/mcp/tools/misc.js.map +1 -1
- package/dist/esm/mcp/tools/workspace.d.ts.map +1 -1
- package/dist/esm/mcp/tools/workspace.js +17 -3
- 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 +2 -0
- package/dist/esm/mcp/tools.js.map +1 -1
- package/dist/esm/subconscious/GitIngest.d.ts.map +1 -1
- package/dist/esm/subconscious/GitIngest.js +49 -1
- package/dist/esm/subconscious/GitIngest.js.map +1 -1
- package/dist/esm/subconscious/TeamBrain.d.ts +82 -0
- package/dist/esm/subconscious/TeamBrain.d.ts.map +1 -0
- package/dist/esm/subconscious/TeamBrain.js +533 -0
- package/dist/esm/subconscious/TeamBrain.js.map +1 -0
- package/dist/esm/subconscious/TheBrainV2.d.ts +4 -2
- package/dist/esm/subconscious/TheBrainV2.d.ts.map +1 -1
- package/dist/esm/subconscious/TheBrainV2.js +1 -0
- package/dist/esm/subconscious/TheBrainV2.js.map +1 -1
- package/dist/esm/utils/FixCapture.d.ts +66 -0
- package/dist/esm/utils/FixCapture.d.ts.map +1 -0
- package/dist/esm/utils/FixCapture.js +328 -0
- package/dist/esm/utils/FixCapture.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@nxuss/lemma)
|
|
4
4
|
[](https://github.com/Nxusbets/lemma/blob/main/LICENSE)
|
|
5
|
-
[](https://github.com/Nxusbets/lemma)
|
|
6
6
|
[](https://github.com/Nxusbets/lemma)
|
|
7
7
|
[](https://github.com/Nxusbets/lemma)
|
|
8
|
-
[](https://github.com/Nxusbets/lemma)
|
|
9
9
|
|
|
10
10
|
```text
|
|
11
11
|
__
|
|
@@ -35,6 +35,18 @@ lemma start # gateway + dashboard on http://localhost:8081
|
|
|
35
35
|
>
|
|
36
36
|
> **🆕 v1.22.0 — Lemma auto-activates in every LLM client.** `npm i -g @nxuss/lemma` registers the skill in your **user** catalog (auto-loads in every `muse` session, any project) and `lemma init` wires each project for Muse, Claude Code, Cursor, Codex, Windsurf, Kiro and OpenCode. Health check: `lemma doctor`.
|
|
37
37
|
>
|
|
38
|
+
> **🆕 v1.28.0 — the Brain goes team-wide through git, and fills itself.** `brain_team push`
|
|
39
|
+
> writes your verified memories into the repo (`.lemma-brain/memories/`, one reviewable JSON file
|
|
40
|
+
> each, paths relative to the repo root); `brain_team pull` — automatic on merge/checkout once
|
|
41
|
+
> `lemma init` installs the hooks — imports a teammate's memories rebased onto *your* checkout. A
|
|
42
|
+
> memory about a file that is byte-identical on your machine arrives **fresh**; one whose file has
|
|
43
|
+
> moved on arrives **stale**, never silently trusted. And memories no longer depend on the model
|
|
44
|
+
> remembering `store_memory`: a test/build/lint command that goes **red → green** is captured as
|
|
45
|
+
> `fix: <the error>` + the files and diff that fixed it — searchable by the exact error message next
|
|
46
|
+
> time — whether it ran through `run_workspace_command` or the agent's own shell (Claude Code
|
|
47
|
+
> `PostToolUse` hook). Commits become memories anchored to their own blobs. Also fixes
|
|
48
|
+
> `lemma-brain-ingest` silently dropping everything it ingested.
|
|
49
|
+
>
|
|
38
50
|
> **🆕 v1.26.0 — a memory without evidence is no longer sold as a verified hit, and the Brain
|
|
39
51
|
> repairs it on the spot.** `search_memory` now splits its results into three buckets — `fresh`
|
|
40
52
|
> (tracked + verified, the only ones credited `tokensSaved`/`hits`), `stale`, and `UNTRACKED`
|
|
@@ -64,7 +76,7 @@ lemma start # gateway + dashboard on http://localhost:8081
|
|
|
64
76
|
the code they're about to touch has dependent memories — including a documented failed
|
|
65
77
|
attempt — instead of only finding out on the next search. Ranking factors in
|
|
66
78
|
real usage (`hits`), explicit negative feedback (`downvote_memory`), and `cognitive_map` domain
|
|
67
|
-
tags; a
|
|
79
|
+
tags; a command that goes red → green auto-captures the fix (see below); two memories
|
|
68
80
|
about the same symbol that disagree on outcome are flagged, not silently both suggested.
|
|
69
81
|
Every hit explains itself (`whyShown` ranking signals + cited `evidence` files/symbols), and
|
|
70
82
|
every reuse books its savings onto the memory itself — `brain_stats` and `brain_upkeep` name
|
|
@@ -72,6 +84,27 @@ lemma start # gateway + dashboard on http://localhost:8081
|
|
|
72
84
|
it, never one without the other). `store_memory` derives `filePaths`/`symbols` by default from
|
|
73
85
|
whatever the session already read, wrote, or patched when the caller doesn't pass them —
|
|
74
86
|
flagged as auto-derived in the response, correctable, but no longer opt-in.
|
|
87
|
+
- **A team Brain, shared through git** — `brain_team push` writes this project's fresh, tracked
|
|
88
|
+
memories to `.lemma-brain/memories/<id>.json`: one sorted-key JSON file per memory, so two people
|
|
89
|
+
sharing at once never conflict, every shared memory goes through code review, and deleting one is
|
|
90
|
+
a `git rm`. Evidence is stored relative to the repo root and rebased onto each clone on `pull`,
|
|
91
|
+
hashes untouched — a teammate's memory is verified against *your* files, fresh only if they are
|
|
92
|
+
byte-identical. Never shared: conversation checkpoints, commit memories (each clone derives its
|
|
93
|
+
own), untracked or stale memories, evidence outside the repo, or anything derived from a memory
|
|
94
|
+
that isn't shared. A pulled path that is absolute or escapes the repo is rejected; a memory a
|
|
95
|
+
teammate deletes leaves your clone into `brain_trash` (restorable), and one you wrote yourself is
|
|
96
|
+
never removed. Pushing is always explicit; pulling is a no-op when nothing changed, so it runs on
|
|
97
|
+
every merge/checkout. `brain_team status` shows both sides; `dryRun:true` previews either.
|
|
98
|
+
- **Memories that write themselves** — a verdict command (jest, vitest, pytest, tsc, eslint, `go
|
|
99
|
+
test`, `cargo test`, `npm test`/`build`/`lint`, …) that fails and later passes stores
|
|
100
|
+
`fix: <headline error>` with the failure signature, the files whose content changed between the
|
|
101
|
+
two runs, and their diff — tracked against those files, tagged `confirmed`, source `auto-fix`.
|
|
102
|
+
A pass with nothing changed since the failure is treated as flaky and stores nothing; a "fix" over
|
|
103
|
+
15 files is a refactor and stores nothing. It sees `run_workspace_command`, `test_oracle`, and — via
|
|
104
|
+
`lemma-brain-ingest --hook-result` — the agent's own Bash tool. Commits are ingested with each
|
|
105
|
+
touched file anchored to its blob *in that commit* (`git show sha:path`), not to the working copy.
|
|
106
|
+
`lemma init` installs the `post-commit`, `post-merge` and `post-checkout` hooks that drive this
|
|
107
|
+
(appended to existing hooks, idempotent, never touches a versioned `core.hooksPath` like husky).
|
|
75
108
|
- **A memory that maintains itself** — `brain_upkeep` revalidates the most-reused memories,
|
|
76
109
|
surfaces exact duplicates and merge candidates, dead weight, and savings, each finding with its
|
|
77
110
|
fix. Since v1.26.0 it also **auto-anchors** evidence-free memories whose body names real repo
|
|
@@ -106,7 +139,7 @@ lemma start # gateway + dashboard on http://localhost:8081
|
|
|
106
139
|
compiler-verified AST location instead of asking the model to output a full-file rewrite.
|
|
107
140
|
- **Infrastructure Command Center** — 22 free tools for Jenkins, OKD/OpenShift, and Terraform:
|
|
108
141
|
discover, connect, deploy, diagnose, and detect drift, all via MCP.
|
|
109
|
-
- **Scoped tool surface** — of the
|
|
142
|
+
- **Scoped tool surface** — of the 110 registered tools, only the ones your project shows evidence
|
|
110
143
|
of needing are advertised to the model each turn; the rest stay one `lemma_toolbox` call away.
|
|
111
144
|
|
|
112
145
|
See [`CHANGELOG.md`](CHANGELOG.md) for the full, per-version history.
|
|
@@ -115,14 +148,14 @@ See [`CHANGELOG.md`](CHANGELOG.md) for the full, per-version history.
|
|
|
115
148
|
|
|
116
149
|
## 💎 Free vs Pro
|
|
117
150
|
|
|
118
|
-
|
|
151
|
+
60 of the 110 registered tools work with no license; 50 require Pro. Enforced by one allowlist in
|
|
119
152
|
code (`FREE_TOOLS` in `src/mcp/tools.ts`, including all 22 infrastructure tools), not by narrative.
|
|
120
153
|
A session without a license doesn't even advertise the Pro tools — they stay registered and
|
|
121
154
|
discoverable via `lemma_toolbox`.
|
|
122
155
|
|
|
123
|
-
| | Free (
|
|
156
|
+
| | Free (60) | Pro (50) |
|
|
124
157
|
| :--- | :--- | :--- |
|
|
125
|
-
| **Caching & memory** | `smarter_cache`, `state_hash_cache`, `token_receipt`, `search_memory`, `list_memories`, `store_memory`, `downvote_memory`, `verify_memory`, `token_budget`, `brain_upkeep`, `brain_trash`, `brain_merge` | `get_telepathic_hints`, `query_hybrid_consensus`, `semantic_dedup_guard`, `local_semantic_autofix` |
|
|
158
|
+
| **Caching & memory** | `smarter_cache`, `state_hash_cache`, `token_receipt`, `search_memory`, `list_memories`, `store_memory`, `downvote_memory`, `verify_memory`, `token_budget`, `brain_upkeep`, `brain_trash`, `brain_merge`, `brain_team` | `get_telepathic_hints`, `query_hybrid_consensus`, `semantic_dedup_guard`, `local_semantic_autofix` |
|
|
126
159
|
| **Reading & context** | `read_workspace_file`, `scrub_privacy`, `import_tree_context`, `bulk_file_digest`, `auto_context_bundle`, `git_blame_heat`, `type_coverage_report` | `get_ast_hologram`, `smart_file_slice`, `get_symbol_surgical_context`, `diff_only` |
|
|
127
160
|
| **Writing & workspace** | — | `write_workspace_file`, `apply_workspace_patch`, `multi_file_patch`, `search_workspace`, `run_workspace_command`, `validate_patch_sandbox`, `surgical_ast_insert` |
|
|
128
161
|
| **Static analysis** | — | `entropy_score`, `coupling_radar`, `pattern_fossil`, `precrime_static`, `dead_export_necromancer`, `depgraph`, `refactor`, `ast_flow_visualizer`, `cognitive_map` |
|
|
@@ -137,8 +170,8 @@ Full per-tool listing: `lemma_toolbox { "action": "list" }`.
|
|
|
137
170
|
|
|
138
171
|
## 🎚️ Tool profiles
|
|
139
172
|
|
|
140
|
-
Every advertised tool schema is re-sent on **every request** of the session — advertising all
|
|
141
|
-
costs ~11,
|
|
173
|
+
Every advertised tool schema is re-sent on **every request** of the session — advertising all 110
|
|
174
|
+
costs ~11,900 tokens/turn. Lemma advertises a scoped surface instead: core tools always, situational
|
|
142
175
|
groups (e.g. infrastructure) only when the repo shows evidence for them. Nothing is removed —
|
|
143
176
|
everything stays registered and reachable via `lemma_toolbox`.
|
|
144
177
|
|
|
@@ -161,7 +194,7 @@ lemma_toolbox { "action": "call", "tool": "terraform_drift", "args": {} }
|
|
|
161
194
|
|
|
162
195
|
| Command | Action |
|
|
163
196
|
| :--- | :--- |
|
|
164
|
-
| `lemma init` | Bootstraps config
|
|
197
|
+
| `lemma init` | Bootstraps config, auto-configures MCP for OpenCode, Claude Desktop, Cursor, Windsurf, Kiro, VS Code, and installs the git hooks that feed the Brain (`--no-hooks` to skip). |
|
|
165
198
|
| `lemma start` | Launches proxy, cache engine, and dashboard at `http://localhost:8081`. |
|
|
166
199
|
| `lemma stop` / `lemma status` / `lemma stats` | Stop, health check, and net token/cost report (reports `NET COST` when a session is underwater). |
|
|
167
200
|
| `lemma mcp` | Starts the stdin/stdout MCP server for IDE integration. |
|
|
@@ -169,7 +202,8 @@ lemma_toolbox { "action": "call", "tool": "terraform_drift", "args": {} }
|
|
|
169
202
|
| `lemma patches` / `lemma apply <id>` / `lemma discard <id>` | List, apply, or discard a pending Silent Patch Watcher fix. |
|
|
170
203
|
| `lemma activate <key>` | Activate a Pro license. |
|
|
171
204
|
| `lemma-checkpoint [projectRoot]` | Derive a session checkpoint from repo state (branch, changed files, commits) with no assistant call. |
|
|
172
|
-
| `lemma-brain-ingest [projectRoot] [--changelog]` | Feed The Brain from git commits and `CHANGELOG.md`, incrementally
|
|
205
|
+
| `lemma-brain-ingest [projectRoot] [--changelog] [--team-pull]` | Feed The Brain from git commits and `CHANGELOG.md`, incrementally; `--team-pull` imports the repo's `.lemma-brain/`. |
|
|
206
|
+
| `lemma-brain-ingest --hook-result` | Read a host `PostToolUse` payload on stdin and capture red → green fixes from the agent's own shell. |
|
|
173
207
|
|
|
174
208
|
Wire the standalone bins into hooks so memory doesn't depend on the assistant remembering to call a tool:
|
|
175
209
|
|
|
@@ -179,11 +213,16 @@ Wire the standalone bins into hooks so memory doesn't depend on the assistant re
|
|
|
179
213
|
"command": "lemma-checkpoint \"${CLAUDE_PROJECT_DIR:-$PWD}\"" }] }] } }
|
|
180
214
|
```
|
|
181
215
|
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
|
|
216
|
+
```jsonc
|
|
217
|
+
// .claude/settings.json — red → green fixes from Claude Code's own Bash tool become memories
|
|
218
|
+
{ "hooks": { "PostToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command",
|
|
219
|
+
"command": "lemma-brain-ingest --hook-result" }] }] } }
|
|
185
220
|
```
|
|
186
221
|
|
|
222
|
+
The git side needs nothing by hand: `lemma init` installs `post-commit` (commit → memory) and
|
|
223
|
+
`post-merge`/`post-checkout` (pull the team's `.lemma-brain/`). With a versioned hooks dir
|
|
224
|
+
(`core.hooksPath`, e.g. husky) it prints the lines to add instead of editing team files.
|
|
225
|
+
|
|
187
226
|
Customize via `lemma.config.json`:
|
|
188
227
|
|
|
189
228
|
```json
|
package/bin/brain-ingest.js
CHANGED
|
@@ -13,18 +13,54 @@
|
|
|
13
13
|
* last commit ingested, so running this after every commit or by hand both take the same path
|
|
14
14
|
* and never double-store. `--max N` only matters the first time there is no marker yet.
|
|
15
15
|
*
|
|
16
|
-
* Usage: lemma-brain-ingest [projectRoot] [--max N] [--changelog] [--no-commits]
|
|
16
|
+
* Usage: lemma-brain-ingest [projectRoot] [--max N] [--changelog] [--no-commits] [--team-pull]
|
|
17
|
+
* lemma-brain-ingest --hook-result < payload.json
|
|
18
|
+
*
|
|
19
|
+
* --team-pull Import the repo's shared memories from .lemma-brain/ (post-merge/post-checkout).
|
|
20
|
+
* --hook-result Read a host PostToolUse hook payload on stdin (Claude Code's Bash tool shape) and
|
|
21
|
+
* feed it to FixCapture, so a test that goes red → green through the agent's own
|
|
22
|
+
* shell becomes a memory too. Wire it in .claude/settings.json:
|
|
23
|
+
* "hooks": { "PostToolUse": [{ "matcher": "Bash",
|
|
24
|
+
* "hooks": [{ "type": "command", "command": "lemma-brain-ingest --hook-result" }] }] }
|
|
17
25
|
*/
|
|
18
26
|
|
|
19
27
|
const path = require('path');
|
|
20
28
|
|
|
29
|
+
function readStdin() {
|
|
30
|
+
try {
|
|
31
|
+
return require('fs').readFileSync(0, 'utf8');
|
|
32
|
+
} catch {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function hookResult() {
|
|
38
|
+
const distPath = path.join(__dirname, '..', 'dist', 'cjs', 'utils', 'FixCapture.js');
|
|
39
|
+
let recordHookPayload;
|
|
40
|
+
try {
|
|
41
|
+
({ recordHookPayload } = require(distPath));
|
|
42
|
+
} catch {
|
|
43
|
+
return; // no build — a hook must stay silent and harmless
|
|
44
|
+
}
|
|
45
|
+
let payload;
|
|
46
|
+
try {
|
|
47
|
+
payload = JSON.parse(readStdin());
|
|
48
|
+
} catch {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const res = recordHookPayload(payload, payload && payload.cwd);
|
|
52
|
+
if (res && res.captured) console.error(res.note.trim());
|
|
53
|
+
}
|
|
54
|
+
|
|
21
55
|
function main() {
|
|
22
56
|
const args = process.argv.slice(2);
|
|
57
|
+
if (args.includes('--hook-result')) return hookResult();
|
|
23
58
|
const maxIndex = args.indexOf('--max');
|
|
24
59
|
const maxCommits = maxIndex >= 0 ? parseInt(args[maxIndex + 1], 10) : undefined;
|
|
25
60
|
const includeChangelog = args.includes('--changelog');
|
|
26
61
|
const includeCommits = !args.includes('--no-commits');
|
|
27
|
-
const
|
|
62
|
+
const teamPull = args.includes('--team-pull');
|
|
63
|
+
const positional = args.filter((a, i) => !a.startsWith("--") && (maxIndex < 0 || i !== maxIndex + 1));
|
|
28
64
|
const projectRoot = path.resolve(positional[0] || process.cwd());
|
|
29
65
|
|
|
30
66
|
const distPath = path.join(__dirname, '..', 'dist', 'cjs', 'subconscious', 'GitIngest.js');
|
|
@@ -41,16 +77,42 @@ function main() {
|
|
|
41
77
|
console.error(`[lemma-brain-ingest] commits: ${res.ingested} stored, ${res.skipped} skipped (up to ${res.lastSha ? res.lastSha.substring(0, 12) : 'none'})`);
|
|
42
78
|
}
|
|
43
79
|
|
|
80
|
+
if (teamPull) {
|
|
81
|
+
try {
|
|
82
|
+
const { pullTeamBrain } = require(path.join(__dirname, '..', 'dist', 'cjs', 'subconscious', 'TeamBrain.js'));
|
|
83
|
+
const res = pullTeamBrain(projectRoot);
|
|
84
|
+
console.error(`[lemma-brain-ingest] team: ${res.message}`);
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error('[lemma-brain-ingest] team pull skipped:', err && err.message);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
44
90
|
if (includeChangelog) {
|
|
45
91
|
const res = ingestChangelog(projectRoot);
|
|
46
92
|
console.error(`[lemma-brain-ingest] changelog: ${res.ingested} stored, ${res.skipped} skipped (${res.versionsSeen} version(s) seen)`);
|
|
47
93
|
}
|
|
48
94
|
}
|
|
49
95
|
|
|
96
|
+
/**
|
|
97
|
+
* The Brain debounces its writes by 500ms (right for a long-lived server), and process.exit()
|
|
98
|
+
* below drops pending timers — so without an explicit flush every memory this script stored was
|
|
99
|
+
* lost the moment it exited. Only flush when the Brain was actually loaded.
|
|
100
|
+
*/
|
|
101
|
+
function flushBrain() {
|
|
102
|
+
try {
|
|
103
|
+
const modPath = require.resolve(path.join(__dirname, '..', 'dist', 'cjs', 'subconscious', 'TheBrainV2.js'));
|
|
104
|
+
if (!require.cache[modPath]) return;
|
|
105
|
+
require(modPath).getBrain().flushSync();
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error('[lemma-brain-ingest] flush failed:', err && err.message);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
50
111
|
try {
|
|
51
112
|
main();
|
|
52
113
|
} catch (err) {
|
|
53
114
|
// A hook that fails must never be the reason a commit or session goes bad.
|
|
54
115
|
console.error('[lemma-brain-ingest] Skipped:', err && err.message);
|
|
55
116
|
}
|
|
117
|
+
flushBrain();
|
|
56
118
|
process.exit(0);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installs the git hooks that feed the Brain without anyone remembering to: post-commit ingests
|
|
3
|
+
* the new commit, post-merge/post-checkout pull the team's shared memories (`.lemma-brain/`).
|
|
4
|
+
*
|
|
5
|
+
* `lemma-brain-ingest` shipped with a post-commit recipe in its header comment and nothing ever
|
|
6
|
+
* installed it — on the author's own machine the corpus held 0 commit memories. This is the
|
|
7
|
+
* missing install step, with the constraints a hook installer owes the repo it touches:
|
|
8
|
+
*
|
|
9
|
+
* - Append, never overwrite. An existing hook keeps running exactly as before; Lemma's part is
|
|
10
|
+
* one delimited block, and re-running init replaces that block in place (idempotent).
|
|
11
|
+
* - Never break a commit. Every line is guarded by `command -v`, backgrounded, and silenced.
|
|
12
|
+
* - Respect `core.hooksPath`. `git rev-parse --git-path hooks` resolves it; when it points inside
|
|
13
|
+
* the working tree (husky and friends — versioned hooks), the files are the team's, so they are
|
|
14
|
+
* left alone and the caller gets the lines to add instead.
|
|
15
|
+
*/
|
|
16
|
+
export interface HookInstallResult {
|
|
17
|
+
installed: string[];
|
|
18
|
+
updated: string[];
|
|
19
|
+
unchanged: string[];
|
|
20
|
+
/** Set when hooks live in a versioned directory Lemma won't write to; holds what to add by hand. */
|
|
21
|
+
manual?: {
|
|
22
|
+
hooksDir: string;
|
|
23
|
+
snippet: string;
|
|
24
|
+
};
|
|
25
|
+
skippedReason?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function installBrainHooks(cwd?: string): HookInstallResult;
|
|
28
|
+
//# sourceMappingURL=gitHooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitHooks.d.ts","sourceRoot":"","sources":["../../../src/cli/gitHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAmBH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,oGAAoG;IACpG,MAAM,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAcD,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAsB,GAAG,iBAAiB,CA0DhF"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Installs the git hooks that feed the Brain without anyone remembering to: post-commit ingests
|
|
4
|
+
* the new commit, post-merge/post-checkout pull the team's shared memories (`.lemma-brain/`).
|
|
5
|
+
*
|
|
6
|
+
* `lemma-brain-ingest` shipped with a post-commit recipe in its header comment and nothing ever
|
|
7
|
+
* installed it — on the author's own machine the corpus held 0 commit memories. This is the
|
|
8
|
+
* missing install step, with the constraints a hook installer owes the repo it touches:
|
|
9
|
+
*
|
|
10
|
+
* - Append, never overwrite. An existing hook keeps running exactly as before; Lemma's part is
|
|
11
|
+
* one delimited block, and re-running init replaces that block in place (idempotent).
|
|
12
|
+
* - Never break a commit. Every line is guarded by `command -v`, backgrounded, and silenced.
|
|
13
|
+
* - Respect `core.hooksPath`. `git rev-parse --git-path hooks` resolves it; when it points inside
|
|
14
|
+
* the working tree (husky and friends — versioned hooks), the files are the team's, so they are
|
|
15
|
+
* left alone and the caller gets the lines to add instead.
|
|
16
|
+
*/
|
|
17
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
18
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.installBrainHooks = installBrainHooks;
|
|
22
|
+
const fs_1 = __importDefault(require("fs"));
|
|
23
|
+
const path_1 = __importDefault(require("path"));
|
|
24
|
+
const child_process_1 = require("child_process");
|
|
25
|
+
const BEGIN = '# >>> lemma brain hooks >>>';
|
|
26
|
+
const END = '# <<< lemma brain hooks <<<';
|
|
27
|
+
const HOOK_BODIES = {
|
|
28
|
+
'post-commit': 'command -v lemma-brain-ingest >/dev/null 2>&1 && (lemma-brain-ingest "$(git rev-parse --show-toplevel)" >/dev/null 2>&1 &)',
|
|
29
|
+
'post-merge': 'command -v lemma-brain-ingest >/dev/null 2>&1 && (lemma-brain-ingest "$(git rev-parse --show-toplevel)" --team-pull --no-commits >/dev/null 2>&1 &)',
|
|
30
|
+
// $3 is 1 for a branch checkout, 0 for a file checkout — only a branch switch can change the shared set.
|
|
31
|
+
'post-checkout': '[ "$3" = "1" ] && command -v lemma-brain-ingest >/dev/null 2>&1 && (lemma-brain-ingest "$(git rev-parse --show-toplevel)" --team-pull --no-commits >/dev/null 2>&1 &)',
|
|
32
|
+
};
|
|
33
|
+
function git(cwd, args) {
|
|
34
|
+
try {
|
|
35
|
+
return (0, child_process_1.execFileSync)('git', ['-C', cwd, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function block(hook) {
|
|
42
|
+
return `${BEGIN}\n${HOOK_BODIES[hook]}\n${END}\n`;
|
|
43
|
+
}
|
|
44
|
+
function installBrainHooks(cwd = process.cwd()) {
|
|
45
|
+
const result = { installed: [], updated: [], unchanged: [] };
|
|
46
|
+
const top = git(cwd, ['rev-parse', '--show-toplevel']);
|
|
47
|
+
const hooksRel = git(cwd, ['rev-parse', '--git-path', 'hooks']);
|
|
48
|
+
if (!top || !hooksRel) {
|
|
49
|
+
result.skippedReason = 'not a git repository';
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
const hooksDir = path_1.default.resolve(cwd, hooksRel);
|
|
53
|
+
// Inside the working tree and not under .git → a versioned hooks dir (husky etc.). Compared
|
|
54
|
+
// on realpaths: git reports the toplevel resolved (/private/var/… on macOS) while hooksDir is
|
|
55
|
+
// built from the cwd as given (/var/…), and the two would otherwise look unrelated.
|
|
56
|
+
const real = (p) => { try {
|
|
57
|
+
return fs_1.default.realpathSync(p);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return p;
|
|
61
|
+
} };
|
|
62
|
+
const rel = path_1.default.relative(real(top), real(hooksDir));
|
|
63
|
+
const insideTree = !rel.startsWith('..') && !path_1.default.isAbsolute(rel);
|
|
64
|
+
const underGitDir = rel === '.git' || rel.startsWith(`.git${path_1.default.sep}`);
|
|
65
|
+
if (insideTree && !underGitDir) {
|
|
66
|
+
result.manual = {
|
|
67
|
+
hooksDir: rel,
|
|
68
|
+
snippet: Object.keys(HOOK_BODIES).map((h) => `# ${rel}/${h}\n${HOOK_BODIES[h]}`).join('\n\n'),
|
|
69
|
+
};
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
fs_1.default.mkdirSync(hooksDir, { recursive: true });
|
|
73
|
+
for (const hook of Object.keys(HOOK_BODIES)) {
|
|
74
|
+
const file = path_1.default.join(hooksDir, hook);
|
|
75
|
+
let current = null;
|
|
76
|
+
try {
|
|
77
|
+
current = fs_1.default.readFileSync(file, 'utf8');
|
|
78
|
+
}
|
|
79
|
+
catch { /* no hook yet */ }
|
|
80
|
+
let next;
|
|
81
|
+
if (current === null) {
|
|
82
|
+
next = `#!/bin/sh\n${block(hook)}`;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const start = current.indexOf(BEGIN);
|
|
86
|
+
const end = current.indexOf(END);
|
|
87
|
+
if (start !== -1 && end > start) {
|
|
88
|
+
next = current.slice(0, start) + block(hook) + current.slice(end + END.length).replace(/^\n/, '');
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Appended after the existing script. A hook that ends in `exit 0` would never reach
|
|
92
|
+
// it — insert before a trailing bare exit instead of after it.
|
|
93
|
+
const trailingExit = current.match(/\n(exit\s+\d+\s*)$/);
|
|
94
|
+
next = trailingExit
|
|
95
|
+
? current.slice(0, trailingExit.index + 1) + block(hook) + trailingExit[1]
|
|
96
|
+
: `${current}${current.endsWith('\n') ? '' : '\n'}${block(hook)}`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (next === current) {
|
|
100
|
+
result.unchanged.push(hook);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
fs_1.default.writeFileSync(file, next, 'utf8');
|
|
104
|
+
try {
|
|
105
|
+
fs_1.default.chmodSync(file, 0o755);
|
|
106
|
+
}
|
|
107
|
+
catch { /* Windows: git for Windows runs hooks via sh regardless */ }
|
|
108
|
+
(current === null ? result.installed : result.updated).push(hook);
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=gitHooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitHooks.js","sourceRoot":"","sources":["../../../src/cli/gitHooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lemma-proxy.d.ts","sourceRoot":"","sources":["../../../src/cli/lemma-proxy.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"lemma-proxy.d.ts","sourceRoot":"","sources":["../../../src/cli/lemma-proxy.ts"],"names":[],"mappings":";AA+cA,wBAAgB,+BAA+B,CAAC,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CA8DvF;AAqfD,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGtD;AAmsDD,wBAAsB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA8KhL"}
|
|
@@ -57,6 +57,7 @@ const ProjectRegistry_1 = require("../proxy/ProjectRegistry");
|
|
|
57
57
|
const ContextInjector_1 = require("../proxy/ContextInjector");
|
|
58
58
|
const SavingsLedger_1 = require("../utils/SavingsLedger");
|
|
59
59
|
const ProjectStore_1 = require("../proxy/ProjectStore");
|
|
60
|
+
const gitHooks_1 = require("./gitHooks");
|
|
60
61
|
const chroma = new chromadb_1.ChromaClient({ host: 'localhost', port: 8000 });
|
|
61
62
|
const dummyEmbeddingFunction = { generate: (texts) => Promise.resolve([]) };
|
|
62
63
|
const complexityRouter = new ComplexityRouter_1.default();
|
|
@@ -2503,6 +2504,7 @@ commander_1.program.command('init')
|
|
|
2503
2504
|
.description('Initialize Lemma in the current project (auto-discovery)')
|
|
2504
2505
|
.option('--no-configure', 'Skip automatic configuration of editors, shell overrides, and Claude Desktop')
|
|
2505
2506
|
.option('--yes', 'Skip prompts and accept defaults')
|
|
2507
|
+
.option('--no-hooks', 'Skip installing the git hooks that feed the Brain (post-commit, post-merge, post-checkout)')
|
|
2506
2508
|
.action(async (opts) => {
|
|
2507
2509
|
const project = detectProject();
|
|
2508
2510
|
console.log(`\n🛠️ Initializing Lemma for [${project}]...`);
|
|
@@ -2516,6 +2518,16 @@ commander_1.program.command('init')
|
|
|
2516
2518
|
if (ensureGitIgnore()) {
|
|
2517
2519
|
console.log('✅ Updated .gitignore with Lemma ignore rules');
|
|
2518
2520
|
}
|
|
2521
|
+
if (opts.hooks !== false) {
|
|
2522
|
+
const hooks = (0, gitHooks_1.installBrainHooks)(process.cwd());
|
|
2523
|
+
const changed = [...hooks.installed, ...hooks.updated];
|
|
2524
|
+
if (hooks.manual) {
|
|
2525
|
+
console.log(`⚠️ Git hooks live in versioned ${hooks.manual.hooksDir}/ — not touching them. To feed the Brain, add:\n${hooks.manual.snippet}`);
|
|
2526
|
+
}
|
|
2527
|
+
else if (changed.length > 0) {
|
|
2528
|
+
console.log(`✅ Git hooks feed the Brain (${changed.join(', ')}): commits become memories, merges pull .lemma-brain/`);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2519
2531
|
const envFile = path_1.default.join(process.cwd(), '.env');
|
|
2520
2532
|
const lemmaConfig = `\n# Lemma AI Gateway Configuration\nOPENAI_BASE_URL=http://localhost:8081/v1\nLEMMA_PROJECT=${project}\n`;
|
|
2521
2533
|
if (fs_1.default.existsSync(envFile)) {
|