@nxuss/lemma 1.25.0 → 1.26.1

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 (34) hide show
  1. package/README.md +14 -3
  2. package/dist/cjs/cli/lemma-proxy.d.ts.map +1 -1
  3. package/dist/cjs/cli/lemma-proxy.js +10 -2
  4. package/dist/cjs/cli/lemma-proxy.js.map +1 -1
  5. package/dist/cjs/mcp/index.js +15 -0
  6. package/dist/cjs/mcp/index.js.map +1 -1
  7. package/dist/cjs/mcp/tools/memory.d.ts.map +1 -1
  8. package/dist/cjs/mcp/tools/memory.js +57 -7
  9. package/dist/cjs/mcp/tools/memory.js.map +1 -1
  10. package/dist/cjs/subconscious/TheBrainV2.d.ts +20 -0
  11. package/dist/cjs/subconscious/TheBrainV2.d.ts.map +1 -1
  12. package/dist/cjs/subconscious/TheBrainV2.js +76 -10
  13. package/dist/cjs/subconscious/TheBrainV2.js.map +1 -1
  14. package/dist/cjs/utils/ClipboardWatcher.d.ts +5 -2
  15. package/dist/cjs/utils/ClipboardWatcher.d.ts.map +1 -1
  16. package/dist/cjs/utils/ClipboardWatcher.js +38 -4
  17. package/dist/cjs/utils/ClipboardWatcher.js.map +1 -1
  18. package/dist/esm/cli/lemma-proxy.d.ts.map +1 -1
  19. package/dist/esm/cli/lemma-proxy.js +10 -2
  20. package/dist/esm/cli/lemma-proxy.js.map +1 -1
  21. package/dist/esm/mcp/index.js +15 -0
  22. package/dist/esm/mcp/index.js.map +1 -1
  23. package/dist/esm/mcp/tools/memory.d.ts.map +1 -1
  24. package/dist/esm/mcp/tools/memory.js +57 -7
  25. package/dist/esm/mcp/tools/memory.js.map +1 -1
  26. package/dist/esm/subconscious/TheBrainV2.d.ts +20 -0
  27. package/dist/esm/subconscious/TheBrainV2.d.ts.map +1 -1
  28. package/dist/esm/subconscious/TheBrainV2.js +75 -10
  29. package/dist/esm/subconscious/TheBrainV2.js.map +1 -1
  30. package/dist/esm/utils/ClipboardWatcher.d.ts +5 -2
  31. package/dist/esm/utils/ClipboardWatcher.d.ts.map +1 -1
  32. package/dist/esm/utils/ClipboardWatcher.js +39 -5
  33. package/dist/esm/utils/ClipboardWatcher.js.map +1 -1
  34. package/package.json +1 -1
@@ -27,6 +27,7 @@ exports.tokenize = tokenize;
27
27
  exports.termFrequencies = termFrequencies;
28
28
  exports.bm25Score = bm25Score;
29
29
  exports.jaccardSimilarity = jaccardSimilarity;
30
+ exports.extractRepoPathsFromText = extractRepoPathsFromText;
30
31
  exports.resolveFreshnessNode = resolveFreshnessNode;
31
32
  exports.buildClaimIndex = buildClaimIndex;
32
33
  exports.findBlastRadius = findBlastRadius;
@@ -815,6 +816,51 @@ function jaccardSimilarity(setA, setB) {
815
816
  return intersection / (setA.size + setB.size - intersection);
816
817
  }
817
818
  // ─── Freshness (file-hash invalidation) ───────────────────────────────────────
819
+ /**
820
+ * Real repository files named in a memory's body — the raw material for turning an
821
+ * untracked entry into a verifiable one. Only paths that actually exist under the project
822
+ * root count: an anchor must stay portable, so references that resolve outside the repo
823
+ * (or into node_modules / generated output) are ignored, and `file:line` suffixes are
824
+ * stripped (`src/foo.ts:123` anchors to `src/foo.ts`). False positives self-filter by
825
+ * existence — a stray "auth.ts" in prose that no such file backs just never resolves.
826
+ */
827
+ function extractRepoPathsFromText(text, opts = {}) {
828
+ const root = opts.root ?? process.cwd();
829
+ const max = opts.max ?? 10;
830
+ const found = [];
831
+ const seen = new Set();
832
+ // A dot-extension filename, optionally prefixed by ./ or a relative path. The character
833
+ // class excludes ':' so `src/foo.ts:123` captures `src/foo.ts` only (the line reference
834
+ // would otherwise defeat the extension anchor). Anchored to start-of-text or one of the
835
+ // delimiters one actually finds around a path in prose: whitespace, quotes, backticks,
836
+ // colons, and opening brackets.
837
+ const tokenRe = /(?:^|[\s"'`=([,:—-])((?:\.\/)?[A-Za-z0-9_][A-Za-z0-9_./-]*\.(?:ts|tsx|js|jsx|mjs|cjs|json|jsonc|yaml|yml|md|toml|txt|env|css|scss|html|py|go|rs|rb|sh|sql|graphql|vue|svelte|java|kt|swift|c|cpp|h|cs|php))(?::\d+)?/g;
838
+ for (const m of text.matchAll(tokenRe)) {
839
+ const raw = m[1];
840
+ if (seen.has(raw))
841
+ continue;
842
+ seen.add(raw);
843
+ const abs = path_1.default.resolve(root, raw);
844
+ const rel = path_1.default.relative(root, abs);
845
+ if (rel.startsWith('..') || path_1.default.isAbsolute(rel))
846
+ continue; // outside the repo
847
+ if (/(?:^|\/)(?:node_modules|\.git|\.next|coverage|dist)(?:\/|$)/.test(rel))
848
+ continue;
849
+ let st;
850
+ try {
851
+ st = fs_1.default.statSync(abs);
852
+ }
853
+ catch {
854
+ continue;
855
+ }
856
+ if (!st.isFile())
857
+ continue;
858
+ found.push(rel);
859
+ if (found.length >= max)
860
+ break;
861
+ }
862
+ return found;
863
+ }
818
864
  function hashFilesForFreshness(filePaths) {
819
865
  const hashes = {};
820
866
  for (const p of filePaths) {
@@ -1846,6 +1892,7 @@ class TheBrainV2 {
1846
1892
  const { fresh, staleFiles, unverifiedFiles, cosmeticChanges } = checkEntryFreshness(r.entry, resolver, resolveNode, 0, new Set(), semanticDiff);
1847
1893
  const claims = r.entry.claims?.map((c) => checkClaimFreshness(c, resolver, semanticDiff));
1848
1894
  const unverified = unverifiedFiles.length > 0;
1895
+ const untracked = trackedArtifactCount(r.entry) === 0;
1849
1896
  return {
1850
1897
  id: r.entry.id,
1851
1898
  query: r.entry.query,
@@ -1854,6 +1901,7 @@ class TheBrainV2 {
1854
1901
  provider: r.entry.provider,
1855
1902
  timestamp: r.entry.timestamp,
1856
1903
  fresh,
1904
+ ...(untracked ? { untracked: true } : {}),
1857
1905
  ...(r.entry.hits ? { hits: r.entry.hits } : {}),
1858
1906
  ...(r.entry.tokensSaved ? { tokensSaved: r.entry.tokensSaved } : {}),
1859
1907
  ...(r.whyShown.length > 0 ? { whyShown: r.whyShown } : {}),
@@ -1870,11 +1918,15 @@ class TheBrainV2 {
1870
1918
  if (countStats) {
1871
1919
  if (results.length > 0) {
1872
1920
  this.sessionHits++;
1873
- // Increment hit counter on top result (LRU-style freshness)
1874
- const topEntry = this.entries.get(results[0].id);
1875
- if (topEntry) {
1876
- topEntry.hits++;
1877
- this.scheduleSave();
1921
+ // An untracked result is a hint about something that was never validated against
1922
+ // code counting it as a reuse would inflate the ROI/eviction ledger.
1923
+ if (!results[0].untracked) {
1924
+ // Increment hit counter on top result (LRU-style freshness)
1925
+ const topEntry = this.entries.get(results[0].id);
1926
+ if (topEntry) {
1927
+ topEntry.hits++;
1928
+ this.scheduleSave();
1929
+ }
1878
1930
  }
1879
1931
  }
1880
1932
  else {
@@ -1933,10 +1985,13 @@ class TheBrainV2 {
1933
1985
  if (options.countStats !== false) {
1934
1986
  if (results.length > 0) {
1935
1987
  this.sessionHits++;
1936
- const topEntry = this.entries.get(results[0].id);
1937
- if (topEntry) {
1938
- topEntry.hits++;
1939
- this.scheduleSave();
1988
+ // Same untracked gate as search(): never credit a hint with no evidence behind it.
1989
+ if (!results[0].untracked) {
1990
+ const topEntry = this.entries.get(results[0].id);
1991
+ if (topEntry) {
1992
+ topEntry.hits++;
1993
+ this.scheduleSave();
1994
+ }
1940
1995
  }
1941
1996
  }
1942
1997
  else {
@@ -2806,7 +2861,18 @@ class TheBrainV2 {
2806
2861
  revalidated.stale.push({ id: v.id, query: queryOf(v.id), staleFiles: v.staleFiles || [], action: 'refresh_memory after checking it is still true' });
2807
2862
  }
2808
2863
  else if (v.status === 'untracked') {
2809
- revalidated.untracked.push({ id: v.id, query: queryOf(v.id), action: 'update_memory with filePaths/symbols so it becomes verifiable' });
2864
+ const entry = this.entries.get(v.id);
2865
+ const candidates = entry
2866
+ ? extractRepoPathsFromText(`${entry.query}\n${entry.response}`, { max: 10 })
2867
+ : [];
2868
+ revalidated.untracked.push({
2869
+ id: v.id,
2870
+ query: queryOf(v.id),
2871
+ action: candidates.length > 0
2872
+ ? 'refresh_memory with the discovered filePaths (auto-anchor)'
2873
+ : 'update_memory with filePaths/symbols so it becomes verifiable',
2874
+ anchorCandidates: candidates.length > 0 ? candidates : undefined,
2875
+ });
2810
2876
  }
2811
2877
  else if (v.status === 'unverified') {
2812
2878
  revalidated.unverified.push({ id: v.id, query: queryOf(v.id), action: 'check the tracked paths exist on this machine, then refresh_memory' });