@nxuss/lemma 1.27.1 → 1.29.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.
Files changed (83) hide show
  1. package/README.md +61 -15
  2. package/bin/brain-ingest.js +64 -2
  3. package/dashboard/dist/assets/index-Dh-whqJ7.js +481 -0
  4. package/dashboard/dist/assets/index-Dh-whqJ7.js.map +1 -0
  5. package/dashboard/dist/assets/{index-DtEKr0hI.css → index-Yt1H2B7F.css} +1 -1
  6. package/dashboard/dist/index.html +2 -2
  7. package/dist/cjs/cli/gitHooks.d.ts +28 -0
  8. package/dist/cjs/cli/gitHooks.d.ts.map +1 -0
  9. package/dist/cjs/cli/gitHooks.js +112 -0
  10. package/dist/cjs/cli/gitHooks.js.map +1 -0
  11. package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
  12. package/dist/cjs/cli/lemma-proxy.js +26 -0
  13. package/dist/cjs/cli/lemma-proxy.js.map +1 -1
  14. package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -1
  15. package/dist/cjs/mcp/tool-profiles.js +8 -0
  16. package/dist/cjs/mcp/tool-profiles.js.map +1 -1
  17. package/dist/cjs/mcp/tools/memory.d.ts.map +1 -1
  18. package/dist/cjs/mcp/tools/memory.js +57 -0
  19. package/dist/cjs/mcp/tools/memory.js.map +1 -1
  20. package/dist/cjs/mcp/tools/misc.d.ts.map +1 -1
  21. package/dist/cjs/mcp/tools/misc.js +16 -1
  22. package/dist/cjs/mcp/tools/misc.js.map +1 -1
  23. package/dist/cjs/mcp/tools/workspace.d.ts.map +1 -1
  24. package/dist/cjs/mcp/tools/workspace.js +17 -3
  25. package/dist/cjs/mcp/tools/workspace.js.map +1 -1
  26. package/dist/cjs/mcp/tools.d.ts.map +1 -1
  27. package/dist/cjs/mcp/tools.js +2 -0
  28. package/dist/cjs/mcp/tools.js.map +1 -1
  29. package/dist/cjs/subconscious/GitIngest.d.ts.map +1 -1
  30. package/dist/cjs/subconscious/GitIngest.js +49 -1
  31. package/dist/cjs/subconscious/GitIngest.js.map +1 -1
  32. package/dist/cjs/subconscious/TeamBrain.d.ts +82 -0
  33. package/dist/cjs/subconscious/TeamBrain.d.ts.map +1 -0
  34. package/dist/cjs/subconscious/TeamBrain.js +543 -0
  35. package/dist/cjs/subconscious/TeamBrain.js.map +1 -0
  36. package/dist/cjs/subconscious/TheBrainV2.d.ts +19 -2
  37. package/dist/cjs/subconscious/TheBrainV2.d.ts.map +1 -1
  38. package/dist/cjs/subconscious/TheBrainV2.js +23 -0
  39. package/dist/cjs/subconscious/TheBrainV2.js.map +1 -1
  40. package/dist/cjs/utils/FixCapture.d.ts +66 -0
  41. package/dist/cjs/utils/FixCapture.d.ts.map +1 -0
  42. package/dist/cjs/utils/FixCapture.js +338 -0
  43. package/dist/cjs/utils/FixCapture.js.map +1 -0
  44. package/dist/esm/cli/gitHooks.d.ts +28 -0
  45. package/dist/esm/cli/gitHooks.d.ts.map +1 -0
  46. package/dist/esm/cli/gitHooks.js +106 -0
  47. package/dist/esm/cli/gitHooks.js.map +1 -0
  48. package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
  49. package/dist/esm/cli/lemma-proxy.js +26 -0
  50. package/dist/esm/cli/lemma-proxy.js.map +1 -1
  51. package/dist/esm/mcp/tool-profiles.d.ts.map +1 -1
  52. package/dist/esm/mcp/tool-profiles.js +8 -0
  53. package/dist/esm/mcp/tool-profiles.js.map +1 -1
  54. package/dist/esm/mcp/tools/memory.d.ts.map +1 -1
  55. package/dist/esm/mcp/tools/memory.js +57 -0
  56. package/dist/esm/mcp/tools/memory.js.map +1 -1
  57. package/dist/esm/mcp/tools/misc.d.ts.map +1 -1
  58. package/dist/esm/mcp/tools/misc.js +16 -1
  59. package/dist/esm/mcp/tools/misc.js.map +1 -1
  60. package/dist/esm/mcp/tools/workspace.d.ts.map +1 -1
  61. package/dist/esm/mcp/tools/workspace.js +17 -3
  62. package/dist/esm/mcp/tools/workspace.js.map +1 -1
  63. package/dist/esm/mcp/tools.d.ts.map +1 -1
  64. package/dist/esm/mcp/tools.js +2 -0
  65. package/dist/esm/mcp/tools.js.map +1 -1
  66. package/dist/esm/subconscious/GitIngest.d.ts.map +1 -1
  67. package/dist/esm/subconscious/GitIngest.js +49 -1
  68. package/dist/esm/subconscious/GitIngest.js.map +1 -1
  69. package/dist/esm/subconscious/TeamBrain.d.ts +82 -0
  70. package/dist/esm/subconscious/TeamBrain.d.ts.map +1 -0
  71. package/dist/esm/subconscious/TeamBrain.js +533 -0
  72. package/dist/esm/subconscious/TeamBrain.js.map +1 -0
  73. package/dist/esm/subconscious/TheBrainV2.d.ts +19 -2
  74. package/dist/esm/subconscious/TheBrainV2.d.ts.map +1 -1
  75. package/dist/esm/subconscious/TheBrainV2.js +23 -0
  76. package/dist/esm/subconscious/TheBrainV2.js.map +1 -1
  77. package/dist/esm/utils/FixCapture.d.ts +66 -0
  78. package/dist/esm/utils/FixCapture.d.ts.map +1 -0
  79. package/dist/esm/utils/FixCapture.js +328 -0
  80. package/dist/esm/utils/FixCapture.js.map +1 -0
  81. package/package.json +1 -1
  82. package/dashboard/dist/assets/index-DiidVeI5.js +0 -481
  83. package/dashboard/dist/assets/index-DiidVeI5.js.map +0 -1
package/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@nxuss/lemma.svg?color=orange&style=flat-square)](https://www.npmjs.com/package/@nxuss/lemma)
4
4
  [![License](https://img.shields.io/npm/l/@nxuss/lemma.svg?color=blue&style=flat-square)](https://github.com/Nxusbets/lemma/blob/main/LICENSE)
5
- [![MCP Tools](https://img.shields.io/badge/MCP%20tools-109-ff69b4?style=flat-square)](https://github.com/Nxusbets/lemma)
5
+ [![MCP Tools](https://img.shields.io/badge/MCP%20tools-110-ff69b4?style=flat-square)](https://github.com/Nxusbets/lemma)
6
6
  [![Security](https://img.shields.io/badge/privacy-firewall%20active-blueviolet?style=flat-square)](https://github.com/Nxusbets/lemma)
7
7
  [![Caching](https://img.shields.io/badge/semantic%20memory-enabled-success?style=flat-square)](https://github.com/Nxusbets/lemma)
8
- [![Tests](https://img.shields.io/badge/tests-919%20passing-00c853?style=flat-square)](https://github.com/Nxusbets/lemma)
8
+ [![Tests](https://img.shields.io/badge/tests-964%20passing-00c853?style=flat-square)](https://github.com/Nxusbets/lemma)
9
9
 
10
10
  ```text
11
11
  __
@@ -35,6 +35,25 @@ 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.29.0 — the dashboard shows what the Brain knows.** `lemma start` now opens with a
39
+ > 🧠 Brain panel: how many files of your codebase it carries evidence about, how many times a
40
+ > memory reached an agent's context, growth this week, how much of it is verifiable against the
41
+ > code, where memories come from (manual, red → green fixes, tests, commits, team), and the most
42
+ > recalled ones. Token savings stay as a footnote — the Brain's value is agents not re-discovering
43
+ > context. Same numbers in `brain_stats` and at `GET /api/brain/stats`.
44
+ >
45
+ > **🆕 v1.28.0 — the Brain goes team-wide through git, and fills itself.** `brain_team push`
46
+ > writes your verified memories into the repo (`.lemma-brain/memories/`, one reviewable JSON file
47
+ > each, paths relative to the repo root); `brain_team pull` — automatic on merge/checkout once
48
+ > `lemma init` installs the hooks — imports a teammate's memories rebased onto *your* checkout. A
49
+ > memory about a file that is byte-identical on your machine arrives **fresh**; one whose file has
50
+ > moved on arrives **stale**, never silently trusted. And memories no longer depend on the model
51
+ > remembering `store_memory`: a test/build/lint command that goes **red → green** is captured as
52
+ > `fix: <the error>` + the files and diff that fixed it — searchable by the exact error message next
53
+ > time — whether it ran through `run_workspace_command` or the agent's own shell (Claude Code
54
+ > `PostToolUse` hook). Commits become memories anchored to their own blobs. Also fixes
55
+ > `lemma-brain-ingest` silently dropping everything it ingested.
56
+ >
38
57
  > **🆕 v1.26.0 — a memory without evidence is no longer sold as a verified hit, and the Brain
39
58
  > repairs it on the spot.** `search_memory` now splits its results into three buckets — `fresh`
40
59
  > (tracked + verified, the only ones credited `tokensSaved`/`hits`), `stale`, and `UNTRACKED`
@@ -64,7 +83,7 @@ lemma start # gateway + dashboard on http://localhost:8081
64
83
  the code they're about to touch has dependent memories — including a documented failed
65
84
  attempt — instead of only finding out on the next search. Ranking factors in
66
85
  real usage (`hits`), explicit negative feedback (`downvote_memory`), and `cognitive_map` domain
67
- tags; a passing `test_oracle` run over an uncommitted diff auto-captures itself; two memories
86
+ tags; a command that goes red → green auto-captures the fix (see below); two memories
68
87
  about the same symbol that disagree on outcome are flagged, not silently both suggested.
69
88
  Every hit explains itself (`whyShown` ranking signals + cited `evidence` files/symbols), and
70
89
  every reuse books its savings onto the memory itself — `brain_stats` and `brain_upkeep` name
@@ -72,6 +91,27 @@ lemma start # gateway + dashboard on http://localhost:8081
72
91
  it, never one without the other). `store_memory` derives `filePaths`/`symbols` by default from
73
92
  whatever the session already read, wrote, or patched when the caller doesn't pass them —
74
93
  flagged as auto-derived in the response, correctable, but no longer opt-in.
94
+ - **A team Brain, shared through git** — `brain_team push` writes this project's fresh, tracked
95
+ memories to `.lemma-brain/memories/<id>.json`: one sorted-key JSON file per memory, so two people
96
+ sharing at once never conflict, every shared memory goes through code review, and deleting one is
97
+ a `git rm`. Evidence is stored relative to the repo root and rebased onto each clone on `pull`,
98
+ hashes untouched — a teammate's memory is verified against *your* files, fresh only if they are
99
+ byte-identical. Never shared: conversation checkpoints, commit memories (each clone derives its
100
+ own), untracked or stale memories, evidence outside the repo, or anything derived from a memory
101
+ that isn't shared. A pulled path that is absolute or escapes the repo is rejected; a memory a
102
+ teammate deletes leaves your clone into `brain_trash` (restorable), and one you wrote yourself is
103
+ never removed. Pushing is always explicit; pulling is a no-op when nothing changed, so it runs on
104
+ every merge/checkout. `brain_team status` shows both sides; `dryRun:true` previews either.
105
+ - **Memories that write themselves** — a verdict command (jest, vitest, pytest, tsc, eslint, `go
106
+ test`, `cargo test`, `npm test`/`build`/`lint`, …) that fails and later passes stores
107
+ `fix: <headline error>` with the failure signature, the files whose content changed between the
108
+ two runs, and their diff — tracked against those files, tagged `confirmed`, source `auto-fix`.
109
+ A pass with nothing changed since the failure is treated as flaky and stores nothing; a "fix" over
110
+ 15 files is a refactor and stores nothing. It sees `run_workspace_command`, `test_oracle`, and — via
111
+ `lemma-brain-ingest --hook-result` — the agent's own Bash tool. Commits are ingested with each
112
+ touched file anchored to its blob *in that commit* (`git show sha:path`), not to the working copy.
113
+ `lemma init` installs the `post-commit`, `post-merge` and `post-checkout` hooks that drive this
114
+ (appended to existing hooks, idempotent, never touches a versioned `core.hooksPath` like husky).
75
115
  - **A memory that maintains itself** — `brain_upkeep` revalidates the most-reused memories,
76
116
  surfaces exact duplicates and merge candidates, dead weight, and savings, each finding with its
77
117
  fix. Since v1.26.0 it also **auto-anchors** evidence-free memories whose body names real repo
@@ -106,7 +146,7 @@ lemma start # gateway + dashboard on http://localhost:8081
106
146
  compiler-verified AST location instead of asking the model to output a full-file rewrite.
107
147
  - **Infrastructure Command Center** — 22 free tools for Jenkins, OKD/OpenShift, and Terraform:
108
148
  discover, connect, deploy, diagnose, and detect drift, all via MCP.
109
- - **Scoped tool surface** — of the 109 registered tools, only the ones your project shows evidence
149
+ - **Scoped tool surface** — of the 110 registered tools, only the ones your project shows evidence
110
150
  of needing are advertised to the model each turn; the rest stay one `lemma_toolbox` call away.
111
151
 
112
152
  See [`CHANGELOG.md`](CHANGELOG.md) for the full, per-version history.
@@ -115,14 +155,14 @@ See [`CHANGELOG.md`](CHANGELOG.md) for the full, per-version history.
115
155
 
116
156
  ## 💎 Free vs Pro
117
157
 
118
- 59 of the 109 registered tools work with no license; 50 require Pro. Enforced by one allowlist in
158
+ 60 of the 110 registered tools work with no license; 50 require Pro. Enforced by one allowlist in
119
159
  code (`FREE_TOOLS` in `src/mcp/tools.ts`, including all 22 infrastructure tools), not by narrative.
120
160
  A session without a license doesn't even advertise the Pro tools — they stay registered and
121
161
  discoverable via `lemma_toolbox`.
122
162
 
123
- | | Free (59) | Pro (50) |
163
+ | | Free (60) | Pro (50) |
124
164
  | :--- | :--- | :--- |
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` |
165
+ | **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
166
  | **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
167
  | **Writing & workspace** | — | `write_workspace_file`, `apply_workspace_patch`, `multi_file_patch`, `search_workspace`, `run_workspace_command`, `validate_patch_sandbox`, `surgical_ast_insert` |
128
168
  | **Static analysis** | — | `entropy_score`, `coupling_radar`, `pattern_fossil`, `precrime_static`, `dead_export_necromancer`, `depgraph`, `refactor`, `ast_flow_visualizer`, `cognitive_map` |
@@ -137,8 +177,8 @@ Full per-tool listing: `lemma_toolbox { "action": "list" }`.
137
177
 
138
178
  ## 🎚️ Tool profiles
139
179
 
140
- Every advertised tool schema is re-sent on **every request** of the session — advertising all 109
141
- costs ~11,600 tokens/turn. Lemma advertises a scoped surface instead: core tools always, situational
180
+ Every advertised tool schema is re-sent on **every request** of the session — advertising all 110
181
+ costs ~11,900 tokens/turn. Lemma advertises a scoped surface instead: core tools always, situational
142
182
  groups (e.g. infrastructure) only when the repo shows evidence for them. Nothing is removed —
143
183
  everything stays registered and reachable via `lemma_toolbox`.
144
184
 
@@ -161,15 +201,16 @@ lemma_toolbox { "action": "call", "tool": "terraform_drift", "args": {} }
161
201
 
162
202
  | Command | Action |
163
203
  | :--- | :--- |
164
- | `lemma init` | Bootstraps config and auto-configures MCP for OpenCode, Claude Desktop, Cursor, Windsurf, Kiro, VS Code. |
165
- | `lemma start` | Launches proxy, cache engine, and dashboard at `http://localhost:8081`. |
204
+ | `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). |
205
+ | `lemma start` | Launches proxy, cache engine, and dashboard at `http://localhost:8081` (gateway savings + Brain knowledge panel). |
166
206
  | `lemma stop` / `lemma status` / `lemma stats` | Stop, health check, and net token/cost report (reports `NET COST` when a session is underwater). |
167
207
  | `lemma mcp` | Starts the stdin/stdout MCP server for IDE integration. |
168
208
  | `lemma heal` / `lemma autopilot` / `lemma watch` | Diagnose+repair the last crash / toggle autonomous fix-apply / silent patch-only watcher. |
169
209
  | `lemma patches` / `lemma apply <id>` / `lemma discard <id>` | List, apply, or discard a pending Silent Patch Watcher fix. |
170
210
  | `lemma activate <key>` | Activate a Pro license. |
171
211
  | `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. |
212
+ | `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/`. |
213
+ | `lemma-brain-ingest --hook-result` | Read a host `PostToolUse` payload on stdin and capture red → green fixes from the agent's own shell. |
173
214
 
174
215
  Wire the standalone bins into hooks so memory doesn't depend on the assistant remembering to call a tool:
175
216
 
@@ -179,11 +220,16 @@ Wire the standalone bins into hooks so memory doesn't depend on the assistant re
179
220
  "command": "lemma-checkpoint \"${CLAUDE_PROJECT_DIR:-$PWD}\"" }] }] } }
180
221
  ```
181
222
 
182
- ```bash
183
- echo 'lemma-brain-ingest "$(git rev-parse --show-toplevel)" &' >> .git/hooks/post-commit
184
- chmod +x .git/hooks/post-commit
223
+ ```jsonc
224
+ // .claude/settings.json — red → green fixes from Claude Code's own Bash tool become memories
225
+ { "hooks": { "PostToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command",
226
+ "command": "lemma-brain-ingest --hook-result" }] }] } }
185
227
  ```
186
228
 
229
+ The git side needs nothing by hand: `lemma init` installs `post-commit` (commit → memory) and
230
+ `post-merge`/`post-checkout` (pull the team's `.lemma-brain/`). With a versioned hooks dir
231
+ (`core.hooksPath`, e.g. husky) it prints the lines to add instead of editing team files.
232
+
187
233
  Customize via `lemma.config.json`:
188
234
 
189
235
  ```json
@@ -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 positional = args.filter((a, i) => !a.startsWith('--') && i !== maxIndex + 1);
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);