@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
@@ -788,6 +788,51 @@ export function jaccardSimilarity(setA, setB) {
788
788
  return intersection / (setA.size + setB.size - intersection);
789
789
  }
790
790
  // ─── Freshness (file-hash invalidation) ───────────────────────────────────────
791
+ /**
792
+ * Real repository files named in a memory's body — the raw material for turning an
793
+ * untracked entry into a verifiable one. Only paths that actually exist under the project
794
+ * root count: an anchor must stay portable, so references that resolve outside the repo
795
+ * (or into node_modules / generated output) are ignored, and `file:line` suffixes are
796
+ * stripped (`src/foo.ts:123` anchors to `src/foo.ts`). False positives self-filter by
797
+ * existence — a stray "auth.ts" in prose that no such file backs just never resolves.
798
+ */
799
+ export function extractRepoPathsFromText(text, opts = {}) {
800
+ const root = opts.root ?? process.cwd();
801
+ const max = opts.max ?? 10;
802
+ const found = [];
803
+ const seen = new Set();
804
+ // A dot-extension filename, optionally prefixed by ./ or a relative path. The character
805
+ // class excludes ':' so `src/foo.ts:123` captures `src/foo.ts` only (the line reference
806
+ // would otherwise defeat the extension anchor). Anchored to start-of-text or one of the
807
+ // delimiters one actually finds around a path in prose: whitespace, quotes, backticks,
808
+ // colons, and opening brackets.
809
+ 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;
810
+ for (const m of text.matchAll(tokenRe)) {
811
+ const raw = m[1];
812
+ if (seen.has(raw))
813
+ continue;
814
+ seen.add(raw);
815
+ const abs = path.resolve(root, raw);
816
+ const rel = path.relative(root, abs);
817
+ if (rel.startsWith('..') || path.isAbsolute(rel))
818
+ continue; // outside the repo
819
+ if (/(?:^|\/)(?:node_modules|\.git|\.next|coverage|dist)(?:\/|$)/.test(rel))
820
+ continue;
821
+ let st;
822
+ try {
823
+ st = fs.statSync(abs);
824
+ }
825
+ catch {
826
+ continue;
827
+ }
828
+ if (!st.isFile())
829
+ continue;
830
+ found.push(rel);
831
+ if (found.length >= max)
832
+ break;
833
+ }
834
+ return found;
835
+ }
791
836
  function hashFilesForFreshness(filePaths) {
792
837
  const hashes = {};
793
838
  for (const p of filePaths) {
@@ -1819,6 +1864,7 @@ export class TheBrainV2 {
1819
1864
  const { fresh, staleFiles, unverifiedFiles, cosmeticChanges } = checkEntryFreshness(r.entry, resolver, resolveNode, 0, new Set(), semanticDiff);
1820
1865
  const claims = r.entry.claims?.map((c) => checkClaimFreshness(c, resolver, semanticDiff));
1821
1866
  const unverified = unverifiedFiles.length > 0;
1867
+ const untracked = trackedArtifactCount(r.entry) === 0;
1822
1868
  return {
1823
1869
  id: r.entry.id,
1824
1870
  query: r.entry.query,
@@ -1827,6 +1873,7 @@ export class TheBrainV2 {
1827
1873
  provider: r.entry.provider,
1828
1874
  timestamp: r.entry.timestamp,
1829
1875
  fresh,
1876
+ ...(untracked ? { untracked: true } : {}),
1830
1877
  ...(r.entry.hits ? { hits: r.entry.hits } : {}),
1831
1878
  ...(r.entry.tokensSaved ? { tokensSaved: r.entry.tokensSaved } : {}),
1832
1879
  ...(r.whyShown.length > 0 ? { whyShown: r.whyShown } : {}),
@@ -1843,11 +1890,15 @@ export class TheBrainV2 {
1843
1890
  if (countStats) {
1844
1891
  if (results.length > 0) {
1845
1892
  this.sessionHits++;
1846
- // Increment hit counter on top result (LRU-style freshness)
1847
- const topEntry = this.entries.get(results[0].id);
1848
- if (topEntry) {
1849
- topEntry.hits++;
1850
- this.scheduleSave();
1893
+ // An untracked result is a hint about something that was never validated against
1894
+ // code counting it as a reuse would inflate the ROI/eviction ledger.
1895
+ if (!results[0].untracked) {
1896
+ // Increment hit counter on top result (LRU-style freshness)
1897
+ const topEntry = this.entries.get(results[0].id);
1898
+ if (topEntry) {
1899
+ topEntry.hits++;
1900
+ this.scheduleSave();
1901
+ }
1851
1902
  }
1852
1903
  }
1853
1904
  else {
@@ -1906,10 +1957,13 @@ export class TheBrainV2 {
1906
1957
  if (options.countStats !== false) {
1907
1958
  if (results.length > 0) {
1908
1959
  this.sessionHits++;
1909
- const topEntry = this.entries.get(results[0].id);
1910
- if (topEntry) {
1911
- topEntry.hits++;
1912
- this.scheduleSave();
1960
+ // Same untracked gate as search(): never credit a hint with no evidence behind it.
1961
+ if (!results[0].untracked) {
1962
+ const topEntry = this.entries.get(results[0].id);
1963
+ if (topEntry) {
1964
+ topEntry.hits++;
1965
+ this.scheduleSave();
1966
+ }
1913
1967
  }
1914
1968
  }
1915
1969
  else {
@@ -2779,7 +2833,18 @@ export class TheBrainV2 {
2779
2833
  revalidated.stale.push({ id: v.id, query: queryOf(v.id), staleFiles: v.staleFiles || [], action: 'refresh_memory after checking it is still true' });
2780
2834
  }
2781
2835
  else if (v.status === 'untracked') {
2782
- revalidated.untracked.push({ id: v.id, query: queryOf(v.id), action: 'update_memory with filePaths/symbols so it becomes verifiable' });
2836
+ const entry = this.entries.get(v.id);
2837
+ const candidates = entry
2838
+ ? extractRepoPathsFromText(`${entry.query}\n${entry.response}`, { max: 10 })
2839
+ : [];
2840
+ revalidated.untracked.push({
2841
+ id: v.id,
2842
+ query: queryOf(v.id),
2843
+ action: candidates.length > 0
2844
+ ? 'refresh_memory with the discovered filePaths (auto-anchor)'
2845
+ : 'update_memory with filePaths/symbols so it becomes verifiable',
2846
+ anchorCandidates: candidates.length > 0 ? candidates : undefined,
2847
+ });
2783
2848
  }
2784
2849
  else if (v.status === 'unverified') {
2785
2850
  revalidated.unverified.push({ id: v.id, query: queryOf(v.id), action: 'check the tracked paths exist on this machine, then refresh_memory' });