@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FixCapture.d.ts","sourceRoot":"","sources":["../../../src/utils/FixCapture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,OAAO,EAA6B,UAAU,EAAE,MAAM,4BAA4B,CAAC;AA8BnF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAuBzD;AAaD,MAAM,WAAW,cAAc;IAC7B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AA2DD,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAyBtF;AAwBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iGAAiG;IACjG,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,cAAc,CA6EjB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAchF;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAU3D"}
@@ -0,0 +1,338 @@
1
+ "use strict";
2
+ /**
3
+ * FixCapture — turns a red → green transition into a memory, with nobody asking.
4
+ *
5
+ * The auto-capture that existed before this (test_oracle's "tests passing after changes to
6
+ * a.ts") stored the one thing nobody searches for: that a suite passed. What a later session
7
+ * actually has in hand is the *error* — CLAUDE.md tells it to search_memory with the error
8
+ * message — and the answer it needs is "this exact failure was fixed by changing these files".
9
+ * That pairing only exists across two runs of the same command, so this module keeps the
10
+ * failing run's signature and the hash of every dirty file at that moment, and when the same
11
+ * command next passes, the files whose content moved in between are the fix.
12
+ *
13
+ * State lives on disk (`.lemma/session/fix-capture.json`), not in the process: the failing run
14
+ * and the passing one may arrive through different processes — the MCP server's
15
+ * run_workspace_command, or the host's own Bash tool relayed by a PostToolUse hook
16
+ * (`lemma-brain-ingest --hook-result`). Agents mostly run tests through their native shell, so
17
+ * a capture that only saw Lemma's tools would see a fraction of the fixes.
18
+ *
19
+ * It never guesses: a pass with no file changed since the failure was flaky, and is recorded as
20
+ * nothing at all.
21
+ */
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.isVerdictCommand = isVerdictCommand;
27
+ exports.extractFailure = extractFailure;
28
+ exports.recordCommandOutcome = recordCommandOutcome;
29
+ exports.recordHookPayload = recordHookPayload;
30
+ exports.inferVerdict = inferVerdict;
31
+ const fs_1 = __importDefault(require("fs"));
32
+ const path_1 = __importDefault(require("path"));
33
+ const crypto_1 = __importDefault(require("crypto"));
34
+ const child_process_1 = require("child_process");
35
+ const AffectedTests_1 = require("./AffectedTests");
36
+ const TheBrainV2_1 = require("../subconscious/TheBrainV2");
37
+ const TeamBrain_1 = require("../subconscious/TeamBrain");
38
+ const STATE_FILE = path_1.default.join('.lemma', 'session', 'fix-capture.json');
39
+ /** A failure older than this is no longer the thing the next pass fixed. */
40
+ const PENDING_TTL_MS = 24 * 60 * 60 * 1000;
41
+ const MAX_SIGNATURE_LINES = 20;
42
+ const MAX_SIGNATURE_CHARS = 1500;
43
+ const MAX_DIFF_CHARS = 2500;
44
+ /** A "fix" touching more files than this is a refactor or a branch switch, not a fix worth recalling. */
45
+ const MAX_FIX_FILES = 15;
46
+ /** Runners whose exit code is a verdict on the code by themselves. */
47
+ const VERDICT_TOOLS = new Set([
48
+ 'jest', 'vitest', 'mocha', 'ava', 'tap', 'pytest', 'tox', 'tsc', 'vue-tsc', 'eslint', 'biome',
49
+ 'ruff', 'mypy', 'pyright', 'rspec', 'phpunit', 'mvn', 'gradle', 'gradlew', 'playwright', 'cypress',
50
+ ]);
51
+ /** Tools that are a verdict only with the right subcommand (`go test`, not `go run`). */
52
+ const VERDICT_SUBCOMMANDS = {
53
+ go: /^(test|vet|build)$/,
54
+ cargo: /^(test|build|check|clippy|nextest)$/,
55
+ dotnet: /^(test|build)$/,
56
+ swift: /^(test|build)$/,
57
+ make: /^(test|check|lint)$/,
58
+ };
59
+ /** Package-manager scripts that conventionally run a verdict. */
60
+ const VERDICT_SCRIPT_RE = /^(test|build|lint|typecheck|type-check|check|tsc|ci)(:.*)?$/;
61
+ /** Launchers that run the next token as the real command. */
62
+ const LAUNCHERS = new Set(['npx', 'bunx', 'pnpx', 'env', 'time', 'uv', 'poetry']);
63
+ /**
64
+ * Whether a command's pass/fail is a verdict on the code. Judged per shell segment on the
65
+ * command's head, not anywhere in the text: `cat jest.config.js` mentions jest and is no
66
+ * verdict, `CI=1 npx jest` and `cd app && npm test` are.
67
+ */
68
+ function isVerdictCommand(command) {
69
+ for (const segment of (command || '').split(/&&|\|\||;|\|/)) {
70
+ let tokens = segment.trim().split(/\s+/).filter(Boolean);
71
+ while (tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[0]))
72
+ tokens = tokens.slice(1); // FOO=bar
73
+ // Launchers and their run/exec/dlx/-m forms: `npx jest`, `pnpm exec vitest`, `python -m pytest`, `uv run pytest`.
74
+ for (let guard = 0; guard < 4 && tokens.length > 1; guard++) {
75
+ const head = path_1.default.basename(tokens[0]);
76
+ if (LAUNCHERS.has(head)) {
77
+ tokens = tokens.slice(1);
78
+ if (/^(run|x)$/.test(tokens[0] || ''))
79
+ tokens = tokens.slice(1);
80
+ continue;
81
+ }
82
+ if (/^(pnpm|yarn|bun)$/.test(head) && /^(exec|dlx|x)$/.test(tokens[1])) {
83
+ tokens = tokens.slice(2);
84
+ continue;
85
+ }
86
+ if (/^python\d?(\.\d+)?$/.test(head) && tokens[1] === '-m') {
87
+ tokens = tokens.slice(2);
88
+ continue;
89
+ }
90
+ break;
91
+ }
92
+ if (tokens.length === 0)
93
+ continue;
94
+ const head = path_1.default.basename(tokens[0]).replace(/\.(cmd|exe|sh)$/, '');
95
+ if (VERDICT_TOOLS.has(head))
96
+ return true;
97
+ const sub = VERDICT_SUBCOMMANDS[head];
98
+ if (sub && tokens[1] && sub.test(tokens[1]))
99
+ return true;
100
+ if (/^(npm|pnpm|yarn|bun)$/.test(head)) {
101
+ const script = tokens[1] === 'run' || tokens[1] === 'run-script' ? tokens[2] : tokens[1];
102
+ if (script && (VERDICT_SCRIPT_RE.test(script) || script === 't'))
103
+ return true;
104
+ }
105
+ }
106
+ return false;
107
+ }
108
+ function stateKey(command) {
109
+ return command.trim().replace(/\s+/g, ' ');
110
+ }
111
+ function readState(root) {
112
+ try {
113
+ const parsed = JSON.parse(fs_1.default.readFileSync(path_1.default.join(root, STATE_FILE), 'utf8'));
114
+ return parsed && typeof parsed === 'object' ? parsed : {};
115
+ }
116
+ catch {
117
+ return {};
118
+ }
119
+ }
120
+ function writeState(root, state) {
121
+ const p = path_1.default.join(root, STATE_FILE);
122
+ fs_1.default.mkdirSync(path_1.default.dirname(p), { recursive: true });
123
+ fs_1.default.writeFileSync(p, JSON.stringify(state), 'utf8');
124
+ }
125
+ function hashWorkingFile(root, rel) {
126
+ try {
127
+ return crypto_1.default.createHash('sha256').update(fs_1.default.readFileSync(path_1.default.join(root, rel))).digest('hex');
128
+ }
129
+ catch {
130
+ return 'DELETED';
131
+ }
132
+ }
133
+ /** Lemma's own bookkeeping is never "the fix". */
134
+ function isOwnFile(rel) {
135
+ return rel.startsWith('.lemma/') || rel.startsWith('.lemma-brain/');
136
+ }
137
+ function dirtySnapshot(root) {
138
+ const out = {};
139
+ for (const rel of (0, AffectedTests_1.changedFilesFromGit)(root)) {
140
+ if (isOwnFile(rel))
141
+ continue;
142
+ out[rel] = hashWorkingFile(root, rel);
143
+ }
144
+ return out;
145
+ }
146
+ // Order matters: the first pattern that matches anywhere wins the headline, so the most
147
+ // specific, most searchable forms come first (a named JS/Python error, a tsc code, a failing
148
+ // test title) and the generic "error" line last.
149
+ const HEADLINE_PATTERNS = [
150
+ /\b[A-Z]\w*(Error|Exception)\b:?\s+\S.*/,
151
+ /error\s+TS\d+:.*/,
152
+ /^\s*●\s+(?!Console\b).+/,
153
+ /^\s*(✕|×|✗)\s+.+/,
154
+ /^FAILED\s+.+/,
155
+ /^\s*E\s{2,}.+/,
156
+ /\berror(\[E\d+\])?:\s+.+/i,
157
+ ];
158
+ const SIGNATURE_LINE_RE = /(●\s|✕|×|✗|^FAIL\b|^FAILED\b|error\s+TS\d+|\b\w*(Error|Exception)\b|^\s*E\s{2,}|expected|received|assert|panicked|\berror(\[E\d+\])?:)/i;
159
+ /** The failure, compressed to what identifies it — test titles, error lines, their locations. */
160
+ function extractFailure(output) {
161
+ const lines = (output || '').split('\n').map((l) => l.replace(/\s+$/, ''));
162
+ let headline = '';
163
+ for (const re of HEADLINE_PATTERNS) {
164
+ for (const line of lines) {
165
+ const m = line.match(re);
166
+ if (m) {
167
+ headline = m[0].trim();
168
+ break;
169
+ }
170
+ }
171
+ if (headline)
172
+ break;
173
+ }
174
+ const picked = [];
175
+ const seen = new Set();
176
+ for (const line of lines) {
177
+ const t = line.trim();
178
+ if (!t || seen.has(t) || /node_modules/.test(t))
179
+ continue;
180
+ if (!SIGNATURE_LINE_RE.test(t))
181
+ continue;
182
+ seen.add(t);
183
+ picked.push(t);
184
+ if (picked.length >= MAX_SIGNATURE_LINES)
185
+ break;
186
+ }
187
+ let signature = picked.join('\n');
188
+ if (!signature)
189
+ signature = lines.filter(Boolean).slice(-MAX_SIGNATURE_LINES).join('\n');
190
+ if (signature.length > MAX_SIGNATURE_CHARS)
191
+ signature = signature.slice(0, MAX_SIGNATURE_CHARS) + '\n…';
192
+ if (!headline)
193
+ headline = (picked[0] || lines.find((l) => l.trim()) || 'command failed').trim();
194
+ return { headline: headline.slice(0, 200), signature };
195
+ }
196
+ function diffFor(root, files) {
197
+ let diff = '';
198
+ try {
199
+ diff = (0, child_process_1.execFileSync)('git', ['-C', root, 'diff', '--no-color', 'HEAD', '--', ...files], {
200
+ encoding: 'utf8',
201
+ stdio: ['ignore', 'pipe', 'ignore'],
202
+ maxBuffer: 8 * 1024 * 1024,
203
+ });
204
+ }
205
+ catch { /* no HEAD yet, or not a repo — the file list still stands */ }
206
+ if (diff.length > MAX_DIFF_CHARS)
207
+ diff = diff.slice(0, MAX_DIFF_CHARS) + '\n… (diff truncated)';
208
+ return diff.trim();
209
+ }
210
+ function isGitRepo(root) {
211
+ try {
212
+ (0, child_process_1.execFileSync)('git', ['-C', root, 'rev-parse', '--git-dir'], { stdio: 'ignore' });
213
+ return true;
214
+ }
215
+ catch {
216
+ return false;
217
+ }
218
+ }
219
+ /**
220
+ * Feed one finished run of a command. Returns a note to append to the tool result (empty when
221
+ * the command isn't a verdict command or nothing happened worth mentioning).
222
+ */
223
+ function recordCommandOutcome(input) {
224
+ const command = input.command || '';
225
+ if (!input.verdict && !isVerdictCommand(command))
226
+ return { note: '' };
227
+ const root = (0, TeamBrain_1.repoRoot)(input.cwd || process.cwd());
228
+ // Without git there's no way to tell which files the fix touched.
229
+ if (!isGitRepo(root))
230
+ return { note: '' };
231
+ const now = input.now ?? Date.now();
232
+ const key = stateKey(command);
233
+ const state = readState(root);
234
+ for (const [k, p] of Object.entries(state)) {
235
+ if (now - p.failedAt > PENDING_TTL_MS)
236
+ delete state[k];
237
+ }
238
+ if (!input.passed) {
239
+ const { headline, signature } = extractFailure(input.output);
240
+ // Keep the FIRST failure's dirty snapshot across repeated red runs: the fix is everything
241
+ // that changed since the suite first went red, not since the last of several attempts.
242
+ const prior = state[key];
243
+ state[key] = {
244
+ command: key,
245
+ failedAt: prior ? prior.failedAt : now,
246
+ headline,
247
+ signature,
248
+ dirty: prior ? prior.dirty : dirtySnapshot(root),
249
+ };
250
+ writeState(root, state);
251
+ return { note: '', pending: true };
252
+ }
253
+ const pending = state[key];
254
+ if (!pending) {
255
+ writeState(root, state);
256
+ return { note: '' };
257
+ }
258
+ delete state[key];
259
+ writeState(root, state);
260
+ // The fix = files whose content differs from what it was when the run went red: edited,
261
+ // newly created, deleted, or reverted back to HEAD.
262
+ const current = dirtySnapshot(root);
263
+ const candidates = new Set([...Object.keys(current), ...Object.keys(pending.dirty)]);
264
+ const fixFiles = [...candidates]
265
+ .filter((rel) => (current[rel] ?? hashWorkingFile(root, rel)) !== pending.dirty[rel])
266
+ .sort();
267
+ if (fixFiles.length === 0) {
268
+ return { note: `\n\n[Lemma] \`${key}\` passed with no file changed since it failed — looks flaky, so nothing was stored.` };
269
+ }
270
+ if (fixFiles.length > MAX_FIX_FILES) {
271
+ return { note: `\n\n[Lemma] \`${key}\` went green, but ${fixFiles.length} files changed since it failed — too broad to record as one fix, nothing stored.` };
272
+ }
273
+ const diff = diffFor(root, fixFiles);
274
+ const query = `fix: ${pending.headline}`;
275
+ const response = [
276
+ `\`${key}\` was failing with:`,
277
+ pending.signature,
278
+ '',
279
+ `It passed after changes to:`,
280
+ ...fixFiles.map((f) => `- ${f}${hashWorkingFile(root, f) === 'DELETED' ? ' (deleted)' : ''}`),
281
+ ...(diff ? ['', 'Diff vs HEAD at capture time (may include unrelated edits already in those files):', diff] : []),
282
+ ].join('\n');
283
+ const brain = input.brain ?? (0, TheBrainV2_1.getBrain)();
284
+ const existing = fixFiles.filter((f) => fs_1.default.existsSync(path_1.default.join(root, f))).map((f) => path_1.default.join(root, f));
285
+ const res = brain.store(query, response, 'fix_capture_auto', 0.92, existing.length ? existing : undefined, (0, TheBrainV2_1.deriveProjectId)(root), 'confirmed', undefined, undefined, undefined, undefined, { source: 'auto-fix' });
286
+ if (!res.stored || !res.id) {
287
+ return { note: '' };
288
+ }
289
+ return {
290
+ captured: { id: res.id, query, files: fixFiles },
291
+ note: `\n\n[Lemma] Captured this fix in Brain (id ${res.id}): "${query.slice(0, 120)}" → ${fixFiles.length} file(s). ` +
292
+ `search_memory with that error will surface it; it goes stale as soon as those files change. ` +
293
+ `If this wasn't really the fix, forget_memory ${res.id}.`,
294
+ };
295
+ }
296
+ /**
297
+ * Entry point for a host PostToolUse hook (Claude Code shape: `tool_name`, `tool_input.command`,
298
+ * `tool_response`). Exit status isn't always in the payload, so when it's missing the verdict is
299
+ * read from the output's own summary lines — and when that's ambiguous, nothing is recorded
300
+ * rather than a guess.
301
+ */
302
+ function recordHookPayload(payload, cwd) {
303
+ if (!payload || typeof payload !== 'object')
304
+ return { note: '' };
305
+ const p = payload;
306
+ if (p.tool_name && p.tool_name !== 'Bash')
307
+ return { note: '' };
308
+ const command = p.tool_input?.command;
309
+ if (typeof command !== 'string')
310
+ return { note: '' };
311
+ const resp = p.tool_response ?? {};
312
+ const output = typeof resp === 'string' ? resp : [resp.stdout, resp.stderr, resp.output].filter((s) => typeof s === 'string').join('\n');
313
+ if (resp.interrupted === true)
314
+ return { note: '' };
315
+ const code = [resp.exit_code, resp.exitCode, resp.code, p.exit_code].find((c) => typeof c === 'number');
316
+ const passed = typeof code === 'number' ? code === 0 : inferVerdict(output);
317
+ if (passed === null)
318
+ return { note: '' };
319
+ return recordCommandOutcome({ command, cwd: cwd || p.cwd, passed, output });
320
+ }
321
+ /** Pass/fail from a runner's own summary line; null when the output doesn't say. */
322
+ function inferVerdict(output) {
323
+ const o = output || '';
324
+ // Counts are [1-9]-anchored: "0 failed", "Found 0 errors" and "0 errors, 0 warnings" are
325
+ // what a clean run prints, and reading them as failures would invent a red run.
326
+ if (/Tests?:\s+.*\b[1-9]\d* failed\b/.test(o) || /\b[1-9]\d* failed\b.*\bpassed\b/.test(o) || /^FAIL\s/m.test(o))
327
+ return false;
328
+ if (/error\s+TS\d+:/.test(o) || /Found [1-9]\d* errors?\b/.test(o))
329
+ return false;
330
+ if (/✖ [1-9]\d* problems?/.test(o) || /\b[1-9]\d* errors?\b.*\bwarnings?\b/.test(o))
331
+ return false;
332
+ if (/^=+ .*\bfailed\b.* =+$/m.test(o))
333
+ return false;
334
+ if (/Tests?:\s+.*\b\d+ passed\b/.test(o) || /^=+ .*\bpassed\b.* =+$/m.test(o))
335
+ return true;
336
+ return null;
337
+ }
338
+ //# sourceMappingURL=FixCapture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FixCapture.js","sourceRoot":"","sources":["../../../src/utils/FixCapture.ts"],"names":[],"mappings":"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"}
@@ -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,106 @@
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
+ import fs from 'fs';
17
+ import path from 'path';
18
+ import { execFileSync } from 'child_process';
19
+ const BEGIN = '# >>> lemma brain hooks >>>';
20
+ const END = '# <<< lemma brain hooks <<<';
21
+ const HOOK_BODIES = {
22
+ 'post-commit': 'command -v lemma-brain-ingest >/dev/null 2>&1 && (lemma-brain-ingest "$(git rev-parse --show-toplevel)" >/dev/null 2>&1 &)',
23
+ '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 &)',
24
+ // $3 is 1 for a branch checkout, 0 for a file checkout — only a branch switch can change the shared set.
25
+ '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 &)',
26
+ };
27
+ function git(cwd, args) {
28
+ try {
29
+ return execFileSync('git', ['-C', cwd, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ function block(hook) {
36
+ return `${BEGIN}\n${HOOK_BODIES[hook]}\n${END}\n`;
37
+ }
38
+ export function installBrainHooks(cwd = process.cwd()) {
39
+ const result = { installed: [], updated: [], unchanged: [] };
40
+ const top = git(cwd, ['rev-parse', '--show-toplevel']);
41
+ const hooksRel = git(cwd, ['rev-parse', '--git-path', 'hooks']);
42
+ if (!top || !hooksRel) {
43
+ result.skippedReason = 'not a git repository';
44
+ return result;
45
+ }
46
+ const hooksDir = path.resolve(cwd, hooksRel);
47
+ // Inside the working tree and not under .git → a versioned hooks dir (husky etc.). Compared
48
+ // on realpaths: git reports the toplevel resolved (/private/var/… on macOS) while hooksDir is
49
+ // built from the cwd as given (/var/…), and the two would otherwise look unrelated.
50
+ const real = (p) => { try {
51
+ return fs.realpathSync(p);
52
+ }
53
+ catch {
54
+ return p;
55
+ } };
56
+ const rel = path.relative(real(top), real(hooksDir));
57
+ const insideTree = !rel.startsWith('..') && !path.isAbsolute(rel);
58
+ const underGitDir = rel === '.git' || rel.startsWith(`.git${path.sep}`);
59
+ if (insideTree && !underGitDir) {
60
+ result.manual = {
61
+ hooksDir: rel,
62
+ snippet: Object.keys(HOOK_BODIES).map((h) => `# ${rel}/${h}\n${HOOK_BODIES[h]}`).join('\n\n'),
63
+ };
64
+ return result;
65
+ }
66
+ fs.mkdirSync(hooksDir, { recursive: true });
67
+ for (const hook of Object.keys(HOOK_BODIES)) {
68
+ const file = path.join(hooksDir, hook);
69
+ let current = null;
70
+ try {
71
+ current = fs.readFileSync(file, 'utf8');
72
+ }
73
+ catch { /* no hook yet */ }
74
+ let next;
75
+ if (current === null) {
76
+ next = `#!/bin/sh\n${block(hook)}`;
77
+ }
78
+ else {
79
+ const start = current.indexOf(BEGIN);
80
+ const end = current.indexOf(END);
81
+ if (start !== -1 && end > start) {
82
+ next = current.slice(0, start) + block(hook) + current.slice(end + END.length).replace(/^\n/, '');
83
+ }
84
+ else {
85
+ // Appended after the existing script. A hook that ends in `exit 0` would never reach
86
+ // it — insert before a trailing bare exit instead of after it.
87
+ const trailingExit = current.match(/\n(exit\s+\d+\s*)$/);
88
+ next = trailingExit
89
+ ? current.slice(0, trailingExit.index + 1) + block(hook) + trailingExit[1]
90
+ : `${current}${current.endsWith('\n') ? '' : '\n'}${block(hook)}`;
91
+ }
92
+ }
93
+ if (next === current) {
94
+ result.unchanged.push(hook);
95
+ continue;
96
+ }
97
+ fs.writeFileSync(file, next, 'utf8');
98
+ try {
99
+ fs.chmodSync(file, 0o755);
100
+ }
101
+ catch { /* Windows: git for Windows runs hooks via sh regardless */ }
102
+ (current === null ? result.installed : result.updated).push(hook);
103
+ }
104
+ return result;
105
+ }
106
+ //# 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":";AA8cA,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;AAwrDD,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"}
1
+ {"version":3,"file":"lemma-proxy.d.ts","sourceRoot":"","sources":["../../../src/cli/lemma-proxy.ts"],"names":[],"mappings":";AAgdA,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;AAgtDD,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"}
@@ -48,6 +48,8 @@ import { ProjectRegistry } from '../proxy/ProjectRegistry.js';
48
48
  import { shouldInject, buildMinimalContext, buildDeepContext, sniffProjectFromMessages } from '../proxy/ContextInjector.js';
49
49
  import { savingsLedger } from '../utils/SavingsLedger.js';
50
50
  import { CACHE_DIR, PID_FILE, PORT_FILE, STATS_FILE, USAGE_FILE, LICENSE_FILE, CLIPBOARD_PID_FILE, FREE_TIER_LIMIT } from '../proxy/ProjectStore.js';
51
+ import { installBrainHooks } from './gitHooks.js';
52
+ import { getBrain } from '../subconscious/TheBrainV2.js';
51
53
  const chroma = new ChromaClient({ host: 'localhost', port: 8000 });
52
54
  const dummyEmbeddingFunction = { generate: (texts) => Promise.resolve([]) };
53
55
  const complexityRouter = new ComplexityRouter();
@@ -1349,6 +1351,19 @@ class LemmaServer {
1349
1351
  this.app.get('/api/savings-breakdown', (req, res) => {
1350
1352
  res.json(savingsLedger.getSnapshot());
1351
1353
  });
1354
+ // Brain corpus stats for the dashboard. The corpus lives on disk and is shared with the
1355
+ // MCP server and git hooks; getStats() re-syncs from disk if another process wrote it.
1356
+ // cacheHits/cacheMisses are per-process session counters — in this daemon they'd read 0
1357
+ // and look like a Brain nobody uses, so they're dropped rather than shown misleadingly.
1358
+ this.app.get('/api/brain/stats', (req, res) => {
1359
+ try {
1360
+ const { cacheHits, cacheMisses, ...stats } = getBrain().getStats();
1361
+ res.json(stats);
1362
+ }
1363
+ catch (e) {
1364
+ res.status(500).json({ error: e.message });
1365
+ }
1366
+ });
1352
1367
  // Serve codebase timeline snapshots (AST Time-Travel Telemetry)
1353
1368
  this.app.get('/api/project/timeline', (req, res) => {
1354
1369
  res.json({ success: true, timeline: this.timeline });
@@ -2494,6 +2509,7 @@ program.command('init')
2494
2509
  .description('Initialize Lemma in the current project (auto-discovery)')
2495
2510
  .option('--no-configure', 'Skip automatic configuration of editors, shell overrides, and Claude Desktop')
2496
2511
  .option('--yes', 'Skip prompts and accept defaults')
2512
+ .option('--no-hooks', 'Skip installing the git hooks that feed the Brain (post-commit, post-merge, post-checkout)')
2497
2513
  .action(async (opts) => {
2498
2514
  const project = detectProject();
2499
2515
  console.log(`\n🛠️ Initializing Lemma for [${project}]...`);
@@ -2507,6 +2523,16 @@ program.command('init')
2507
2523
  if (ensureGitIgnore()) {
2508
2524
  console.log('✅ Updated .gitignore with Lemma ignore rules');
2509
2525
  }
2526
+ if (opts.hooks !== false) {
2527
+ const hooks = installBrainHooks(process.cwd());
2528
+ const changed = [...hooks.installed, ...hooks.updated];
2529
+ if (hooks.manual) {
2530
+ console.log(`⚠️ Git hooks live in versioned ${hooks.manual.hooksDir}/ — not touching them. To feed the Brain, add:\n${hooks.manual.snippet}`);
2531
+ }
2532
+ else if (changed.length > 0) {
2533
+ console.log(`✅ Git hooks feed the Brain (${changed.join(', ')}): commits become memories, merges pull .lemma-brain/`);
2534
+ }
2535
+ }
2510
2536
  const envFile = path.join(process.cwd(), '.env');
2511
2537
  const lemmaConfig = `\n# Lemma AI Gateway Configuration\nOPENAI_BASE_URL=http://localhost:8081/v1\nLEMMA_PROJECT=${project}\n`;
2512
2538
  if (fs.existsSync(envFile)) {